Auto merge of #105218 - matthiaskrgr:rollup-8d3k08n, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #104199 (Keep track of the start of the argument block of a closure)
 - #105050 (Remove useless borrows and derefs)
 - #105153 (Create a hacky fail-fast mode that stops tests at the first failure)
 - #105164 (Restore `use` suggestion for `dyn` method call requiring `Sized`)
 - #105193 (Disable coverage instrumentation for naked functions)
 - #105200 (Remove useless filter in unused extern crate check.)
 - #105201 (Do not call fn_sig on non-functions.)
 - #105208 (Add AmbiguityError for inconsistent resolution for an import)
 - #105214 (update Miri)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-12-03 21:25:45 +00:00
commit b8a52e3a4b
116 changed files with 1471 additions and 1142 deletions

View file

@ -5356,9 +5356,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]] [[package]]
name = "ui_test" name = "ui_test"
version = "0.4.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4559da3fe6b481f8674a29379677cb9606cd6f75fc254a2c9834c55638503d" checksum = "54ddb6f31025943e2f9d59237f433711c461a43d9415974c3eb3a4902edc1c1f"
dependencies = [ dependencies = [
"bstr 1.0.1", "bstr 1.0.1",
"cargo_metadata 0.15.0", "cargo_metadata 0.15.0",

View file

@ -354,7 +354,7 @@ pub trait LayoutCalculator {
if !always_sized { StructKind::MaybeUnsized } else { StructKind::AlwaysSized } if !always_sized { StructKind::MaybeUnsized } else { StructKind::AlwaysSized }
}; };
let mut st = self.univariant(dl, &variants[v], &repr, kind)?; let mut st = self.univariant(dl, &variants[v], repr, kind)?;
st.variants = Variants::Single { index: v }; st.variants = Variants::Single { index: v };
if is_unsafe_cell { if is_unsafe_cell {
@ -457,7 +457,7 @@ pub trait LayoutCalculator {
let mut variant_layouts = variants let mut variant_layouts = variants
.iter_enumerated() .iter_enumerated()
.map(|(j, v)| { .map(|(j, v)| {
let mut st = self.univariant(dl, v, &repr, StructKind::AlwaysSized)?; let mut st = self.univariant(dl, v, repr, StructKind::AlwaysSized)?;
st.variants = Variants::Single { index: j }; st.variants = Variants::Single { index: j };
align = align.max(st.align); align = align.max(st.align);
@ -647,8 +647,8 @@ pub trait LayoutCalculator {
.map(|(i, field_layouts)| { .map(|(i, field_layouts)| {
let mut st = self.univariant( let mut st = self.univariant(
dl, dl,
&field_layouts, field_layouts,
&repr, repr,
StructKind::Prefixed(min_ity.size(), prefix_align), StructKind::Prefixed(min_ity.size(), prefix_align),
)?; )?;
st.variants = Variants::Single { index: i }; st.variants = Variants::Single { index: i };
@ -755,7 +755,7 @@ pub trait LayoutCalculator {
// Try to use a ScalarPair for all tagged enums. // Try to use a ScalarPair for all tagged enums.
let mut common_prim = None; let mut common_prim = None;
let mut common_prim_initialized_in_all_variants = true; let mut common_prim_initialized_in_all_variants = true;
for (field_layouts, layout_variant) in iter::zip(&*variants, &layout_variants) { for (field_layouts, layout_variant) in iter::zip(variants, &layout_variants) {
let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else { let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
panic!(); panic!();
}; };

View file

@ -1179,7 +1179,7 @@ impl Expr {
pub fn peel_parens(&self) -> &Expr { pub fn peel_parens(&self) -> &Expr {
let mut expr = self; let mut expr = self;
while let ExprKind::Paren(inner) = &expr.kind { while let ExprKind::Paren(inner) = &expr.kind {
expr = &inner; expr = inner;
} }
expr expr
} }
@ -1312,8 +1312,10 @@ pub struct Closure {
pub movability: Movability, pub movability: Movability,
pub fn_decl: P<FnDecl>, pub fn_decl: P<FnDecl>,
pub body: P<Expr>, pub body: P<Expr>,
/// The span of the argument block `|...|`. /// The span of the declaration block: 'move |...| -> ...'
pub fn_decl_span: Span, pub fn_decl_span: Span,
/// The span of the argument block `|...|`
pub fn_arg_span: Span,
} }
/// Limit types of a range (inclusive or exclusive) /// Limit types of a range (inclusive or exclusive)
@ -2027,7 +2029,7 @@ impl Ty {
pub fn peel_refs(&self) -> &Self { pub fn peel_refs(&self) -> &Self {
let mut final_ty = self; let mut final_ty = self;
while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind { while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind {
final_ty = &ty; final_ty = ty;
} }
final_ty final_ty
} }

View file

@ -736,8 +736,7 @@ pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
return; // Avoid visiting the span for the second time. return; // Avoid visiting the span for the second time.
} }
token::Interpolated(nt) => { token::Interpolated(nt) => {
let mut nt = Lrc::make_mut(nt); visit_nonterminal(Lrc::make_mut(nt), vis);
visit_nonterminal(&mut nt, vis);
} }
_ => {} _ => {}
} }
@ -1368,6 +1367,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
fn_decl, fn_decl,
body, body,
fn_decl_span, fn_decl_span,
fn_arg_span: _,
}) => { }) => {
vis.visit_closure_binder(binder); vis.visit_closure_binder(binder);
vis.visit_asyncness(asyncness); vis.visit_asyncness(asyncness);

View file

@ -64,7 +64,7 @@ impl TokenTree {
match (self, other) { match (self, other) {
(TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind, (TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
(TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => {
delim == delim2 && tts.eq_unspanned(&tts2) delim == delim2 && tts.eq_unspanned(tts2)
} }
_ => false, _ => false,
} }
@ -402,7 +402,7 @@ impl TokenStream {
let mut t1 = self.trees(); let mut t1 = self.trees();
let mut t2 = other.trees(); let mut t2 = other.trees();
for (t1, t2) in iter::zip(&mut t1, &mut t2) { for (t1, t2) in iter::zip(&mut t1, &mut t2) {
if !t1.eq_unspanned(&t2) { if !t1.eq_unspanned(t2) {
return false; return false;
} }
} }
@ -475,7 +475,7 @@ impl TokenStream {
token::Interpolated(nt) => TokenTree::Delimited( token::Interpolated(nt) => TokenTree::Delimited(
DelimSpan::from_single(token.span), DelimSpan::from_single(token.span),
Delimiter::Invisible, Delimiter::Invisible,
TokenStream::from_nonterminal_ast(&nt).flattened(), TokenStream::from_nonterminal_ast(nt).flattened(),
), ),
_ => TokenTree::Token(token.clone(), spacing), _ => TokenTree::Token(token.clone(), spacing),
} }
@ -511,7 +511,7 @@ impl TokenStream {
fn try_glue_to_last(vec: &mut Vec<TokenTree>, tt: &TokenTree) -> bool { fn try_glue_to_last(vec: &mut Vec<TokenTree>, tt: &TokenTree) -> bool {
if let Some(TokenTree::Token(last_tok, Spacing::Joint)) = vec.last() if let Some(TokenTree::Token(last_tok, Spacing::Joint)) = vec.last()
&& let TokenTree::Token(tok, spacing) = tt && let TokenTree::Token(tok, spacing) = tt
&& let Some(glued_tok) = last_tok.glue(&tok) && let Some(glued_tok) = last_tok.glue(tok)
{ {
// ...then overwrite the last token tree in `vec` with the // ...then overwrite the last token tree in `vec` with the
// glued token, and skip the first token tree from `stream`. // glued token, and skip the first token tree from `stream`.

View file

@ -110,7 +110,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
} else { } else {
&mut lines &mut lines
}; };
if let Some(horizontal) = get_horizontal_trim(&lines, kind) { if let Some(horizontal) = get_horizontal_trim(lines, kind) {
changes = true; changes = true;
// remove a "[ \t]*\*" block from each line, if possible // remove a "[ \t]*\*" block from each line, if possible
for line in lines.iter_mut() { for line in lines.iter_mut() {
@ -147,7 +147,7 @@ fn all_whitespace(s: &str, col: CharPos) -> Option<usize> {
fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str { fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str {
let len = s.len(); let len = s.len();
match all_whitespace(&s, col) { match all_whitespace(s, col) {
Some(col) => { Some(col) => {
if col < len { if col < len {
&s[col..] &s[col..]

View file

@ -52,14 +52,14 @@ impl LitKind {
// new symbol because the string in the LitKind is different to the // new symbol because the string in the LitKind is different to the
// string in the token. // string in the token.
let s = symbol.as_str(); let s = symbol.as_str();
let symbol = if s.contains(&['\\', '\r']) { let symbol = if s.contains(['\\', '\r']) {
let mut buf = String::with_capacity(s.len()); let mut buf = String::with_capacity(s.len());
let mut error = Ok(()); let mut error = Ok(());
// Force-inlining here is aggressive but the closure is // Force-inlining here is aggressive but the closure is
// called on every char in the string, so it can be // called on every char in the string, so it can be
// hot in programs with many long strings. // hot in programs with many long strings.
unescape_literal( unescape_literal(
&s, s,
Mode::Str, Mode::Str,
&mut #[inline(always)] &mut #[inline(always)]
|_, unescaped_char| match unescaped_char { |_, unescaped_char| match unescaped_char {
@ -85,7 +85,7 @@ impl LitKind {
if s.contains('\r') { if s.contains('\r') {
let mut buf = String::with_capacity(s.len()); let mut buf = String::with_capacity(s.len());
let mut error = Ok(()); let mut error = Ok(());
unescape_literal(&s, Mode::RawStr, &mut |_, unescaped_char| { unescape_literal(s, Mode::RawStr, &mut |_, unescaped_char| {
match unescaped_char { match unescaped_char {
Ok(c) => buf.push(c), Ok(c) => buf.push(c),
Err(err) => { Err(err) => {
@ -106,7 +106,7 @@ impl LitKind {
let s = symbol.as_str(); let s = symbol.as_str();
let mut buf = Vec::with_capacity(s.len()); let mut buf = Vec::with_capacity(s.len());
let mut error = Ok(()); let mut error = Ok(());
unescape_literal(&s, Mode::ByteStr, &mut |_, c| match c { unescape_literal(s, Mode::ByteStr, &mut |_, c| match c {
Ok(c) => buf.push(byte_from_char(c)), Ok(c) => buf.push(byte_from_char(c)),
Err(err) => { Err(err) => {
if err.is_fatal() { if err.is_fatal() {
@ -122,7 +122,7 @@ impl LitKind {
let bytes = if s.contains('\r') { let bytes = if s.contains('\r') {
let mut buf = Vec::with_capacity(s.len()); let mut buf = Vec::with_capacity(s.len());
let mut error = Ok(()); let mut error = Ok(());
unescape_literal(&s, Mode::RawByteStr, &mut |_, c| match c { unescape_literal(s, Mode::RawByteStr, &mut |_, c| match c {
Ok(c) => buf.push(byte_from_char(c)), Ok(c) => buf.push(byte_from_char(c)),
Err(err) => { Err(err) => {
if err.is_fatal() { if err.is_fatal() {

View file

@ -384,7 +384,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
| ast::ExprKind::AssignOp(_, lhs, rhs) | ast::ExprKind::AssignOp(_, lhs, rhs)
| ast::ExprKind::Binary(_, lhs, rhs) => { | ast::ExprKind::Binary(_, lhs, rhs) => {
// X { y: 1 } + X { y: 2 } // X { y: 1 } + X { y: 2 }
contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) contains_exterior_struct_lit(lhs) || contains_exterior_struct_lit(rhs)
} }
ast::ExprKind::Await(x) ast::ExprKind::Await(x)
| ast::ExprKind::Unary(_, x) | ast::ExprKind::Unary(_, x)
@ -393,12 +393,12 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
| ast::ExprKind::Field(x, _) | ast::ExprKind::Field(x, _)
| ast::ExprKind::Index(x, _) => { | ast::ExprKind::Index(x, _) => {
// &X { y: 1 }, X { y: 1 }.y // &X { y: 1 }, X { y: 1 }.y
contains_exterior_struct_lit(&x) contains_exterior_struct_lit(x)
} }
ast::ExprKind::MethodCall(box ast::MethodCall { receiver, .. }) => { ast::ExprKind::MethodCall(box ast::MethodCall { receiver, .. }) => {
// X { y: 1 }.bar(...) // X { y: 1 }.bar(...)
contains_exterior_struct_lit(&receiver) contains_exterior_struct_lit(receiver)
} }
_ => false, _ => false,

View file

@ -17,7 +17,7 @@ pub fn contains_text_flow_control_chars(s: &str) -> bool {
// U+2069 - E2 81 A9 // U+2069 - E2 81 A9
let mut bytes = s.as_bytes(); let mut bytes = s.as_bytes();
loop { loop {
match core::slice::memchr::memchr(0xE2, &bytes) { match core::slice::memchr::memchr(0xE2, bytes) {
Some(idx) => { Some(idx) => {
// bytes are valid UTF-8 -> E2 must be followed by two bytes // bytes are valid UTF-8 -> E2 must be followed by two bytes
let ch = &bytes[idx..idx + 3]; let ch = &bytes[idx..idx + 3];

View file

@ -840,6 +840,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
fn_decl, fn_decl,
body, body,
fn_decl_span: _, fn_decl_span: _,
fn_arg_span: _,
}) => { }) => {
visitor.visit_fn(FnKind::Closure(binder, fn_decl, body), expression.span, expression.id) visitor.visit_fn(FnKind::Closure(binder, fn_decl, body), expression.span, expression.id)
} }

View file

@ -176,6 +176,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_decl, fn_decl,
body, body,
fn_decl_span, fn_decl_span,
fn_arg_span,
}) => { }) => {
if let Async::Yes { closure_id, .. } = asyncness { if let Async::Yes { closure_id, .. } = asyncness {
self.lower_expr_async_closure( self.lower_expr_async_closure(
@ -186,6 +187,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_decl, fn_decl,
body, body,
*fn_decl_span, *fn_decl_span,
*fn_arg_span,
) )
} else { } else {
self.lower_expr_closure( self.lower_expr_closure(
@ -196,6 +198,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_decl, fn_decl,
body, body,
*fn_decl_span, *fn_decl_span,
*fn_arg_span,
) )
} }
} }
@ -642,6 +645,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_decl, fn_decl,
body, body,
fn_decl_span: self.lower_span(span), fn_decl_span: self.lower_span(span),
fn_arg_span: None,
movability: Some(hir::Movability::Static), movability: Some(hir::Movability::Static),
}); });
@ -898,6 +902,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
decl: &FnDecl, decl: &FnDecl,
body: &Expr, body: &Expr,
fn_decl_span: Span, fn_decl_span: Span,
fn_arg_span: Span,
) -> hir::ExprKind<'hir> { ) -> hir::ExprKind<'hir> {
let (binder_clause, generic_params) = self.lower_closure_binder(binder); let (binder_clause, generic_params) = self.lower_closure_binder(binder);
@ -928,6 +933,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_decl, fn_decl,
body: body_id, body: body_id,
fn_decl_span: self.lower_span(fn_decl_span), fn_decl_span: self.lower_span(fn_decl_span),
fn_arg_span: Some(self.lower_span(fn_arg_span)),
movability: generator_option, movability: generator_option,
}); });
@ -984,6 +990,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
decl: &FnDecl, decl: &FnDecl,
body: &Expr, body: &Expr,
fn_decl_span: Span, fn_decl_span: Span,
fn_arg_span: Span,
) -> hir::ExprKind<'hir> { ) -> hir::ExprKind<'hir> {
if let &ClosureBinder::For { span, .. } = binder { if let &ClosureBinder::For { span, .. } = binder {
self.tcx.sess.emit_err(NotSupportedForLifetimeBinderAsyncClosure { span }); self.tcx.sess.emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
@ -1038,6 +1045,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_decl, fn_decl,
body, body,
fn_decl_span: self.lower_span(fn_decl_span), fn_decl_span: self.lower_span(fn_decl_span),
fn_arg_span: Some(self.lower_span(fn_arg_span)),
movability: None, movability: None,
}); });
hir::ExprKind::Closure(c) hir::ExprKind::Closure(c)

View file

@ -519,7 +519,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
ast::MetaItemKind::List(items) => { ast::MetaItemKind::List(items) => {
self.print_path(&item.path, false, 0); self.print_path(&item.path, false, 0);
self.popen(); self.popen();
self.commasep(Consistent, &items, |s, i| s.print_meta_list_item(i)); self.commasep(Consistent, items, |s, i| s.print_meta_list_item(i));
self.pclose(); self.pclose();
} }
} }
@ -536,7 +536,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) { fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) {
match tt { match tt {
TokenTree::Token(token, _) => { TokenTree::Token(token, _) => {
let token_str = self.token_to_string_ext(&token, convert_dollar_crate); let token_str = self.token_to_string_ext(token, convert_dollar_crate);
self.word(token_str); self.word(token_str);
if let token::DocComment(..) = token.kind { if let token::DocComment(..) = token.kind {
self.hardbreak() self.hardbreak()
@ -998,7 +998,7 @@ impl<'a> State<'a> {
ast::AssocConstraintKind::Bound { bounds } => { ast::AssocConstraintKind::Bound { bounds } => {
if !bounds.is_empty() { if !bounds.is_empty() {
self.word_nbsp(":"); self.word_nbsp(":");
self.print_type_bounds(&bounds); self.print_type_bounds(bounds);
} }
} }
} }
@ -1035,7 +1035,7 @@ impl<'a> State<'a> {
} }
ast::TyKind::Tup(elts) => { ast::TyKind::Tup(elts) => {
self.popen(); self.popen();
self.commasep(Inconsistent, &elts, |s, ty| s.print_type(ty)); self.commasep(Inconsistent, elts, |s, ty| s.print_type(ty));
if elts.len() == 1 { if elts.len() == 1 {
self.word(","); self.word(",");
} }
@ -1254,7 +1254,7 @@ impl<'a> State<'a> {
self.popen(); self.popen();
self.commasep(Consistent, &args, |s, arg| match arg { self.commasep(Consistent, &args, |s, arg| match arg {
AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked), AsmArg::Template(template) => s.print_string(template, ast::StrStyle::Cooked),
AsmArg::Operand(op) => { AsmArg::Operand(op) => {
let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r { let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r {
InlineAsmRegOrRegClass::Reg(r) => s.print_symbol(*r, ast::StrStyle::Cooked), InlineAsmRegOrRegClass::Reg(r) => s.print_symbol(*r, ast::StrStyle::Cooked),
@ -1424,11 +1424,11 @@ impl<'a> State<'a> {
self.print_path(path, true, 0); self.print_path(path, true, 0);
} }
self.popen(); self.popen();
self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p)); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
self.pclose(); self.pclose();
} }
PatKind::Or(pats) => { PatKind::Or(pats) => {
self.strsep("|", true, Inconsistent, &pats, |s, p| s.print_pat(p)); self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p));
} }
PatKind::Path(None, path) => { PatKind::Path(None, path) => {
self.print_path(path, true, 0); self.print_path(path, true, 0);
@ -1450,7 +1450,7 @@ impl<'a> State<'a> {
} }
self.commasep_cmnt( self.commasep_cmnt(
Consistent, Consistent,
&fields, fields,
|s, f| { |s, f| {
s.cbox(INDENT_UNIT); s.cbox(INDENT_UNIT);
if !f.is_shorthand { if !f.is_shorthand {
@ -1475,7 +1475,7 @@ impl<'a> State<'a> {
} }
PatKind::Tuple(elts) => { PatKind::Tuple(elts) => {
self.popen(); self.popen();
self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p)); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
if elts.len() == 1 { if elts.len() == 1 {
self.word(","); self.word(",");
} }
@ -1498,7 +1498,7 @@ impl<'a> State<'a> {
self.print_pat(inner); self.print_pat(inner);
} }
} }
PatKind::Lit(e) => self.print_expr(&**e), PatKind::Lit(e) => self.print_expr(e),
PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => { PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => {
if let Some(e) = begin { if let Some(e) = begin {
self.print_expr(e); self.print_expr(e);
@ -1514,7 +1514,7 @@ impl<'a> State<'a> {
} }
PatKind::Slice(elts) => { PatKind::Slice(elts) => {
self.word("["); self.word("[");
self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p)); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
self.word("]"); self.word("]");
} }
PatKind::Rest => self.word(".."), PatKind::Rest => self.word(".."),
@ -1600,7 +1600,7 @@ impl<'a> State<'a> {
self.word("<"); self.word("<");
self.commasep(Inconsistent, &generic_params, |s, param| { self.commasep(Inconsistent, generic_params, |s, param| {
s.print_outer_attributes_inline(&param.attrs); s.print_outer_attributes_inline(&param.attrs);
match &param.kind { match &param.kind {

View file

@ -305,10 +305,10 @@ impl<'a> State<'a> {
self.print_expr_tup(exprs); self.print_expr_tup(exprs);
} }
ast::ExprKind::Call(func, args) => { ast::ExprKind::Call(func, args) => {
self.print_expr_call(func, &args); self.print_expr_call(func, args);
} }
ast::ExprKind::MethodCall(box ast::MethodCall { seg, receiver, args, .. }) => { ast::ExprKind::MethodCall(box ast::MethodCall { seg, receiver, args, .. }) => {
self.print_expr_method_call(seg, &receiver, &args); self.print_expr_method_call(seg, receiver, args);
} }
ast::ExprKind::Binary(op, lhs, rhs) => { ast::ExprKind::Binary(op, lhs, rhs) => {
self.print_expr_binary(*op, lhs, rhs); self.print_expr_binary(*op, lhs, rhs);
@ -402,6 +402,7 @@ impl<'a> State<'a> {
fn_decl, fn_decl,
body, body,
fn_decl_span: _, fn_decl_span: _,
fn_arg_span: _,
}) => { }) => {
self.print_closure_binder(binder); self.print_closure_binder(binder);
self.print_movability(*movability); self.print_movability(*movability);
@ -605,7 +606,7 @@ impl<'a> State<'a> {
match binder { match binder {
ast::ClosureBinder::NotPresent => {} ast::ClosureBinder::NotPresent => {}
ast::ClosureBinder::For { generic_params, .. } => { ast::ClosureBinder::For { generic_params, .. } => {
self.print_formal_generic_params(&generic_params) self.print_formal_generic_params(generic_params)
} }
} }
} }

View file

@ -258,13 +258,12 @@ pub fn from_fn_attrs<'ll, 'tcx>(
OptimizeAttr::Speed => {} OptimizeAttr::Speed => {}
} }
let inline = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { let inline =
InlineAttr::Never if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) {
} else if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) { InlineAttr::Hint
InlineAttr::Hint } else {
} else { codegen_fn_attrs.inline
codegen_fn_attrs.inline };
};
to_add.extend(inline_attr(cx, inline)); to_add.extend(inline_attr(cx, inline));
// The `uwtable` attribute according to LLVM is: // The `uwtable` attribute according to LLVM is:

View file

@ -72,7 +72,7 @@ impl<'a, T: PartialOrd> PartialOrd for Interned<'a, T> {
if ptr::eq(self.0, other.0) { if ptr::eq(self.0, other.0) {
Some(Ordering::Equal) Some(Ordering::Equal)
} else { } else {
let res = self.0.partial_cmp(&other.0); let res = self.0.partial_cmp(other.0);
debug_assert_ne!(res, Some(Ordering::Equal)); debug_assert_ne!(res, Some(Ordering::Equal));
res res
} }
@ -86,7 +86,7 @@ impl<'a, T: Ord> Ord for Interned<'a, T> {
if ptr::eq(self.0, other.0) { if ptr::eq(self.0, other.0) {
Ordering::Equal Ordering::Equal
} else { } else {
let res = self.0.cmp(&other.0); let res = self.0.cmp(other.0);
debug_assert_ne!(res, Ordering::Equal); debug_assert_ne!(res, Ordering::Equal);
res res
} }

View file

@ -36,7 +36,7 @@ impl Deref for Mmap {
#[inline] #[inline]
fn deref(&self) -> &[u8] { fn deref(&self) -> &[u8] {
&*self.0 &self.0
} }
} }
@ -102,13 +102,13 @@ impl Deref for MmapMut {
#[inline] #[inline]
fn deref(&self) -> &[u8] { fn deref(&self) -> &[u8] {
&*self.0 &self.0
} }
} }
impl DerefMut for MmapMut { impl DerefMut for MmapMut {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut [u8] { fn deref_mut(&mut self) -> &mut [u8] {
&mut *self.0 &mut self.0
} }
} }

View file

@ -899,25 +899,25 @@ unsafe impl<O, T: ?Sized> StableAddress for OwningRef<O, T> {}
impl<O, T: ?Sized> AsRef<T> for OwningRef<O, T> { impl<O, T: ?Sized> AsRef<T> for OwningRef<O, T> {
fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
&*self self
} }
} }
impl<O, T: ?Sized> AsRef<T> for OwningRefMut<O, T> { impl<O, T: ?Sized> AsRef<T> for OwningRefMut<O, T> {
fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
&*self self
} }
} }
impl<O, T: ?Sized> AsMut<T> for OwningRefMut<O, T> { impl<O, T: ?Sized> AsMut<T> for OwningRefMut<O, T> {
fn as_mut(&mut self) -> &mut T { fn as_mut(&mut self) -> &mut T {
&mut *self self
} }
} }
impl<O, T: ?Sized> Borrow<T> for OwningRef<O, T> { impl<O, T: ?Sized> Borrow<T> for OwningRef<O, T> {
fn borrow(&self) -> &T { fn borrow(&self) -> &T {
&*self self
} }
} }
@ -1021,7 +1021,7 @@ where
T: PartialEq, T: PartialEq,
{ {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
(&*self as &T).eq(&*other as &T) self.deref().eq(other.deref())
} }
} }
@ -1032,7 +1032,7 @@ where
T: PartialOrd, T: PartialOrd,
{ {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(&*self as &T).partial_cmp(&*other as &T) self.deref().partial_cmp(other.deref())
} }
} }
@ -1041,7 +1041,7 @@ where
T: Ord, T: Ord,
{ {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
(&*self as &T).cmp(&*other as &T) self.deref().cmp(other.deref())
} }
} }
@ -1050,7 +1050,7 @@ where
T: Hash, T: Hash,
{ {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
(&*self as &T).hash(state); self.deref().hash(state);
} }
} }
@ -1059,7 +1059,7 @@ where
T: PartialEq, T: PartialEq,
{ {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
(&*self as &T).eq(&*other as &T) self.deref().eq(other.deref())
} }
} }
@ -1070,7 +1070,7 @@ where
T: PartialOrd, T: PartialOrd,
{ {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(&*self as &T).partial_cmp(&*other as &T) self.deref().partial_cmp(other.deref())
} }
} }
@ -1079,7 +1079,7 @@ where
T: Ord, T: Ord,
{ {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
(&*self as &T).cmp(&*other as &T) self.deref().cmp(other.deref())
} }
} }
@ -1088,7 +1088,7 @@ where
T: Hash, T: Hash,
{ {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
(&*self as &T).hash(state); self.deref().hash(state);
} }
} }

View file

@ -192,7 +192,7 @@ impl SelfProfilerRef {
F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>, F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>,
{ {
let profiler = profiler_ref.profiler.as_ref().unwrap(); let profiler = profiler_ref.profiler.as_ref().unwrap();
f(&**profiler) f(profiler)
} }
if self.event_filter_mask.contains(event_filter) { if self.event_filter_mask.contains(event_filter) {
@ -466,7 +466,7 @@ impl SelfProfilerRef {
pub fn with_profiler(&self, f: impl FnOnce(&SelfProfiler)) { pub fn with_profiler(&self, f: impl FnOnce(&SelfProfiler)) {
if let Some(profiler) = &self.profiler { if let Some(profiler) = &self.profiler {
f(&profiler) f(profiler)
} }
} }
@ -733,7 +733,7 @@ impl Drop for VerboseTimingGuard<'_> {
if let Some((start_time, start_rss, ref message)) = self.start_and_message { if let Some((start_time, start_rss, ref message)) = self.start_and_message {
let end_rss = get_resident_set_size(); let end_rss = get_resident_set_size();
let dur = start_time.elapsed(); let dur = start_time.elapsed();
print_time_passes_entry(&message, dur, start_rss, end_rss); print_time_passes_entry(message, dur, start_rss, end_rss);
} }
} }
} }

View file

@ -366,7 +366,7 @@ impl<CTX> HashStable<CTX> for [u8] {
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> { impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> {
#[inline] #[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(&self[..]).hash_stable(ctx, hasher); self[..].hash_stable(ctx, hasher);
} }
} }
@ -405,7 +405,7 @@ where
{ {
#[inline] #[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(&self[..]).hash_stable(ctx, hasher); self[..].hash_stable(ctx, hasher);
} }
} }
@ -440,7 +440,7 @@ impl<CTX> HashStable<CTX> for str {
impl<CTX> HashStable<CTX> for String { impl<CTX> HashStable<CTX> for String {
#[inline] #[inline]
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
(&self[..]).hash_stable(hcx, hasher); self[..].hash_stable(hcx, hasher);
} }
} }

View file

@ -201,7 +201,7 @@ cfg_if! {
#[inline(always)] #[inline(always)]
fn deref(&self) -> &T { fn deref(&self) -> &T {
&*self.0 &self.0
} }
} }

View file

@ -39,7 +39,7 @@ impl Translate for AnnotateSnippetEmitterWriter {
} }
fn fallback_fluent_bundle(&self) -> &FluentBundle { fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle &self.fallback_bundle
} }
} }
@ -49,7 +49,7 @@ impl Emitter for AnnotateSnippetEmitterWriter {
let fluent_args = to_fluent_args(diag.args()); let fluent_args = to_fluent_args(diag.args());
let mut children = diag.children.clone(); let mut children = diag.children.clone();
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
self.fix_multispans_in_extern_macros_and_render_macro_backtrace( self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
&mut primary_span, &mut primary_span,
@ -65,7 +65,7 @@ impl Emitter for AnnotateSnippetEmitterWriter {
&diag.code, &diag.code,
&primary_span, &primary_span,
&children, &children,
&suggestions, suggestions,
); );
} }

View file

@ -292,7 +292,7 @@ impl Diagnostic {
let lint_index = expectation_id.get_lint_index(); let lint_index = expectation_id.get_lint_index();
expectation_id.set_lint_index(None); expectation_id.set_lint_index(None);
let mut stable_id = unstable_to_stable let mut stable_id = unstable_to_stable
.get(&expectation_id) .get(expectation_id)
.expect("each unstable `LintExpectationId` must have a matching stable id") .expect("each unstable `LintExpectationId` must have a matching stable id")
.normalize(); .normalize();

View file

@ -283,7 +283,7 @@ pub trait Emitter: Translate {
if self if self
.source_map() .source_map()
.map(|sm| is_case_difference( .map(|sm| is_case_difference(
&**sm, sm,
substitution, substitution,
sugg.substitutions[0].parts[0].span, sugg.substitutions[0].parts[0].span,
)) ))
@ -525,7 +525,7 @@ impl Translate for EmitterWriter {
} }
fn fallback_fluent_bundle(&self) -> &FluentBundle { fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle &self.fallback_bundle
} }
} }
@ -538,7 +538,7 @@ impl Emitter for EmitterWriter {
let fluent_args = to_fluent_args(diag.args()); let fluent_args = to_fluent_args(diag.args());
let mut children = diag.children.clone(); let mut children = diag.children.clone();
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
debug!("emit_diagnostic: suggestions={:?}", suggestions); debug!("emit_diagnostic: suggestions={:?}", suggestions);
self.fix_multispans_in_extern_macros_and_render_macro_backtrace( self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
@ -555,7 +555,7 @@ impl Emitter for EmitterWriter {
&diag.code, &diag.code,
&primary_span, &primary_span,
&children, &children,
&suggestions, suggestions,
self.track_diagnostics.then_some(&diag.emitted_at), self.track_diagnostics.then_some(&diag.emitted_at),
); );
} }
@ -801,7 +801,7 @@ impl EmitterWriter {
} }
let source_string = match file.get_line(line.line_index - 1) { let source_string = match file.get_line(line.line_index - 1) {
Some(s) => normalize_whitespace(&*s), Some(s) => normalize_whitespace(&s),
None => return Vec::new(), None => return Vec::new(),
}; };
@ -1148,7 +1148,7 @@ impl EmitterWriter {
(pos + 2, annotation.start_col.saturating_sub(left)) (pos + 2, annotation.start_col.saturating_sub(left))
}; };
if let Some(ref label) = annotation.label { if let Some(ref label) = annotation.label {
buffer.puts(line_offset + pos, code_offset + col, &label, style); buffer.puts(line_offset + pos, code_offset + col, label, style);
} }
} }
@ -1358,7 +1358,7 @@ impl EmitterWriter {
// only render error codes, not lint codes // only render error codes, not lint codes
if let Some(DiagnosticId::Error(ref code)) = *code { if let Some(DiagnosticId::Error(ref code)) = *code {
buffer.append(0, "[", Style::Level(*level)); buffer.append(0, "[", Style::Level(*level));
buffer.append(0, &code, Style::Level(*level)); buffer.append(0, code, Style::Level(*level));
buffer.append(0, "]", Style::Level(*level)); buffer.append(0, "]", Style::Level(*level));
label_width += 2 + code.len(); label_width += 2 + code.len();
} }
@ -1683,7 +1683,7 @@ impl EmitterWriter {
}; };
// Render the replacements for each suggestion // Render the replacements for each suggestion
let suggestions = suggestion.splice_lines(&**sm); let suggestions = suggestion.splice_lines(sm);
debug!("emit_suggestion_default: suggestions={:?}", suggestions); debug!("emit_suggestion_default: suggestions={:?}", suggestions);
if suggestions.is_empty() { if suggestions.is_empty() {
@ -1784,7 +1784,7 @@ impl EmitterWriter {
buffer.puts( buffer.puts(
row_num - 1 + line - line_start, row_num - 1 + line - line_start,
max_line_num_len + 3, max_line_num_len + 3,
&normalize_whitespace(&*file_lines.file.get_line(line - 1).unwrap()), &normalize_whitespace(&file_lines.file.get_line(line - 1).unwrap()),
Style::Removal, Style::Removal,
); );
} }
@ -1926,7 +1926,7 @@ impl EmitterWriter {
buffer.putc( buffer.putc(
row_num, row_num,
(padding as isize + p) as usize, (padding as isize + p) as usize,
if part.is_addition(&sm) { '+' } else { '~' }, if part.is_addition(sm) { '+' } else { '~' },
Style::Addition, Style::Addition,
); );
} }
@ -1973,7 +1973,7 @@ impl EmitterWriter {
buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle); buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
} else if notice_capitalization { } else if notice_capitalization {
let msg = "notice the capitalization difference"; let msg = "notice the capitalization difference";
buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle); buffer.puts(row_num, max_line_num_len + 3, msg, Style::NoStyle);
} }
emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?; emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
Ok(()) Ok(())
@ -2028,7 +2028,7 @@ impl EmitterWriter {
for child in children { for child in children {
let span = child.render_span.as_ref().unwrap_or(&child.span); let span = child.render_span.as_ref().unwrap_or(&child.span);
if let Err(err) = self.emit_message_default( if let Err(err) = self.emit_message_default(
&span, span,
&child.message, &child.message,
args, args,
&None, &None,
@ -2113,7 +2113,7 @@ impl EmitterWriter {
*row_num - 1, *row_num - 1,
max_line_num_len + 3, max_line_num_len + 3,
&normalize_whitespace( &normalize_whitespace(
&*file_lines.file.get_line(file_lines.lines[line_pos].line_index).unwrap(), &file_lines.file.get_line(file_lines.lines[line_pos].line_index).unwrap(),
), ),
Style::NoStyle, Style::NoStyle,
); );

View file

@ -136,7 +136,7 @@ impl Translate for JsonEmitter {
} }
fn fallback_fluent_bundle(&self) -> &FluentBundle { fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle &self.fallback_bundle
} }
} }

View file

@ -1328,7 +1328,7 @@ impl HandlerInner {
diagnostic.children.drain_filter(already_emitted_sub).for_each(|_| {}); diagnostic.children.drain_filter(already_emitted_sub).for_each(|_| {});
self.emitter.emit_diagnostic(&diagnostic); self.emitter.emit_diagnostic(diagnostic);
if diagnostic.is_error() { if diagnostic.is_error() {
self.deduplicated_err_count += 1; self.deduplicated_err_count += 1;
} else if let Warning(_) = diagnostic.level { } else if let Warning(_) = diagnostic.level {

View file

@ -59,13 +59,13 @@ pub trait Translate {
trace!(?message, ?args); trace!(?message, ?args);
let (identifier, attr) = match message { let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => { DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
return Cow::Borrowed(&msg); return Cow::Borrowed(msg);
} }
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
}; };
let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> { let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> {
let message = bundle.get_message(&identifier)?; let message = bundle.get_message(identifier)?;
let value = match attr { let value = match attr {
Some(attr) => message.get_attribute(attr)?.value(), Some(attr) => message.get_attribute(attr)?.value(),
None => message.value()?, None => message.value()?,
@ -73,7 +73,7 @@ pub trait Translate {
debug!(?message, ?value); debug!(?message, ?value);
let mut errs = vec![]; let mut errs = vec![];
let translated = bundle.format_pattern(value, Some(&args), &mut errs); let translated = bundle.format_pattern(value, Some(args), &mut errs);
debug!(?translated, ?errs); debug!(?translated, ?errs);
Some((translated, errs)) Some((translated, errs))
}; };

View file

@ -539,6 +539,9 @@ impl<'a> ExtCtxt<'a> {
fn_decl, fn_decl,
body, body,
fn_decl_span: span, fn_decl_span: span,
// FIXME(SarthakSingh31): This points to the start of the declaration block and
// not the span of the argument block.
fn_arg_span: span,
})), })),
) )
} }

View file

@ -65,7 +65,7 @@ pub enum LinkOrCopy {
pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<LinkOrCopy> { pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<LinkOrCopy> {
let p = p.as_ref(); let p = p.as_ref();
let q = q.as_ref(); let q = q.as_ref();
match fs::remove_file(&q) { match fs::remove_file(q) {
Ok(()) => (), Ok(()) => (),
Err(err) if err.kind() == io::ErrorKind::NotFound => (), Err(err) if err.kind() == io::ErrorKind::NotFound => (),
Err(err) => return Err(err), Err(err) => return Err(err),

View file

@ -410,7 +410,7 @@ impl<'a> Id<'a> {
} }
pub fn as_slice(&'a self) -> &'a str { pub fn as_slice(&'a self) -> &'a str {
&*self.name &self.name
} }
} }
@ -515,7 +515,7 @@ impl<'a> LabelText<'a> {
pub fn to_dot_string(&self) -> String { pub fn to_dot_string(&self) -> String {
match *self { match *self {
LabelStr(ref s) => format!("\"{}\"", s.escape_default()), LabelStr(ref s) => format!("\"{}\"", s.escape_default()),
EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s)), EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(s)),
HtmlStr(ref s) => format!("<{}>", s), HtmlStr(ref s) => format!("<{}>", s),
} }
} }
@ -529,7 +529,7 @@ impl<'a> LabelText<'a> {
EscStr(s) => s, EscStr(s) => s,
LabelStr(s) => { LabelStr(s) => {
if s.contains('\\') { if s.contains('\\') {
(&*s).escape_default().to_string().into() s.escape_default().to_string().into()
} else { } else {
s s
} }

View file

@ -943,7 +943,10 @@ pub struct Closure<'hir> {
pub bound_generic_params: &'hir [GenericParam<'hir>], pub bound_generic_params: &'hir [GenericParam<'hir>],
pub fn_decl: &'hir FnDecl<'hir>, pub fn_decl: &'hir FnDecl<'hir>,
pub body: BodyId, pub body: BodyId,
/// The span of the declaration block: 'move |...| -> ...'
pub fn_decl_span: Span, pub fn_decl_span: Span,
/// The span of the argument block `|...|`
pub fn_arg_span: Option<Span>,
pub movability: Option<Movability>, pub movability: Option<Movability>,
} }
@ -2434,7 +2437,7 @@ impl<'hir> Ty<'hir> {
pub fn peel_refs(&self) -> &Self { pub fn peel_refs(&self) -> &Self {
let mut final_ty = self; let mut final_ty = self;
while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind { while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind {
final_ty = &ty; final_ty = ty;
} }
final_ty final_ty
} }

View file

@ -116,7 +116,7 @@ impl Ord for HirId {
impl PartialOrd for HirId { impl PartialOrd for HirId {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(&other)) Some(self.cmp(other))
} }
} }

View file

@ -448,7 +448,7 @@ pub trait Visitor<'v>: Sized {
pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) { pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) {
visitor.visit_id(param.hir_id); visitor.visit_id(param.hir_id);
visitor.visit_pat(&param.pat); visitor.visit_pat(param.pat);
} }
pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
@ -470,7 +470,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
} }
ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn( ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn(
FnKind::ItemFn(item.ident, generics, sig.header), FnKind::ItemFn(item.ident, generics, sig.header),
&sig.decl, sig.decl,
body_id, body_id,
item.span, item.span,
item.hir_id(), item.hir_id(),
@ -544,7 +544,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body<'v>) { pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body<'v>) {
walk_list!(visitor, visit_param, body.params); walk_list!(visitor, visit_param, body.params);
visitor.visit_expr(&body.value); visitor.visit_expr(body.value);
} }
pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) { pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) {
@ -580,7 +580,7 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) {
// dominates the local's definition. // dominates the local's definition.
walk_list!(visitor, visit_expr, &local.init); walk_list!(visitor, visit_expr, &local.init);
visitor.visit_id(local.hir_id); visitor.visit_id(local.hir_id);
visitor.visit_pat(&local.pat); visitor.visit_pat(local.pat);
if let Some(els) = local.els { if let Some(els) = local.els {
visitor.visit_block(els); visitor.visit_block(els);
} }
@ -606,7 +606,7 @@ pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) {
pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) { pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) {
visitor.visit_id(arm.hir_id); visitor.visit_id(arm.hir_id);
visitor.visit_pat(&arm.pat); visitor.visit_pat(arm.pat);
if let Some(ref g) = arm.guard { if let Some(ref g) = arm.guard {
match g { match g {
Guard::If(ref e) => visitor.visit_expr(e), Guard::If(ref e) => visitor.visit_expr(e),
@ -615,7 +615,7 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) {
} }
} }
} }
visitor.visit_expr(&arm.body); visitor.visit_expr(arm.body);
} }
pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) { pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) {
@ -660,7 +660,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) {
pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<'v>) { pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<'v>) {
visitor.visit_id(field.hir_id); visitor.visit_id(field.hir_id);
visitor.visit_ident(field.ident); visitor.visit_ident(field.ident);
visitor.visit_pat(&field.pat) visitor.visit_pat(field.pat)
} }
pub fn walk_array_len<'v, V: Visitor<'v>>(visitor: &mut V, len: &'v ArrayLen) { pub fn walk_array_len<'v, V: Visitor<'v>>(visitor: &mut V, len: &'v ArrayLen) {
@ -740,6 +740,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
body, body,
capture_clause: _, capture_clause: _,
fn_decl_span: _, fn_decl_span: _,
fn_arg_span: _,
movability: _, movability: _,
}) => { }) => {
walk_list!(visitor, visit_generic_param, bound_generic_params); walk_list!(visitor, visit_generic_param, bound_generic_params);
@ -799,7 +800,7 @@ pub fn walk_let_expr<'v, V: Visitor<'v>>(visitor: &mut V, let_expr: &'v Let<'v>)
pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField<'v>) { pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField<'v>) {
visitor.visit_id(field.hir_id); visitor.visit_id(field.hir_id);
visitor.visit_ident(field.ident); visitor.visit_ident(field.ident);
visitor.visit_expr(&field.expr) visitor.visit_expr(field.expr)
} }
pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
@ -807,10 +808,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
match typ.kind { match typ.kind {
TyKind::Slice(ref ty) => visitor.visit_ty(ty), TyKind::Slice(ref ty) => visitor.visit_ty(ty),
TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty), TyKind::Ptr(ref mutable_type) => visitor.visit_ty(mutable_type.ty),
TyKind::Rptr(ref lifetime, ref mutable_type) => { TyKind::Rptr(ref lifetime, ref mutable_type) => {
visitor.visit_lifetime(lifetime); visitor.visit_lifetime(lifetime);
visitor.visit_ty(&mutable_type.ty) visitor.visit_ty(mutable_type.ty)
} }
TyKind::Never => {} TyKind::Never => {}
TyKind::Tup(tuple_element_types) => { TyKind::Tup(tuple_element_types) => {
@ -818,7 +819,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
} }
TyKind::BareFn(ref function_declaration) => { TyKind::BareFn(ref function_declaration) => {
walk_list!(visitor, visit_generic_param, function_declaration.generic_params); walk_list!(visitor, visit_generic_param, function_declaration.generic_params);
visitor.visit_fn_decl(&function_declaration.decl); visitor.visit_fn_decl(function_declaration.decl);
} }
TyKind::Path(ref qpath) => { TyKind::Path(ref qpath) => {
visitor.visit_qpath(qpath, typ.hir_id, typ.span); visitor.visit_qpath(qpath, typ.hir_id, typ.span);
@ -951,8 +952,8 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
let TraitItem { ident, generics, ref defaultness, ref kind, span, owner_id: _ } = *trait_item; let TraitItem { ident, generics, ref defaultness, ref kind, span, owner_id: _ } = *trait_item;
let hir_id = trait_item.hir_id(); let hir_id = trait_item.hir_id();
visitor.visit_ident(ident); visitor.visit_ident(ident);
visitor.visit_generics(&generics); visitor.visit_generics(generics);
visitor.visit_defaultness(&defaultness); visitor.visit_defaultness(defaultness);
match *kind { match *kind {
TraitItemKind::Const(ref ty, default) => { TraitItemKind::Const(ref ty, default) => {
visitor.visit_id(hir_id); visitor.visit_id(hir_id);
@ -961,13 +962,13 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
} }
TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => { TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => {
visitor.visit_id(hir_id); visitor.visit_id(hir_id);
visitor.visit_fn_decl(&sig.decl); visitor.visit_fn_decl(sig.decl);
for &param_name in param_names { for &param_name in param_names {
visitor.visit_ident(param_name); visitor.visit_ident(param_name);
} }
} }
TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => { TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => {
visitor.visit_fn(FnKind::Method(ident, sig), &sig.decl, body_id, span, hir_id); visitor.visit_fn(FnKind::Method(ident, sig), sig.decl, body_id, span, hir_id);
} }
TraitItemKind::Type(bounds, ref default) => { TraitItemKind::Type(bounds, ref default) => {
visitor.visit_id(hir_id); visitor.visit_id(hir_id);
@ -1009,7 +1010,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt
ImplItemKind::Fn(ref sig, body_id) => { ImplItemKind::Fn(ref sig, body_id) => {
visitor.visit_fn( visitor.visit_fn(
FnKind::Method(impl_item.ident, sig), FnKind::Method(impl_item.ident, sig),
&sig.decl, sig.decl,
body_id, body_id,
impl_item.span, impl_item.span,
impl_item.hir_id(), impl_item.hir_id(),
@ -1042,7 +1043,7 @@ pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, impl_item_ref: &'
pub fn walk_trait_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v TraitRef<'v>) { pub fn walk_trait_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v TraitRef<'v>) {
visitor.visit_id(trait_ref.hir_ref_id); visitor.visit_id(trait_ref.hir_ref_id);
visitor.visit_path(&trait_ref.path, trait_ref.hir_ref_id) visitor.visit_path(trait_ref.path, trait_ref.hir_ref_id)
} }
pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericBound<'v>) { pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericBound<'v>) {
@ -1074,7 +1075,7 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(
pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) { pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) {
visitor.visit_id(field.hir_id); visitor.visit_id(field.hir_id);
visitor.visit_ident(field.ident); visitor.visit_ident(field.ident);
visitor.visit_ty(&field.ty); visitor.visit_ty(field.ty);
} }
pub fn walk_enum_def<'v, V: Visitor<'v>>( pub fn walk_enum_def<'v, V: Visitor<'v>>(

View file

@ -56,25 +56,6 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx
.maybe_unused_extern_crates(()) .maybe_unused_extern_crates(())
.iter() .iter()
.filter(|&&(def_id, _)| {
// The `def_id` here actually was calculated during resolution (at least
// at the time of this writing) and is being shipped to us via a side
// channel of the tcx. There may have been extra expansion phases,
// however, which ended up removing the `def_id` *after* expansion.
//
// As a result we need to verify that `def_id` is indeed still valid for
// our AST and actually present in the HIR map. If it's not there then
// there's safely nothing to warn about, and otherwise we carry on with
// our execution.
//
// Note that if we carry through to the `extern_mod_stmt_cnum` query
// below it'll cause a panic because `def_id` is actually bogus at this
// point in time otherwise.
if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() {
return false;
}
true
})
.filter(|&&(def_id, _)| { .filter(|&&(def_id, _)| {
tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| { tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| {
!tcx.is_compiler_builtins(cnum) !tcx.is_compiler_builtins(cnum)

View file

@ -2073,6 +2073,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
} }
} }
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
codegen_fn_attrs.inline = InlineAttr::Never;
}
// Weak lang items have the same semantics as "std internal" symbols in the // Weak lang items have the same semantics as "std internal" symbols in the
// sense that they're preserved through all our LTO passes and only // sense that they're preserved through all our LTO passes and only
// strippable by the linker. // strippable by the linker.

View file

@ -1480,6 +1480,7 @@ impl<'a> State<'a> {
fn_decl, fn_decl,
body, body,
fn_decl_span: _, fn_decl_span: _,
fn_arg_span: _,
movability: _, movability: _,
def_id: _, def_id: _,
}) => { }) => {

View file

@ -456,10 +456,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.iter() .iter()
.map(|ty| ArgKind::from_expected_ty(*ty, None)) .map(|ty| ArgKind::from_expected_ty(*ty, None))
.collect(); .collect();
let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) { let (closure_span, closure_arg_span, found_args) =
Some((sp, args)) => (Some(sp), args), match self.get_fn_like_arguments(expr_map_node) {
None => (None, Vec::new()), Some((sp, arg_sp, args)) => (Some(sp), arg_sp, args),
}; None => (None, None, Vec::new()),
};
let expected_span = let expected_span =
expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id)); expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id));
self.report_arg_count_mismatch( self.report_arg_count_mismatch(
@ -468,6 +469,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected_args, expected_args,
found_args, found_args,
true, true,
closure_arg_span,
) )
.emit(); .emit();

View file

@ -1918,6 +1918,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
receiver: Option<&'tcx hir::Expr<'tcx>>, receiver: Option<&'tcx hir::Expr<'tcx>>,
args: &'tcx [hir::Expr<'tcx>], args: &'tcx [hir::Expr<'tcx>],
) -> bool { ) -> bool {
// Do not call `fn_sig` on non-functions.
if !matches!(
self.tcx.def_kind(def_id),
DefKind::Fn | DefKind::AssocFn | DefKind::Variant | DefKind::Ctor(..)
) {
return false;
}
let sig = self.tcx.fn_sig(def_id).skip_binder(); let sig = self.tcx.fn_sig(def_id).skip_binder();
let args_referencing_param: Vec<_> = sig let args_referencing_param: Vec<_> = sig
.inputs() .inputs()

View file

@ -20,7 +20,7 @@ use rustc_hir::def_id::DefId;
use rustc_infer::infer::{self, InferOk}; use rustc_infer::infer::{self, InferOk};
use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, Ty, TypeVisitable}; use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitable};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::Span; use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@ -217,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
// We probe again, taking all traits into account (not only those in scope). // We probe again, taking all traits into account (not only those in scope).
let mut candidates = let candidates =
match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) { match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) {
// If we find a different result the caller probably forgot to import a trait. // If we find a different result the caller probably forgot to import a trait.
Ok(ref new_pick) if pick.differs_from(new_pick) => { Ok(ref new_pick) if pick.differs_from(new_pick) => {
@ -236,7 +236,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.collect(), .collect(),
_ => Vec::new(), _ => Vec::new(),
}; };
candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id));
return Err(IllegalSizedBound(candidates, needs_mut, span)); return Err(IllegalSizedBound(candidates, needs_mut, span));
} }

View file

@ -29,8 +29,8 @@ impl<'a> DiagnosticDerive<'a> {
let DiagnosticDerive { mut structure, mut builder } = self; let DiagnosticDerive { mut structure, mut builder } = self;
let implementation = builder.each_variant(&mut structure, |mut builder, variant| { let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(&variant); let preamble = builder.preamble(variant);
let body = builder.body(&variant); let body = builder.body(variant);
let diag = &builder.parent.diag; let diag = &builder.parent.diag;
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else { let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
@ -39,7 +39,7 @@ impl<'a> DiagnosticDerive<'a> {
let init = match builder.slug.value_ref() { let init = match builder.slug.value_ref() {
None => { None => {
span_err(builder.span, "diagnostic slug not specified") span_err(builder.span, "diagnostic slug not specified")
.help(&format!( .help(format!(
"specify the slug as the first argument to the `#[diag(...)]` \ "specify the slug as the first argument to the `#[diag(...)]` \
attribute, such as `#[diag(hir_analysis_example_error)]`", attribute, such as `#[diag(hir_analysis_example_error)]`",
)) ))
@ -48,10 +48,10 @@ impl<'a> DiagnosticDerive<'a> {
} }
Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => {
span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match") span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match")
.note(&format!( .note(format!(
"slug is `{slug_name}` but the crate name is `{crate_name}`" "slug is `{slug_name}` but the crate name is `{crate_name}`"
)) ))
.help(&format!( .help(format!(
"expected a slug starting with `{slug_prefix}_...`" "expected a slug starting with `{slug_prefix}_...`"
)) ))
.emit(); .emit();
@ -113,8 +113,8 @@ impl<'a> LintDiagnosticDerive<'a> {
let LintDiagnosticDerive { mut structure, mut builder } = self; let LintDiagnosticDerive { mut structure, mut builder } = self;
let implementation = builder.each_variant(&mut structure, |mut builder, variant| { let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(&variant); let preamble = builder.preamble(variant);
let body = builder.body(&variant); let body = builder.body(variant);
let diag = &builder.parent.diag; let diag = &builder.parent.diag;
let formatting_init = &builder.formatting_init; let formatting_init = &builder.formatting_init;
@ -128,28 +128,28 @@ impl<'a> LintDiagnosticDerive<'a> {
let msg = builder.each_variant(&mut structure, |mut builder, variant| { let msg = builder.each_variant(&mut structure, |mut builder, variant| {
// Collect the slug by generating the preamble. // Collect the slug by generating the preamble.
let _ = builder.preamble(&variant); let _ = builder.preamble(variant);
match builder.slug.value_ref() { match builder.slug.value_ref() {
None => { None => {
span_err(builder.span, "diagnostic slug not specified") span_err(builder.span, "diagnostic slug not specified")
.help(&format!( .help(format!(
"specify the slug as the first argument to the attribute, such as \ "specify the slug as the first argument to the attribute, such as \
`#[diag(compiletest_example)]`", `#[diag(compiletest_example)]`",
)) ))
.emit(); .emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error(); DiagnosticDeriveError::ErrorHandled.to_compile_error()
} }
Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => {
span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match") span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match")
.note(&format!( .note(format!(
"slug is `{slug_name}` but the crate name is `{crate_name}`" "slug is `{slug_name}` but the crate name is `{crate_name}`"
)) ))
.help(&format!( .help(format!(
"expected a slug starting with `{slug_prefix}_...`" "expected a slug starting with `{slug_prefix}_...`"
)) ))
.emit(); .emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error(); DiagnosticDeriveError::ErrorHandled.to_compile_error()
} }
Some(slug) => { Some(slug) => {
quote! { quote! {

View file

@ -100,7 +100,7 @@ impl DiagnosticDeriveBuilder {
_ => variant.ast().ident.span().unwrap(), _ => variant.ast().ident.span().unwrap(),
}; };
let builder = DiagnosticDeriveVariantBuilder { let builder = DiagnosticDeriveVariantBuilder {
parent: &self, parent: self,
span, span,
field_map: build_field_mapping(variant), field_map: build_field_mapping(variant),
formatting_init: TokenStream::new(), formatting_init: TokenStream::new(),
@ -211,7 +211,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
nested_iter.next(); nested_iter.next();
} }
Some(NestedMeta::Meta(Meta::NameValue { .. })) => {} Some(NestedMeta::Meta(Meta::NameValue { .. })) => {}
Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| diag Some(nested_attr) => throw_invalid_nested_attr!(attr, nested_attr, |diag| diag
.help("a diagnostic slug is required as the first argument")), .help("a diagnostic slug is required as the first argument")),
None => throw_invalid_attr!(attr, &meta, |diag| diag None => throw_invalid_attr!(attr, &meta, |diag| diag
.help("a diagnostic slug is required as the first argument")), .help("a diagnostic slug is required as the first argument")),
@ -227,13 +227,13 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
.. ..
})) => (value, path), })) => (value, path),
NestedMeta::Meta(Meta::Path(_)) => { NestedMeta::Meta(Meta::Path(_)) => {
invalid_nested_attr(attr, &nested_attr) invalid_nested_attr(attr, nested_attr)
.help("diagnostic slug must be the first argument") .help("diagnostic slug must be the first argument")
.emit(); .emit();
continue; continue;
} }
_ => { _ => {
invalid_nested_attr(attr, &nested_attr).emit(); invalid_nested_attr(attr, nested_attr).emit();
continue; continue;
} }
}; };
@ -251,7 +251,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
#diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
}); });
} }
_ => invalid_nested_attr(attr, &nested_attr) _ => invalid_nested_attr(attr, nested_attr)
.help("only `code` is a valid nested attributes following the slug") .help("only `code` is a valid nested attributes following the slug")
.emit(), .emit(),
} }
@ -427,9 +427,9 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
} }
SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => {
if type_matches_path(&info.ty, &["rustc_span", "Span"]) { if type_matches_path(info.ty, &["rustc_span", "Span"]) {
Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
} else if type_is_unit(&info.ty) { } else if type_is_unit(info.ty) {
Ok(self.add_subdiagnostic(&fn_ident, slug)) Ok(self.add_subdiagnostic(&fn_ident, slug))
} else { } else {
report_type_error(attr, "`Span` or `()`")? report_type_error(attr, "`Span` or `()`")?

View file

@ -409,7 +409,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
let mut code = None; let mut code = None;
for nested_attr in list.nested.iter() { for nested_attr in list.nested.iter() {
let NestedMeta::Meta(ref meta) = nested_attr else { let NestedMeta::Meta(ref meta) = nested_attr else {
throw_invalid_nested_attr!(attr, &nested_attr); throw_invalid_nested_attr!(attr, nested_attr);
}; };
let span = meta.span().unwrap(); let span = meta.span().unwrap();
@ -427,7 +427,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
); );
code.set_once((code_field, formatting_init), span); code.set_once((code_field, formatting_init), span);
} }
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { _ => throw_invalid_nested_attr!(attr, nested_attr, |diag| {
diag.help("`code` is the only valid nested attribute") diag.help("`code` is the only valid nested attribute")
}), }),
} }

View file

@ -80,7 +80,7 @@ fn report_error_if_not_applied_to_ty(
path: &[&str], path: &[&str],
ty_name: &str, ty_name: &str,
) -> Result<(), DiagnosticDeriveError> { ) -> Result<(), DiagnosticDeriveError> {
if !type_matches_path(&info.ty, path) { if !type_matches_path(info.ty, path) {
report_type_error(attr, ty_name)?; report_type_error(attr, ty_name)?;
} }
@ -105,8 +105,8 @@ pub(crate) fn report_error_if_not_applied_to_span(
attr: &Attribute, attr: &Attribute,
info: &FieldInfo<'_>, info: &FieldInfo<'_>,
) -> Result<(), DiagnosticDeriveError> { ) -> Result<(), DiagnosticDeriveError> {
if !type_matches_path(&info.ty, &["rustc_span", "Span"]) if !type_matches_path(info.ty, &["rustc_span", "Span"])
&& !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"]) && !type_matches_path(info.ty, &["rustc_errors", "MultiSpan"])
{ {
report_type_error(attr, "`Span` or `MultiSpan`")?; report_type_error(attr, "`Span` or `MultiSpan`")?;
} }
@ -686,7 +686,7 @@ impl SubdiagnosticKind {
let meta = match nested_attr { let meta = match nested_attr {
NestedMeta::Meta(ref meta) => meta, NestedMeta::Meta(ref meta) => meta,
NestedMeta::Lit(_) => { NestedMeta::Lit(_) => {
invalid_nested_attr(attr, &nested_attr).emit(); invalid_nested_attr(attr, nested_attr).emit();
continue; continue;
} }
}; };
@ -698,7 +698,7 @@ impl SubdiagnosticKind {
let string_value = match meta { let string_value = match meta {
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => Some(value), Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => Some(value),
Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { Meta::Path(_) => throw_invalid_nested_attr!(attr, nested_attr, |diag| {
diag.help("a diagnostic slug must be the first argument to the attribute") diag.help("a diagnostic slug must be the first argument to the attribute")
}), }),
_ => None, _ => None,
@ -720,7 +720,7 @@ impl SubdiagnosticKind {
| SubdiagnosticKind::MultipartSuggestion { ref mut applicability, .. }, | SubdiagnosticKind::MultipartSuggestion { ref mut applicability, .. },
) => { ) => {
let Some(value) = string_value else { let Some(value) = string_value else {
invalid_nested_attr(attr, &nested_attr).emit(); invalid_nested_attr(attr, nested_attr).emit();
continue; continue;
}; };
@ -736,7 +736,7 @@ impl SubdiagnosticKind {
| SubdiagnosticKind::MultipartSuggestion { .. }, | SubdiagnosticKind::MultipartSuggestion { .. },
) => { ) => {
let Some(value) = string_value else { let Some(value) = string_value else {
invalid_nested_attr(attr, &nested_attr).emit(); invalid_nested_attr(attr, nested_attr).emit();
continue; continue;
}; };
@ -752,19 +752,19 @@ impl SubdiagnosticKind {
// Invalid nested attribute // Invalid nested attribute
(_, SubdiagnosticKind::Suggestion { .. }) => { (_, SubdiagnosticKind::Suggestion { .. }) => {
invalid_nested_attr(attr, &nested_attr) invalid_nested_attr(attr, nested_attr)
.help( .help(
"only `style`, `code` and `applicability` are valid nested attributes", "only `style`, `code` and `applicability` are valid nested attributes",
) )
.emit(); .emit();
} }
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => { (_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
invalid_nested_attr(attr, &nested_attr) invalid_nested_attr(attr, nested_attr)
.help("only `style` and `applicability` are valid nested attributes") .help("only `style` and `applicability` are valid nested attributes")
.emit() .emit()
} }
_ => { _ => {
invalid_nested_attr(attr, &nested_attr).emit(); invalid_nested_attr(attr, nested_attr).emit();
} }
} }
} }

View file

@ -1022,7 +1022,7 @@ impl<'hir> Map<'hir> {
.. ..
}) => { }) => {
// Ensure that the returned span has the item's SyntaxContext. // Ensure that the returned span has the item's SyntaxContext.
fn_decl_span.find_ancestor_in_same_ctxt(*span).unwrap_or(*span) fn_decl_span.find_ancestor_inside(*span).unwrap_or(*span)
} }
_ => self.span_with_body(hir_id), _ => self.span_with_body(hir_id),
}; };

View file

@ -363,10 +363,6 @@ impl<'tcx> Inliner<'tcx> {
return Err("C variadic"); return Err("C variadic");
} }
if callee_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
return Err("naked");
}
if callee_attrs.flags.contains(CodegenFnAttrFlags::COLD) { if callee_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
return Err("cold"); return Err("cold");
} }

View file

@ -2060,7 +2060,7 @@ impl<'a> Parser<'a> {
}; };
let capture_clause = self.parse_capture_clause()?; let capture_clause = self.parse_capture_clause()?;
let fn_decl = self.parse_fn_block_decl()?; let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?;
let decl_hi = self.prev_token.span; let decl_hi = self.prev_token.span;
let mut body = match fn_decl.output { let mut body = match fn_decl.output {
FnRetTy::Default(_) => { FnRetTy::Default(_) => {
@ -2101,6 +2101,7 @@ impl<'a> Parser<'a> {
fn_decl, fn_decl,
body, body,
fn_decl_span: lo.to(decl_hi), fn_decl_span: lo.to(decl_hi),
fn_arg_span,
})), })),
); );
@ -2129,7 +2130,9 @@ impl<'a> Parser<'a> {
} }
/// Parses the `|arg, arg|` header of a closure. /// Parses the `|arg, arg|` header of a closure.
fn parse_fn_block_decl(&mut self) -> PResult<'a, P<FnDecl>> { fn parse_fn_block_decl(&mut self) -> PResult<'a, (P<FnDecl>, Span)> {
let arg_start = self.token.span.lo();
let inputs = if self.eat(&token::OrOr) { let inputs = if self.eat(&token::OrOr) {
Vec::new() Vec::new()
} else { } else {
@ -2145,10 +2148,11 @@ impl<'a> Parser<'a> {
self.expect_or()?; self.expect_or()?;
args args
}; };
let arg_span = self.prev_token.span.with_lo(arg_start);
let output = let output =
self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes)?; self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes)?;
Ok(P(FnDecl { inputs, output })) Ok((P(FnDecl { inputs, output }), arg_span))
} }
/// Parses a parameter in a closure header (e.g., `|arg, arg|`). /// Parses a parameter in a closure header (e.g., `|arg, arg|`).

View file

@ -4,7 +4,10 @@ use crate::diagnostics::{import_candidates, Suggestion};
use crate::Determinacy::{self, *}; use crate::Determinacy::{self, *};
use crate::Namespace::*; use crate::Namespace::*;
use crate::{module_to_string, names_to_string, ImportSuggestion}; use crate::{module_to_string, names_to_string, ImportSuggestion};
use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; use crate::{
AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingKey, ModuleKind, ResolutionError,
Resolver, Segment,
};
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet}; use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
use crate::{NameBinding, NameBindingKind, PathResult}; use crate::{NameBinding, NameBindingKind, PathResult};
@ -791,7 +794,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
match binding { match binding {
Ok(binding) => { Ok(binding) => {
// Consistency checks, analogous to `finalize_macro_resolutions`. // Consistency checks, analogous to `finalize_macro_resolutions`.
let initial_res = source_bindings[ns].get().map(|initial_binding| { let initial_binding = source_bindings[ns].get().map(|initial_binding| {
all_ns_err = false; all_ns_err = false;
if let Some(target_binding) = target_bindings[ns].get() { if let Some(target_binding) = target_bindings[ns].get() {
if target.name == kw::Underscore if target.name == kw::Underscore
@ -805,12 +808,20 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
); );
} }
} }
initial_binding.res() initial_binding
}); });
let res = binding.res(); let res = binding.res();
if let Ok(initial_res) = initial_res { if let Ok(initial_binding) = initial_binding {
let initial_res = initial_binding.res();
if res != initial_res && this.ambiguity_errors.is_empty() { if res != initial_res && this.ambiguity_errors.is_empty() {
span_bug!(import.span, "inconsistent resolution for an import"); this.ambiguity_errors.push(AmbiguityError {
kind: AmbiguityKind::Import,
ident,
b1: initial_binding,
b2: binding,
misc1: AmbiguityErrorMisc::None,
misc2: AmbiguityErrorMisc::None,
});
} }
} else if res != Res::Err } else if res != Res::Err
&& this.ambiguity_errors.is_empty() && this.ambiguity_errors.is_empty()

View file

@ -121,7 +121,7 @@ impl CguReuseTracker {
let at_least = if at_least { 1 } else { 0 }; let at_least = if at_least { 1 } else { 0 };
IncorrectCguReuseType { IncorrectCguReuseType {
span: error_span.0, span: error_span.0,
cgu_user_name: &cgu_user_name, cgu_user_name,
actual_reuse, actual_reuse,
expected_reuse, expected_reuse,
at_least, at_least,

View file

@ -622,7 +622,7 @@ impl OutputFilenames {
/// should be placed on disk. /// should be placed on disk.
pub fn output_path(&self, flavor: OutputType) -> PathBuf { pub fn output_path(&self, flavor: OutputType) -> PathBuf {
let extension = flavor.extension(); let extension = flavor.extension();
self.with_directory_and_extension(&self.out_directory, &extension) self.with_directory_and_extension(&self.out_directory, extension)
} }
/// Gets the path where a compilation artifact of the given type for the /// Gets the path where a compilation artifact of the given type for the
@ -659,7 +659,7 @@ impl OutputFilenames {
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
self.with_directory_and_extension(&temps_directory, &extension) self.with_directory_and_extension(temps_directory, &extension)
} }
pub fn with_extension(&self, extension: &str) -> PathBuf { pub fn with_extension(&self, extension: &str) -> PathBuf {
@ -1159,7 +1159,7 @@ impl CrateCheckConfig {
values_target_family values_target_family
.extend(target.options.families.iter().map(|family| Symbol::intern(family))); .extend(target.options.families.iter().map(|family| Symbol::intern(family)));
values_target_arch.insert(Symbol::intern(&target.arch)); values_target_arch.insert(Symbol::intern(&target.arch));
values_target_endian.insert(Symbol::intern(&target.options.endian.as_str())); values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
values_target_env.insert(Symbol::intern(&target.options.env)); values_target_env.insert(Symbol::intern(&target.options.env));
values_target_abi.insert(Symbol::intern(&target.options.abi)); values_target_abi.insert(Symbol::intern(&target.options.abi));
values_target_vendor.insert(Symbol::intern(&target.options.vendor)); values_target_vendor.insert(Symbol::intern(&target.options.vendor));
@ -1846,7 +1846,7 @@ pub fn parse_target_triple(
match matches.opt_str("target") { match matches.opt_str("target") {
Some(target) if target.ends_with(".json") => { Some(target) if target.ends_with(".json") => {
let path = Path::new(&target); let path = Path::new(&target);
TargetTriple::from_path(&path).unwrap_or_else(|_| { TargetTriple::from_path(path).unwrap_or_else(|_| {
early_error(error_format, &format!("target file {path:?} does not exist")) early_error(error_format, &format!("target file {path:?} does not exist"))
}) })
} }
@ -1992,7 +1992,7 @@ fn parse_native_lib_modifiers(
) -> (NativeLibKind, Option<bool>) { ) -> (NativeLibKind, Option<bool>) {
let mut verbatim = None; let mut verbatim = None;
for modifier in modifiers.split(',') { for modifier in modifiers.split(',') {
let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { let (modifier, value) = match modifier.strip_prefix(['+', '-']) {
Some(m) => (m, modifier.starts_with('+')), Some(m) => (m, modifier.starts_with('+')),
None => early_error( None => early_error(
error_format, error_format,
@ -2421,7 +2421,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let mut search_paths = vec![]; let mut search_paths = vec![];
for s in &matches.opt_strs("L") { for s in &matches.opt_strs("L") {
search_paths.push(SearchPath::from_cli_opt(&s, error_format)); search_paths.push(SearchPath::from_cli_opt(s, error_format));
} }
let libs = parse_libs(matches, error_format); let libs = parse_libs(matches, error_format);

View file

@ -317,7 +317,7 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span:
LitError::InvalidIntSuffix => { LitError::InvalidIntSuffix => {
let suf = suffix.expect("suffix error with no suffix"); let suf = suffix.expect("suffix error with no suffix");
let suf = suf.as_str(); let suf = suf.as_str();
if looks_like_width_suffix(&['i', 'u'], &suf) { if looks_like_width_suffix(&['i', 'u'], suf) {
// If it looks like a width, try to be helpful. // If it looks like a width, try to be helpful.
sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() }); sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() });
} else if let Some(fixed) = fix_base_capitalisation(suf) { } else if let Some(fixed) = fix_base_capitalisation(suf) {

View file

@ -247,7 +247,7 @@ fn analyze_source_file_generic(
// The slow path: // The slow path:
// This is either ASCII control character "DEL" or the beginning of // This is either ASCII control character "DEL" or the beginning of
// a multibyte char. Just decode to `char`. // a multibyte char. Just decode to `char`.
let c = (&src[i..]).chars().next().unwrap(); let c = src[i..].chars().next().unwrap();
char_len = c.len_utf8(); char_len = c.len_utf8();
let pos = BytePos::from_usize(i) + output_offset; let pos = BytePos::from_usize(i) + output_offset;

View file

@ -165,7 +165,7 @@ impl<'sm> CachingSourceMapView<'sm> {
Some(new_file_and_idx) Some(new_file_and_idx)
} else { } else {
let file = &self.line_cache[oldest].file; let file = &self.line_cache[oldest].file;
if !file_contains(&file, span_data.hi) { if !file_contains(file, span_data.hi) {
return None; return None;
} }

View file

@ -381,7 +381,7 @@ impl HygieneData {
} }
pub fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T { pub fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
with_session_globals(|session_globals| f(&mut *session_globals.hygiene_data.borrow_mut())) with_session_globals(|session_globals| f(&mut session_globals.hygiene_data.borrow_mut()))
} }
#[inline] #[inline]

View file

@ -238,7 +238,7 @@ impl RealFileName {
pub fn remapped_path_if_available(&self) -> &Path { pub fn remapped_path_if_available(&self) -> &Path {
match self { match self {
RealFileName::LocalPath(p) RealFileName::LocalPath(p)
| RealFileName::Remapped { local_path: _, virtual_name: p } => &p, | RealFileName::Remapped { local_path: _, virtual_name: p } => p,
} }
} }

View file

@ -166,5 +166,5 @@ impl SpanInterner {
// If an interner exists, return it. Otherwise, prepare a fresh one. // If an interner exists, return it. Otherwise, prepare a fresh one.
#[inline] #[inline]
fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T { fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
crate::with_session_globals(|session_globals| f(&mut *session_globals.span_interner.lock())) crate::with_session_globals(|session_globals| f(&mut session_globals.span_interner.lock()))
} }

View file

@ -1877,7 +1877,7 @@ impl<S: Encoder> Encodable<S> for Symbol {
impl<D: Decoder> Decodable<D> for Symbol { impl<D: Decoder> Decodable<D> for Symbol {
#[inline] #[inline]
default fn decode(d: &mut D) -> Symbol { default fn decode(d: &mut D) -> Symbol {
Symbol::intern(&d.read_str()) Symbol::intern(d.read_str())
} }
} }

View file

@ -78,7 +78,7 @@ fn arg_scalar_pair<C>(
where where
C: HasDataLayout, C: HasDataLayout,
{ {
data = arg_scalar(cx, &scalar1, offset, data); data = arg_scalar(cx, scalar1, offset, data);
match (scalar1.primitive(), scalar2.primitive()) { match (scalar1.primitive(), scalar2.primitive()) {
(abi::F32, _) => offset += Reg::f32().size, (abi::F32, _) => offset += Reg::f32().size,
(_, abi::F64) => offset += Reg::f64().size, (_, abi::F64) => offset += Reg::f64().size,
@ -90,7 +90,7 @@ where
if (offset.bytes() % 4) != 0 && scalar2.primitive().is_float() { if (offset.bytes() % 4) != 0 && scalar2.primitive().is_float() {
offset += Size::from_bytes(4 - (offset.bytes() % 4)); offset += Size::from_bytes(4 - (offset.bytes() % 4));
} }
data = arg_scalar(cx, &scalar2, offset, data); data = arg_scalar(cx, scalar2, offset, data);
return data; return data;
} }

View file

@ -2658,7 +2658,7 @@ impl Target {
// Additionally look in the sysroot under `lib/rustlib/<triple>/target.json` // Additionally look in the sysroot under `lib/rustlib/<triple>/target.json`
// as a fallback. // as a fallback.
let rustlib_path = crate::target_rustlib_path(&sysroot, &target_triple); let rustlib_path = crate::target_rustlib_path(sysroot, target_triple);
let p = PathBuf::from_iter([ let p = PathBuf::from_iter([
Path::new(sysroot), Path::new(sysroot),
Path::new(&rustlib_path), Path::new(&rustlib_path),

View file

@ -71,7 +71,7 @@ pub trait InferCtxtExt<'tcx> {
/// returns a span and `ArgKind` information that describes the /// returns a span and `ArgKind` information that describes the
/// arguments it expects. This can be supplied to /// arguments it expects. This can be supplied to
/// `report_arg_count_mismatch`. /// `report_arg_count_mismatch`.
fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec<ArgKind>)>; fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)>;
/// Reports an error when the number of arguments needed by a /// Reports an error when the number of arguments needed by a
/// trait match doesn't match the number that the expression /// trait match doesn't match the number that the expression
@ -83,6 +83,7 @@ pub trait InferCtxtExt<'tcx> {
expected_args: Vec<ArgKind>, expected_args: Vec<ArgKind>,
found_args: Vec<ArgKind>, found_args: Vec<ArgKind>,
is_closure: bool, is_closure: bool,
closure_pipe_span: Option<Span>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
@ -135,15 +136,16 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
/// returns a span and `ArgKind` information that describes the /// returns a span and `ArgKind` information that describes the
/// arguments it expects. This can be supplied to /// arguments it expects. This can be supplied to
/// `report_arg_count_mismatch`. /// `report_arg_count_mismatch`.
fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec<ArgKind>)> { fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
let sm = self.tcx.sess.source_map(); let sm = self.tcx.sess.source_map();
let hir = self.tcx.hir(); let hir = self.tcx.hir();
Some(match node { Some(match node {
Node::Expr(&hir::Expr { Node::Expr(&hir::Expr {
kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }), kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
.. ..
}) => ( }) => (
fn_decl_span, fn_decl_span,
fn_arg_span,
hir.body(body) hir.body(body)
.params .params
.iter() .iter()
@ -174,6 +176,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
kind: hir::TraitItemKind::Fn(ref sig, _), .. kind: hir::TraitItemKind::Fn(ref sig, _), ..
}) => ( }) => (
sig.span, sig.span,
None,
sig.decl sig.decl
.inputs .inputs
.iter() .iter()
@ -188,7 +191,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
), ),
Node::Ctor(ref variant_data) => { Node::Ctor(ref variant_data) => {
let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
(span, vec![ArgKind::empty(); variant_data.fields().len()]) (span, None, vec![ArgKind::empty(); variant_data.fields().len()])
} }
_ => panic!("non-FnLike node found: {:?}", node), _ => panic!("non-FnLike node found: {:?}", node),
}) })
@ -204,6 +207,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
expected_args: Vec<ArgKind>, expected_args: Vec<ArgKind>,
found_args: Vec<ArgKind>, found_args: Vec<ArgKind>,
is_closure: bool, is_closure: bool,
closure_arg_span: Option<Span>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let kind = if is_closure { "closure" } else { "function" }; let kind = if is_closure { "closure" } else { "function" };
@ -241,24 +245,13 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
if let Some(found_span) = found_span { if let Some(found_span) = found_span {
err.span_label(found_span, format!("takes {}", found_str)); err.span_label(found_span, format!("takes {}", found_str));
// move |_| { ... }
// ^^^^^^^^-- def_span
//
// move |_| { ... }
// ^^^^^-- prefix
let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span);
// move |_| { ... }
// ^^^-- pipe_span
let pipe_span =
if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span };
// Suggest to take and ignore the arguments with expected_args_length `_`s if // Suggest to take and ignore the arguments with expected_args_length `_`s if
// found arguments is empty (assume the user just wants to ignore args in this case). // found arguments is empty (assume the user just wants to ignore args in this case).
// For example, if `expected_args_length` is 2, suggest `|_, _|`. // For example, if `expected_args_length` is 2, suggest `|_, _|`.
if found_args.is_empty() && is_closure { if found_args.is_empty() && is_closure {
let underscores = vec!["_"; expected_args.len()].join(", "); let underscores = vec!["_"; expected_args.len()].join(", ");
err.span_suggestion_verbose( err.span_suggestion_verbose(
pipe_span, closure_arg_span.unwrap_or(found_span),
&format!( &format!(
"consider changing the closure to take and ignore the expected argument{}", "consider changing the closure to take and ignore the expected argument{}",
pluralize!(expected_args.len()) pluralize!(expected_args.len())
@ -1252,13 +1245,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
obligation.cause.code(), obligation.cause.code(),
) )
} else { } else {
let (closure_span, found) = found_did let (closure_span, closure_arg_span, found) = found_did
.and_then(|did| { .and_then(|did| {
let node = self.tcx.hir().get_if_local(did)?; let node = self.tcx.hir().get_if_local(did)?;
let (found_span, found) = self.get_fn_like_arguments(node)?; let (found_span, closure_arg_span, found) =
Some((Some(found_span), found)) self.get_fn_like_arguments(node)?;
Some((Some(found_span), closure_arg_span, found))
}) })
.unwrap_or((found_span, found)); .unwrap_or((found_span, None, found));
self.report_arg_count_mismatch( self.report_arg_count_mismatch(
span, span,
@ -1266,6 +1260,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
expected, expected,
found, found,
found_trait_ty.is_closure(), found_trait_ty.is_closure(),
closure_arg_span,
) )
} }
} }

View file

@ -26,6 +26,10 @@ pub struct TestOpts {
pub test_threads: Option<usize>, pub test_threads: Option<usize>,
pub skip: Vec<String>, pub skip: Vec<String>,
pub time_options: Option<TestTimeOptions>, pub time_options: Option<TestTimeOptions>,
/// Stop at first failing test.
/// May run a few more tests due to threading, but will
/// abort as soon as possible.
pub fail_fast: bool,
pub options: Options, pub options: Options,
} }
@ -296,6 +300,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
skip, skip,
time_options, time_options,
options, options,
fail_fast: false,
}; };
Ok(test_opts) Ok(test_opts)

View file

@ -293,7 +293,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?;
st.exec_time = start_time.map(|t| TestSuiteExecTime(t.elapsed())); st.exec_time = start_time.map(|t| TestSuiteExecTime(t.elapsed()));
assert!(st.current_test_count() == st.total); assert!(opts.fail_fast || st.current_test_count() == st.total);
out.write_run_finish(&st) out.write_run_finish(&st)
} }

View file

@ -384,8 +384,17 @@ where
let mut completed_test = rx.recv().unwrap(); let mut completed_test = rx.recv().unwrap();
RunningTest { join_handle }.join(&mut completed_test); RunningTest { join_handle }.join(&mut completed_test);
let fail_fast = match completed_test.result {
TrIgnored | TrOk | TrBench(_) => false,
TrFailed | TrFailedMsg(_) | TrTimedFail => opts.fail_fast,
};
let event = TestEvent::TeResult(completed_test); let event = TestEvent::TeResult(completed_test);
notify_about_test_event(event)?; notify_about_test_event(event)?;
if fail_fast {
return Ok(());
}
} }
} else { } else {
while pending > 0 || !remaining.is_empty() { while pending > 0 || !remaining.is_empty() {
@ -431,9 +440,20 @@ where
let running_test = running_tests.remove(&completed_test.id).unwrap(); let running_test = running_tests.remove(&completed_test.id).unwrap();
running_test.join(&mut completed_test); running_test.join(&mut completed_test);
let fail_fast = match completed_test.result {
TrIgnored | TrOk | TrBench(_) => false,
TrFailed | TrFailedMsg(_) | TrTimedFail => opts.fail_fast,
};
let event = TestEvent::TeResult(completed_test); let event = TestEvent::TeResult(completed_test);
notify_about_test_event(event)?; notify_about_test_event(event)?;
pending -= 1; pending -= 1;
if fail_fast {
// Prevent remaining test threads from panicking
std::mem::forget(rx);
return Ok(());
}
} }
} }

View file

@ -51,6 +51,7 @@ impl TestOpts {
skip: vec![], skip: vec![],
time_options: None, time_options: None,
options: Options::new(), options: Options::new(),
fail_fast: false,
} }
} }
} }

View file

@ -0,0 +1,19 @@
// Checks that naked functions are not instrumented by -Cinstrument-coverage.
// Regression test for issue #105170.
//
// needs-asm-support
// needs-profiler-support
// compile-flags: -Cinstrument-coverage
#![crate_type = "lib"]
#![feature(naked_functions)]
use std::arch::asm;
#[naked]
#[no_mangle]
pub unsafe extern "C" fn f() {
// CHECK: define void @f()
// CHECK-NEXT: start:
// CHECK-NEXT: call void asm
// CHECK-NEXT: unreachable
asm!("", options(noreturn));
}

View file

@ -126,6 +126,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
fn_decl: decl.clone(), fn_decl: decl.clone(),
body: e, body: e,
fn_decl_span: DUMMY_SP, fn_decl_span: DUMMY_SP,
fn_arg_span: DUMMY_SP,
}))) })))
}); });
} }

View file

@ -0,0 +1,6 @@
#[w = { extern crate alloc; }]
//~^ ERROR unexpected expression: `{
//~| ERROR cannot find attribute `w` in this scope
fn f() {}
fn main() {}

View file

@ -0,0 +1,16 @@
error: unexpected expression: `{
extern crate alloc;
}`
--> $DIR/unused-item-in-attr.rs:1:7
|
LL | #[w = { extern crate alloc; }]
| ^^^^^^^^^^^^^^^^^^^^^^^
error: cannot find attribute `w` in this scope
--> $DIR/unused-item-in-attr.rs:1:3
|
LL | #[w = { extern crate alloc; }]
| ^
error: aborting due to 2 previous errors

View file

@ -1,5 +1,9 @@
// revisions: imported unimported
//[imported] check-pass
mod private { mod private {
pub trait Future { pub trait Future {
//[unimported]~^^ HELP perhaps add a `use` for it
fn wait(&self) where Self: Sized; fn wait(&self) where Self: Sized;
} }
@ -8,13 +12,13 @@ mod private {
} }
} }
//use private::Future; #[cfg(imported)]
use private::Future;
fn bar(arg: Box<dyn private::Future>) { fn bar(arg: Box<dyn private::Future>) {
// Importing the trait means that we don't autoderef `Box<dyn Future>`
arg.wait(); arg.wait();
//~^ ERROR the `wait` method cannot be invoked on a trait object //[unimported]~^ ERROR the `wait` method cannot be invoked on a trait object
} }
fn main() { fn main() {}
}

View file

@ -1,11 +1,16 @@
error: the `wait` method cannot be invoked on a trait object error: the `wait` method cannot be invoked on a trait object
--> $DIR/issue-35976.rs:14:9 --> $DIR/issue-35976.rs:20:9
| |
LL | fn wait(&self) where Self: Sized; LL | fn wait(&self) where Self: Sized;
| ----- this has a `Sized` requirement | ----- this has a `Sized` requirement
... ...
LL | arg.wait(); LL | arg.wait();
| ^^^^ | ^^^^
|
help: another candidate was found in the following trait, perhaps add a `use` for it:
|
LL | use private::Future;
|
error: aborting due to previous error error: aborting due to previous error

View file

@ -0,0 +1,11 @@
use self::A::*;
use V; //~ ERROR `V` is ambiguous
use self::B::*;
enum A {
V
}
enum B {
V
}
fn main() {}

View file

@ -0,0 +1,21 @@
error[E0659]: `V` is ambiguous
--> $DIR/issue-105069.rs:2:5
|
LL | use V;
| ^ ambiguous name
|
= note: ambiguous because of multiple potential import sources
note: `V` could refer to the variant imported here
--> $DIR/issue-105069.rs:1:5
|
LL | use self::A::*;
| ^^^^^^^^^^
note: `V` could also refer to the variant imported here
--> $DIR/issue-105069.rs:3:5
|
LL | use self::B::*;
| ^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0659`.

View file

@ -0,0 +1,18 @@
unsafe fn pointer(v: usize, w: u32) {}
pub trait UniformScalar {}
impl UniformScalar for u32 {}
pub trait GlUniformScalar: UniformScalar {
const FACTORY: unsafe fn(usize, Self) -> ();
}
impl GlUniformScalar for u32 {
const FACTORY: unsafe fn(usize, Self) -> () = pointer;
}
pub fn foo<T: UniformScalar>(value: T) {
<T as GlUniformScalar>::FACTORY(1, value);
//~^ ERROR the trait bound `T: GlUniformScalar` is not satisfied
}
fn main() {}

View file

@ -0,0 +1,14 @@
error[E0277]: the trait bound `T: GlUniformScalar` is not satisfied
--> $DIR/assoc-const-as-fn.rs:14:5
|
LL | <T as GlUniformScalar>::FACTORY(1, value);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `GlUniformScalar` is not implemented for `T`
|
help: consider further restricting this bound
|
LL | pub fn foo<T: UniformScalar + GlUniformScalar>(value: T) {
| +++++++++++++++++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -514,6 +514,7 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
options: test::Options::new(), options: test::Options::new(),
time_options: None, time_options: None,
force_run_in_process: false, force_run_in_process: false,
fail_fast: std::env::var_os("RUSTC_TEST_FAIL_FAST").is_some(),
} }
} }

View file

@ -203,65 +203,32 @@ for more information about configuring VS Code and `rust-analyzer`.
[rdg-r-a]: https://rustc-dev-guide.rust-lang.org/building/suggested.html#configuring-rust-analyzer-for-rustc [rdg-r-a]: https://rustc-dev-guide.rust-lang.org/building/suggested.html#configuring-rust-analyzer-for-rustc
## Advanced topic: other build environments ## Advanced topic: Working on Miri in the rustc tree
We described above the simplest way to get a working build environment for Miri, We described above the simplest way to get a working build environment for Miri,
which is to use the version of rustc indicated by `rustc-version`. But which is to use the version of rustc indicated by `rustc-version`. But
sometimes, that is not enough. sometimes, that is not enough.
### Building Miri with a locally built rustc A big part of the Miri driver is shared with rustc, so working on Miri will
sometimes require also working on rustc itself. In this case, you should *not*
work in a clone of the Miri repository, but in a clone of the
[main Rust repository](https://github.com/rust-lang/rust/). There is a copy of
Miri located at `src/tools/miri` that you can work on directly. A maintainer
will eventually sync those changes back into this repository.
[building Miri with a locally built rustc]: #building-miri-with-a-locally-built-rustc When working on Miri in the rustc tree, here's how you can run tests:
A big part of the Miri driver lives in rustc, so working on Miri will sometimes ```
require using a locally built rustc. The bug you want to fix may actually be on ./x.py test miri --stage 0
the rustc side, or you just need to get more detailed trace of the execution
than what is possible with release builds -- in both cases, you should develop
Miri against a rustc you compiled yourself, with debug assertions (and hence
tracing) enabled.
The setup for a local rustc works as follows:
```sh
# Clone the rust-lang/rust repo.
git clone https://github.com/rust-lang/rust rustc
cd rustc
# Create a config.toml with defaults for working on Miri.
./x.py setup compiler
# Now edit `config.toml` and under `[rust]` set `debug-assertions = true`.
# Build a stage 2 rustc, and build the rustc libraries with that rustc.
# This step can take 30 minutes or more.
./x.py build --stage 2 compiler/rustc
# If you change something, you can get a faster rebuild by doing
./x.py build --keep-stage 0 --stage 2 compiler/rustc
# You may have to change the architecture in the next command
rustup toolchain link stage2 build/x86_64-unknown-linux-gnu/stage2
# Now cd to your Miri directory, then configure rustup
rustup override set stage2
``` ```
Note: When you are working with a locally built rustc or any other toolchain that `--bless` will work, too.
is not the same as the one in `rust-version`, you should not have `.auto-everything` or
`.auto-toolchain` as that will keep resetting your toolchain. You can also directly run Miri on a Rust source file:
```sh
rm -f .auto-everything .auto-toolchain
``` ```
./x.py run miri --stage 0 --args src/tools/miri/tests/pass/hello.rs
Important: You need to delete the Miri cache when you change the stdlib; otherwise the ```
old, chached version will be used. On Linux, the cache is located at `~/.cache/miri`,
and on Windows, it is located at `%LOCALAPPDATA%\rust-lang\miri\cache`; the exact
location is printed after the library build: "A libstd for Miri is now available in ...".
Note: `./x.py --stage 2 compiler/rustc` currently errors with `thread 'main'
panicked at 'fs::read(stamp) failed with No such file or directory (os error 2)`,
you can simply ignore that error; Miri will build anyway.
For more information about building and configuring a local compiler,
see <https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html>.
With this, you should now have a working development setup! See
[above](#building-and-testing-miri) for how to proceed working on Miri.
## Advanced topic: Syncing with the rustc repo ## Advanced topic: Syncing with the rustc repo

View file

@ -724,9 +724,9 @@ dependencies = [
[[package]] [[package]]
name = "ui_test" name = "ui_test"
version = "0.4.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4559da3fe6b481f8674a29379677cb9606cd6f75fc254a2c9834c55638503d" checksum = "54ddb6f31025943e2f9d59237f433711c461a43d9415974c3eb3a4902edc1c1f"
dependencies = [ dependencies = [
"bstr", "bstr",
"cargo_metadata", "cargo_metadata",

View file

@ -39,7 +39,7 @@ libloading = "0.7"
[dev-dependencies] [dev-dependencies]
colored = "2" colored = "2"
ui_test = "0.4" ui_test = "0.5"
rustc_version = "0.4" rustc_version = "0.4"
# Features chosen to match those required by env_logger, to avoid rebuilds # Features chosen to match those required by env_logger, to avoid rebuilds
regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] } regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] }

View file

@ -94,7 +94,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
let target = target.as_ref().unwrap_or(host); let target = target.as_ref().unwrap_or(host);
// We always setup. // We always setup.
setup(&subcommand, target, &rustc_version); setup(&subcommand, target, &rustc_version, verbose);
// Invoke actual cargo for the job, but with different flags. // Invoke actual cargo for the job, but with different flags.
// We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but // We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
@ -486,8 +486,7 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
continue; continue;
} else if verbose > 0 { } else if verbose > 0 {
eprintln!( eprintln!(
"[cargo-miri runner] Overwriting run-time env var {:?}={:?} with build-time value {:?}", "[cargo-miri runner] Overwriting run-time env var {name:?}={old_val:?} with build-time value {val:?}"
name, old_val, val
); );
} }
} }

View file

@ -13,7 +13,7 @@ use crate::util::*;
/// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets /// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets
/// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has /// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has
/// done all this already. /// done all this already.
pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta) { pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta, verbose: usize) {
let only_setup = matches!(subcommand, MiriCommand::Setup); let only_setup = matches!(subcommand, MiriCommand::Setup);
let ask_user = !only_setup; let ask_user = !only_setup;
let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
@ -99,12 +99,13 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
// `config.toml`. // `config.toml`.
command.env("RUSTC_WRAPPER", ""); command.env("RUSTC_WRAPPER", "");
if only_setup { if only_setup && !print_sysroot {
if print_sysroot { // Forward output. Even make it verbose, if requested.
// Be extra sure there is no noise on stdout. for _ in 0..verbose {
command.stdout(process::Stdio::null()); command.arg("-v");
} }
} else { } else {
// Supress output.
command.stdout(process::Stdio::null()); command.stdout(process::Stdio::null());
command.stderr(process::Stdio::null()); command.stderr(process::Stdio::null());
} }
@ -120,7 +121,9 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
std::env::set_var("MIRI_SYSROOT", &sysroot_dir); std::env::set_var("MIRI_SYSROOT", &sysroot_dir);
// Do the build. // Do the build.
if only_setup { if print_sysroot {
// Be silent.
} else if only_setup {
// We want to be explicit. // We want to be explicit.
eprintln!("Preparing a sysroot for Miri (target: {target})..."); eprintln!("Preparing a sysroot for Miri (target: {target})...");
} else { } else {
@ -143,7 +146,9 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
) )
} }
}); });
if only_setup { if print_sysroot {
// Be silent.
} else if only_setup {
eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display()); eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display());
} else { } else {
eprintln!("done"); eprintln!("done");

View file

@ -40,10 +40,15 @@ function run_tests {
./miri test ./miri test
if [ -z "${MIRI_TEST_TARGET+exists}" ]; then if [ -z "${MIRI_TEST_TARGET+exists}" ]; then
# Only for host architecture: tests with optimizations (`-O` is what cargo passes, but crank MIR # Only for host architecture: tests with optimizations (`-O` is what cargo passes, but crank MIR
# optimizations up all the way). # optimizations up all the way, too).
# Optimizations change diagnostics (mostly backtraces), so we don't check them # Optimizations change diagnostics (mostly backtraces), so we don't check
#FIXME(#2155): we want to only run the pass and panic tests here, not the fail tests. # them. Also error locations change so we don't run the failing tests.
MIRIFLAGS="${MIRIFLAGS:-} -O -Zmir-opt-level=4" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic} MIRIFLAGS="${MIRIFLAGS:-} -O -Zmir-opt-level=4" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic}
# Also run some many-seeds tests. 64 seeds means this takes around a minute per test.
for FILE in tests/many-seeds/*.rs; do
MIRI_SEEDS=64 CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS -q" ./miri many-seeds ./miri run "$FILE"
done
fi fi
## test-cargo-miri ## test-cargo-miri

View file

@ -36,7 +36,8 @@ Mainly meant to be invoked by rust-analyzer.
./miri many-seeds <command>: ./miri many-seeds <command>:
Runs <command> over and over again with different seeds for Miri. The MIRIFLAGS Runs <command> over and over again with different seeds for Miri. The MIRIFLAGS
variable is set to its original value appended with ` -Zmiri-seed=$SEED` for variable is set to its original value appended with ` -Zmiri-seed=$SEED` for
many different seeds. many different seeds. The MIRI_SEEDS variable controls how many seeds are being
tried; MIRI_SEED_START controls the first seed to try.
./miri bench <benches>: ./miri bench <benches>:
Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
@ -174,7 +175,9 @@ rustc-push)
fi fi
;; ;;
many-seeds) many-seeds)
for SEED in $(seq 0 255); do MIRI_SEED_START=${MIRI_SEED_START:-0} # default to 0
MIRI_SEEDS=${MIRI_SEEDS:-256} # default to 256
for SEED in $(seq $MIRI_SEED_START $(( $MIRI_SEED_START + $MIRI_SEEDS - 1 )) ); do
echo "Trying seed: $SEED" echo "Trying seed: $SEED"
MIRIFLAGS="$MIRIFLAGS -Zlayout-seed=$SEED -Zmiri-seed=$SEED" $@ || { echo "Failing seed: $SEED"; break; } MIRIFLAGS="$MIRIFLAGS -Zlayout-seed=$SEED -Zmiri-seed=$SEED" $@ || { echo "Failing seed: $SEED"; break; }
done done
@ -249,6 +252,8 @@ export RUSTFLAGS="-C link-args=-Wl,-rpath,$LIBDIR $RUSTFLAGS"
# Build a sysroot and set MIRI_SYSROOT to use it. Arguments are passed to `cargo miri setup`. # Build a sysroot and set MIRI_SYSROOT to use it. Arguments are passed to `cargo miri setup`.
build_sysroot() { build_sysroot() {
if ! MIRI_SYSROOT="$($CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup --print-sysroot "$@")"; then if ! MIRI_SYSROOT="$($CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup --print-sysroot "$@")"; then
# Run it again so the user can see the error.
$CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup "$@"
echo "'cargo miri setup' failed" echo "'cargo miri setup' failed"
exit 1 exit 1
fi fi

View file

@ -1 +1 @@
454784afba5bf35b5ff14ada0e31265ad1d75e73 cef44f53034eac46be3a0e3eec7b2b3d4ef5140b

View file

@ -192,10 +192,7 @@ fn init_late_loggers(tcx: TyCtxt<'_>) {
if log::Level::from_str(&var).is_ok() { if log::Level::from_str(&var).is_ok() {
env::set_var( env::set_var(
"RUSTC_LOG", "RUSTC_LOG",
format!( format!("rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var}"),
"rustc_middle::mir::interpret={0},rustc_const_eval::interpret={0}",
var
),
); );
} else { } else {
env::set_var("RUSTC_LOG", &var); env::set_var("RUSTC_LOG", &var);
@ -317,7 +314,7 @@ fn main() {
} else if arg == "-Zmiri-disable-validation" { } else if arg == "-Zmiri-disable-validation" {
miri_config.validate = false; miri_config.validate = false;
} else if arg == "-Zmiri-disable-stacked-borrows" { } else if arg == "-Zmiri-disable-stacked-borrows" {
miri_config.stacked_borrows = false; miri_config.borrow_tracker = None;
} else if arg == "-Zmiri-disable-data-race-detector" { } else if arg == "-Zmiri-disable-data-race-detector" {
miri_config.data_race_detector = false; miri_config.data_race_detector = false;
miri_config.weak_memory_emulation = false; miri_config.weak_memory_emulation = false;
@ -413,7 +410,7 @@ fn main() {
err err
), ),
}; };
for id in ids.into_iter().map(miri::SbTag::new) { for id in ids.into_iter().map(miri::BorTag::new) {
if let Some(id) = id { if let Some(id) = id {
miri_config.tracked_pointer_tags.insert(id); miri_config.tracked_pointer_tags.insert(id);
} else { } else {

View file

@ -0,0 +1,371 @@
use std::cell::RefCell;
use std::fmt;
use std::num::NonZeroU64;
use log::trace;
use smallvec::SmallVec;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::mir::RetagKind;
use rustc_target::abi::Size;
use crate::*;
pub mod stacked_borrows;
use stacked_borrows::diagnostics::RetagCause;
pub type CallId = NonZeroU64;
/// Tracking pointer provenance
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct BorTag(NonZeroU64);
impl BorTag {
pub fn new(i: u64) -> Option<Self> {
NonZeroU64::new(i).map(BorTag)
}
pub fn get(&self) -> u64 {
self.0.get()
}
pub fn inner(&self) -> NonZeroU64 {
self.0
}
pub fn succ(self) -> Option<Self> {
self.0.checked_add(1).map(Self)
}
/// The minimum representable tag
pub fn one() -> Self {
Self::new(1).unwrap()
}
}
impl std::default::Default for BorTag {
/// The default to be used when borrow tracking is disabled
fn default() -> Self {
Self::one()
}
}
impl fmt::Debug for BorTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<{}>", self.0)
}
}
/// Per-call-stack-frame data for borrow tracking
#[derive(Debug)]
pub struct FrameState {
/// The ID of the call this frame corresponds to.
pub call_id: CallId,
/// If this frame is protecting any tags, they are listed here. We use this list to do
/// incremental updates of the global list of protected tags stored in the
/// `stacked_borrows::GlobalState` upon function return, and if we attempt to pop a protected
/// tag, to identify which call is responsible for protecting the tag.
/// See `Stack::item_popped` for more explanation.
///
/// This will contain one tag per reference passed to the function, so
/// a size of 2 is enough for the vast majority of functions.
pub protected_tags: SmallVec<[BorTag; 2]>,
}
impl VisitTags for FrameState {
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// `protected_tags` are fine to GC.
}
}
/// Extra global state, available to the memory access hooks.
#[derive(Debug)]
pub struct GlobalStateInner {
/// Borrow tracker method currently in use.
pub borrow_tracker_method: BorrowTrackerMethod,
/// Next unused pointer ID (tag).
pub next_ptr_tag: BorTag,
/// Table storing the "base" tag for each allocation.
/// The base tag is the one used for the initial pointer.
/// We need this in a separate table to handle cyclic statics.
pub base_ptr_tags: FxHashMap<AllocId, BorTag>,
/// Next unused call ID (for protectors).
pub next_call_id: CallId,
/// All currently protected tags.
/// An item is protected if its tag is in this set, *and* it has the "protected" bit set.
/// We add tags to this when they are created with a protector in `reborrow`, and
/// we remove tags from this when the call which is protecting them returns, in
/// `GlobalStateInner::end_call`. See `Stack::item_popped` for more details.
pub protected_tags: FxHashMap<BorTag, ProtectorKind>,
/// The pointer ids to trace
pub tracked_pointer_tags: FxHashSet<BorTag>,
/// The call ids to trace
pub tracked_call_ids: FxHashSet<CallId>,
/// Whether to recurse into datatypes when searching for pointers to retag.
pub retag_fields: RetagFields,
}
impl VisitTags for GlobalStateInner {
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// The only candidate is base_ptr_tags, and that does not need visiting since we don't ever
// GC the bottommost tag.
}
}
/// We need interior mutable access to the global state.
pub type GlobalState = RefCell<GlobalStateInner>;
/// Indicates which kind of access is being performed.
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
pub enum AccessKind {
Read,
Write,
}
impl fmt::Display for AccessKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AccessKind::Read => write!(f, "read access"),
AccessKind::Write => write!(f, "write access"),
}
}
}
/// Policy on whether to recurse into fields to retag
#[derive(Copy, Clone, Debug)]
pub enum RetagFields {
/// Don't retag any fields.
No,
/// Retag all fields.
Yes,
/// Only retag fields of types with Scalar and ScalarPair layout,
/// to match the LLVM `noalias` we generate.
OnlyScalar,
}
/// The flavor of the protector.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ProtectorKind {
/// Protected against aliasing violations from other pointers.
///
/// Items protected like this cause UB when they are invalidated, *but* the pointer itself may
/// still be used to issue a deallocation.
///
/// This is required for LLVM IR pointers that are `noalias` but *not* `dereferenceable`.
WeakProtector,
/// Protected against any kind of invalidation.
///
/// Items protected like this cause UB when they are invalidated or the memory is deallocated.
/// This is strictly stronger protection than `WeakProtector`.
///
/// This is required for LLVM IR pointers that are `dereferenceable` (and also allows `noalias`).
StrongProtector,
}
/// Utilities for initialization and ID generation
impl GlobalStateInner {
pub fn new(
borrow_tracker_method: BorrowTrackerMethod,
tracked_pointer_tags: FxHashSet<BorTag>,
tracked_call_ids: FxHashSet<CallId>,
retag_fields: RetagFields,
) -> Self {
GlobalStateInner {
borrow_tracker_method,
next_ptr_tag: BorTag::one(),
base_ptr_tags: FxHashMap::default(),
next_call_id: NonZeroU64::new(1).unwrap(),
protected_tags: FxHashMap::default(),
tracked_pointer_tags,
tracked_call_ids,
retag_fields,
}
}
/// Generates a new pointer tag. Remember to also check track_pointer_tags and log its creation!
pub fn new_ptr(&mut self) -> BorTag {
let id = self.next_ptr_tag;
self.next_ptr_tag = id.succ().unwrap();
id
}
pub fn new_frame(&mut self, machine: &MiriMachine<'_, '_>) -> FrameState {
let call_id = self.next_call_id;
trace!("new_frame: Assigning call ID {}", call_id);
if self.tracked_call_ids.contains(&call_id) {
machine.emit_diagnostic(NonHaltingDiagnostic::CreatedCallId(call_id));
}
self.next_call_id = NonZeroU64::new(call_id.get() + 1).unwrap();
FrameState { call_id, protected_tags: SmallVec::new() }
}
pub fn end_call(&mut self, frame: &machine::FrameExtra<'_>) {
for tag in &frame
.borrow_tracker
.as_ref()
.expect("we should have borrow tracking data")
.protected_tags
{
self.protected_tags.remove(tag);
}
}
pub fn base_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> BorTag {
self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| {
let tag = self.new_ptr();
if self.tracked_pointer_tags.contains(&tag) {
machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
tag.inner(),
None,
None,
));
}
trace!("New allocation {:?} has base tag {:?}", id, tag);
self.base_ptr_tags.try_insert(id, tag).unwrap();
tag
})
}
}
/// Which borrow tracking method to use
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BorrowTrackerMethod {
/// Stacked Borrows, as implemented in borrow_tracker/stacked
StackedBorrows,
}
impl BorrowTrackerMethod {
pub fn instanciate_global_state(self, config: &MiriConfig) -> GlobalState {
RefCell::new(GlobalStateInner::new(
self,
config.tracked_pointer_tags.clone(),
config.tracked_call_ids.clone(),
config.retag_fields,
))
}
}
impl GlobalStateInner {
pub fn new_allocation(
&mut self,
id: AllocId,
alloc_size: Size,
kind: MemoryKind<machine::MiriMemoryKind>,
machine: &MiriMachine<'_, '_>,
) -> AllocState {
match self.borrow_tracker_method {
BorrowTrackerMethod::StackedBorrows =>
AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
id, alloc_size, self, kind, machine,
)))),
}
}
}
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn retag(&mut self, kind: RetagKind, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
match method {
BorrowTrackerMethod::StackedBorrows => this.sb_retag(kind, place),
}
}
fn retag_return_place(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
match method {
BorrowTrackerMethod::StackedBorrows => this.sb_retag_return_place(),
}
}
fn expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
match method {
BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag),
}
}
}
/// Extra per-allocation data for borrow tracking
#[derive(Debug, Clone)]
pub enum AllocState {
/// Data corresponding to Stacked Borrows
StackedBorrows(Box<RefCell<stacked_borrows::AllocState>>),
}
impl machine::AllocExtra {
#[track_caller]
pub fn borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState> {
match self.borrow_tracker {
Some(AllocState::StackedBorrows(ref sb)) => sb,
_ => panic!("expected Stacked Borrows borrow tracking, got something else"),
}
}
#[track_caller]
pub fn borrow_tracker_sb_mut(&mut self) -> &mut RefCell<stacked_borrows::AllocState> {
match self.borrow_tracker {
Some(AllocState::StackedBorrows(ref mut sb)) => sb,
_ => panic!("expected Stacked Borrows borrow tracking, got something else"),
}
}
}
impl AllocState {
pub fn before_memory_read<'tcx>(
&self,
alloc_id: AllocId,
prov_extra: ProvenanceExtra,
range: AllocRange,
machine: &MiriMachine<'_, 'tcx>,
) -> InterpResult<'tcx> {
match self {
AllocState::StackedBorrows(sb) =>
sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
}
}
pub fn before_memory_write<'tcx>(
&mut self,
alloc_id: AllocId,
prov_extra: ProvenanceExtra,
range: AllocRange,
machine: &mut MiriMachine<'_, 'tcx>,
) -> InterpResult<'tcx> {
match self {
AllocState::StackedBorrows(sb) =>
sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
}
}
pub fn before_memory_deallocation<'tcx>(
&mut self,
alloc_id: AllocId,
prov_extra: ProvenanceExtra,
range: AllocRange,
machine: &mut MiriMachine<'_, 'tcx>,
) -> InterpResult<'tcx> {
match self {
AllocState::StackedBorrows(sb) =>
sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, range, machine),
}
}
pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
match self {
AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
}
}
}
impl VisitTags for AllocState {
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
match self {
AllocState::StackedBorrows(sb) => sb.visit_tags(visit),
}
}
}

View file

@ -1,15 +1,16 @@
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt; use std::fmt;
use rustc_middle::mir::interpret::{alloc_range, AllocId, AllocRange}; use rustc_middle::mir::interpret::{alloc_range, AllocId, AllocRange, InterpError};
use rustc_span::{Span, SpanData}; use rustc_span::{Span, SpanData};
use rustc_target::abi::Size; use rustc_target::abi::Size;
use crate::stacked_borrows::{err_sb_ub, AccessKind, GlobalStateInner, Permission, ProtectorKind}; use crate::borrow_tracker::{
stacked_borrows::{err_sb_ub, Permission},
AccessKind, GlobalStateInner, ProtectorKind,
};
use crate::*; use crate::*;
use rustc_middle::mir::interpret::InterpError;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AllocHistory { pub struct AllocHistory {
id: AllocId, id: AllocId,
@ -51,7 +52,7 @@ impl Creation {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Invalidation { struct Invalidation {
tag: SbTag, tag: BorTag,
range: AllocRange, range: AllocRange,
span: Span, span: Span,
cause: InvalidationCause, cause: InvalidationCause,
@ -98,7 +99,7 @@ impl fmt::Display for InvalidationCause {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Protection { struct Protection {
tag: SbTag, tag: BorTag,
span: Span, span: Span,
} }
@ -133,7 +134,7 @@ impl<'ecx, 'mir, 'tcx> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> {
pub fn retag( pub fn retag(
machine: &'ecx MiriMachine<'mir, 'tcx>, machine: &'ecx MiriMachine<'mir, 'tcx>,
cause: RetagCause, cause: RetagCause,
new_tag: SbTag, new_tag: BorTag,
orig_tag: ProvenanceExtra, orig_tag: ProvenanceExtra,
range: AllocRange, range: AllocRange,
) -> Self { ) -> Self {
@ -183,7 +184,7 @@ enum Operation {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct RetagOp { struct RetagOp {
cause: RetagCause, cause: RetagCause,
new_tag: SbTag, new_tag: BorTag,
orig_tag: ProvenanceExtra, orig_tag: ProvenanceExtra,
range: AllocRange, range: AllocRange,
permission: Option<Permission>, permission: Option<Permission>,
@ -255,7 +256,7 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
.push(Creation { retag: op.clone(), span: self.machine.current_span() }); .push(Creation { retag: op.clone(), span: self.machine.current_span() });
} }
pub fn log_invalidation(&mut self, tag: SbTag) { pub fn log_invalidation(&mut self, tag: BorTag) {
let mut span = self.machine.current_span(); let mut span = self.machine.current_span();
let (range, cause) = match &self.operation { let (range, cause) = match &self.operation {
Operation::Retag(RetagOp { cause, range, permission, .. }) => { Operation::Retag(RetagOp { cause, range, permission, .. }) => {
@ -286,8 +287,8 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
pub fn get_logs_relevant_to( pub fn get_logs_relevant_to(
&self, &self,
tag: SbTag, tag: BorTag,
protector_tag: Option<SbTag>, protector_tag: Option<BorTag>,
) -> Option<TagHistory> { ) -> Option<TagHistory> {
let Some(created) = self.history let Some(created) = self.history
.creations .creations
@ -408,7 +409,7 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
.all_stacks() .all_stacks()
.flatten() .flatten()
.map(|frame| { .map(|frame| {
frame.extra.stacked_borrows.as_ref().expect("we should have Stacked Borrows data") frame.extra.borrow_tracker.as_ref().expect("we should have borrow tracking data")
}) })
.find(|frame| frame.protected_tags.contains(&item.tag())) .find(|frame| frame.protected_tags.contains(&item.tag()))
.map(|frame| frame.call_id) .map(|frame| frame.call_id)
@ -454,23 +455,18 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
if !global.tracked_pointer_tags.contains(&item.tag()) { if !global.tracked_pointer_tags.contains(&item.tag()) {
return; return;
} }
let summary = match self.operation { let cause = match self.operation {
Operation::Dealloc(_) => None, Operation::Dealloc(_) => format!(" due to deallocation"),
Operation::Access(AccessOp { kind, tag, .. }) => Some((tag, kind)), Operation::Access(AccessOp { kind, tag, .. }) =>
format!(" due to {kind:?} access for {tag:?}"),
Operation::Retag(RetagOp { orig_tag, permission, .. }) => { Operation::Retag(RetagOp { orig_tag, permission, .. }) => {
let kind = match permission let permission = permission
.expect("start_grant should set the current permission before popping a tag") .expect("start_grant should set the current permission before popping a tag");
{ format!(" due to {permission:?} retag from {orig_tag:?}")
Permission::SharedReadOnly => AccessKind::Read,
Permission::Unique => AccessKind::Write,
Permission::SharedReadWrite | Permission::Disabled => {
panic!("Only SharedReadOnly and Unique retags can pop tags");
}
};
Some((orig_tag, kind))
} }
}; };
self.machine.emit_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(*item, summary));
self.machine.emit_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(*item, cause));
} }
} }

View file

@ -1,13 +1,13 @@
use crate::stacked_borrows::SbTag;
use std::fmt; use std::fmt;
use std::num::NonZeroU64;
use crate::borrow_tracker::BorTag;
/// An item in the per-location borrow stack. /// An item in the per-location borrow stack.
#[derive(Copy, Clone, Hash, PartialEq, Eq)] #[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct Item(u64); pub struct Item(u64);
// An Item contains 3 bitfields: // An Item contains 3 bitfields:
// * Bits 0-61 store an SbTag // * Bits 0-61 store a BorTag
// * Bits 61-63 store a Permission // * Bits 61-63 store a Permission
// * Bit 64 stores a flag which indicates if we have a protector // * Bit 64 stores a flag which indicates if we have a protector
const TAG_MASK: u64 = u64::MAX >> 3; const TAG_MASK: u64 = u64::MAX >> 3;
@ -18,9 +18,9 @@ const PERM_SHIFT: u64 = 61;
const PROTECTED_SHIFT: u64 = 63; const PROTECTED_SHIFT: u64 = 63;
impl Item { impl Item {
pub fn new(tag: SbTag, perm: Permission, protected: bool) -> Self { pub fn new(tag: BorTag, perm: Permission, protected: bool) -> Self {
assert!(tag.0.get() <= TAG_MASK); assert!(tag.get() <= TAG_MASK);
let packed_tag = tag.0.get(); let packed_tag = tag.get();
let packed_perm = perm.to_bits() << PERM_SHIFT; let packed_perm = perm.to_bits() << PERM_SHIFT;
let packed_protected = u64::from(protected) << PROTECTED_SHIFT; let packed_protected = u64::from(protected) << PROTECTED_SHIFT;
@ -34,8 +34,8 @@ impl Item {
} }
/// The pointers the permission is granted to. /// The pointers the permission is granted to.
pub fn tag(self) -> SbTag { pub fn tag(self) -> BorTag {
SbTag(NonZeroU64::new(self.0 & TAG_MASK).unwrap()) BorTag::new(self.0 & TAG_MASK).unwrap()
} }
/// The permission this item grants. /// The permission this item grants.

View file

@ -2,81 +2,30 @@
//! for further information. //! for further information.
use log::trace; use log::trace;
use std::cell::RefCell;
use std::cmp; use std::cmp;
use std::fmt; use std::fmt::{self, Write};
use std::fmt::Write;
use std::num::NonZeroU64;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::Mutability; use rustc_middle::mir::{Mutability, RetagKind};
use rustc_middle::mir::RetagKind;
use rustc_middle::ty::{ use rustc_middle::ty::{
self, self,
layout::{HasParamEnv, LayoutOf}, layout::{HasParamEnv, LayoutOf},
}; };
use rustc_target::abi::Abi; use rustc_target::abi::{Abi, Size};
use rustc_target::abi::Size;
use smallvec::SmallVec;
use crate::borrow_tracker::{
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder, TagHistory},
AccessKind, GlobalStateInner, ProtectorKind, RetagCause, RetagFields,
};
use crate::*; use crate::*;
pub mod diagnostics;
use diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder, RetagCause, TagHistory};
mod item; mod item;
pub use item::{Item, Permission}; pub use item::{Item, Permission};
mod stack; mod stack;
pub use stack::Stack; pub use stack::Stack;
pub mod diagnostics;
pub type CallId = NonZeroU64; pub type AllocState = Stacks;
// Even reading memory can have effects on the stack, so we need a `RefCell` here.
pub type AllocExtra = RefCell<Stacks>;
/// Tracking pointer provenance
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct SbTag(NonZeroU64);
impl SbTag {
pub fn new(i: u64) -> Option<Self> {
NonZeroU64::new(i).map(SbTag)
}
// The default to be used when SB is disabled
#[allow(clippy::should_implement_trait)]
pub fn default() -> Self {
Self::new(1).unwrap()
}
}
impl fmt::Debug for SbTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<{}>", self.0)
}
}
#[derive(Debug)]
pub struct FrameExtra {
/// The ID of the call this frame corresponds to.
call_id: CallId,
/// If this frame is protecting any tags, they are listed here. We use this list to do
/// incremental updates of the global list of protected tags stored in the
/// `stacked_borrows::GlobalState` upon function return, and if we attempt to pop a protected
/// tag, to identify which call is responsible for protecting the tag.
/// See `Stack::item_invalidated` for more explanation.
///
/// This will contain one tag per reference passed to the function, so
/// a size of 2 is enough for the vast majority of functions.
protected_tags: SmallVec<[SbTag; 2]>,
}
impl VisitTags for FrameExtra {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
// `protected_tags` are fine to GC.
}
}
/// Extra per-allocation state. /// Extra per-allocation state.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -86,98 +35,16 @@ pub struct Stacks {
/// Stores past operations on this allocation /// Stores past operations on this allocation
history: AllocHistory, history: AllocHistory,
/// The set of tags that have been exposed inside this allocation. /// The set of tags that have been exposed inside this allocation.
exposed_tags: FxHashSet<SbTag>, exposed_tags: FxHashSet<BorTag>,
/// Whether this memory has been modified since the last time the tag GC ran /// Whether this memory has been modified since the last time the tag GC ran
modified_since_last_gc: bool, modified_since_last_gc: bool,
} }
/// The flavor of the protector.
#[derive(Copy, Clone, Debug)]
enum ProtectorKind {
/// Protected against aliasing violations from other pointers.
///
/// Items protected like this cause UB when they are invalidated, *but* the pointer itself may
/// still be used to issue a deallocation.
///
/// This is required for LLVM IR pointers that are `noalias` but *not* `dereferenceable`.
WeakProtector,
/// Protected against any kind of invalidation.
///
/// Items protected like this cause UB when they are invalidated or the memory is deallocated.
/// This is strictly stronger protection than `WeakProtector`.
///
/// This is required for LLVM IR pointers that are `dereferenceable` (and also allows `noalias`).
StrongProtector,
}
/// Extra global state, available to the memory access hooks.
#[derive(Debug)]
pub struct GlobalStateInner {
/// Next unused pointer ID (tag).
next_ptr_tag: SbTag,
/// Table storing the "base" tag for each allocation.
/// The base tag is the one used for the initial pointer.
/// We need this in a separate table to handle cyclic statics.
base_ptr_tags: FxHashMap<AllocId, SbTag>,
/// Next unused call ID (for protectors).
next_call_id: CallId,
/// All currently protected tags, and the status of their protection.
/// An item is protected if its tag is in this set, *and* it has the "protected" bit set.
/// We add tags to this when they are created with a protector in `reborrow`, and
/// we remove tags from this when the call which is protecting them returns, in
/// `GlobalStateInner::end_call`. See `Stack::item_invalidated` for more details.
protected_tags: FxHashMap<SbTag, ProtectorKind>,
/// The pointer ids to trace
tracked_pointer_tags: FxHashSet<SbTag>,
/// The call ids to trace
tracked_call_ids: FxHashSet<CallId>,
/// Whether to recurse into datatypes when searching for pointers to retag.
retag_fields: RetagFields,
}
#[derive(Copy, Clone, Debug)]
pub enum RetagFields {
/// Don't retag any fields.
No,
/// Retag all fields.
Yes,
/// Only retag fields of types with Scalar and ScalarPair layout,
/// to match the LLVM `noalias` we generate.
OnlyScalar,
}
impl VisitTags for GlobalStateInner {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
// The only candidate is base_ptr_tags, and that does not need visiting since we don't ever
// GC the bottommost tag.
}
}
/// We need interior mutable access to the global state.
pub type GlobalState = RefCell<GlobalStateInner>;
/// Indicates which kind of access is being performed.
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
pub enum AccessKind {
Read,
Write,
}
impl fmt::Display for AccessKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AccessKind::Read => write!(f, "read access"),
AccessKind::Write => write!(f, "write access"),
}
}
}
/// Indicates which kind of reference is being created. /// Indicates which kind of reference is being created.
/// Used by high-level `reborrow` to compute which permissions to grant to the /// Used by high-level `reborrow` to compute which permissions to grant to the
/// new pointer. /// new pointer.
#[derive(Copy, Clone, Hash, PartialEq, Eq)] #[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub enum RefKind { enum RefKind {
/// `&mut` and `Box`. /// `&mut` and `Box`.
Unique { two_phase: bool }, Unique { two_phase: bool },
/// `&` with or without interior mutability. /// `&` with or without interior mutability.
@ -198,65 +65,6 @@ impl fmt::Display for RefKind {
} }
} }
/// Utilities for initialization and ID generation
impl GlobalStateInner {
pub fn new(
tracked_pointer_tags: FxHashSet<SbTag>,
tracked_call_ids: FxHashSet<CallId>,
retag_fields: RetagFields,
) -> Self {
GlobalStateInner {
next_ptr_tag: SbTag(NonZeroU64::new(1).unwrap()),
base_ptr_tags: FxHashMap::default(),
next_call_id: NonZeroU64::new(1).unwrap(),
protected_tags: FxHashMap::default(),
tracked_pointer_tags,
tracked_call_ids,
retag_fields,
}
}
/// Generates a new pointer tag. Remember to also check track_pointer_tags and log its creation!
fn new_ptr(&mut self) -> SbTag {
let id = self.next_ptr_tag;
self.next_ptr_tag = SbTag(NonZeroU64::new(id.0.get() + 1).unwrap());
id
}
pub fn new_frame(&mut self, machine: &MiriMachine<'_, '_>) -> FrameExtra {
let call_id = self.next_call_id;
trace!("new_frame: Assigning call ID {}", call_id);
if self.tracked_call_ids.contains(&call_id) {
machine.emit_diagnostic(NonHaltingDiagnostic::CreatedCallId(call_id));
}
self.next_call_id = NonZeroU64::new(call_id.get() + 1).unwrap();
FrameExtra { call_id, protected_tags: SmallVec::new() }
}
pub fn end_call(&mut self, frame: &machine::FrameData<'_>) {
for tag in &frame
.stacked_borrows
.as_ref()
.expect("we should have Stacked Borrows data")
.protected_tags
{
self.protected_tags.remove(tag);
}
}
pub fn base_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> SbTag {
self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| {
let tag = self.new_ptr();
if self.tracked_pointer_tags.contains(&tag) {
machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(tag.0, None, None));
}
trace!("New allocation {:?} has base tag {:?}", id, tag);
self.base_ptr_tags.try_insert(id, tag).unwrap();
tag
})
}
}
/// Error reporting /// Error reporting
pub fn err_sb_ub<'tcx>( pub fn err_sb_ub<'tcx>(
msg: String, msg: String,
@ -329,14 +137,7 @@ impl<'tcx> Stack {
} }
} }
/// Check if the given item is protected. /// The given item was invalidated -- check its protectors for whether that will cause UB.
///
/// The `provoking_access` argument is only used to produce diagnostics.
/// It is `Some` when we are granting the contained access for said tag, and it is
/// `None` during a deallocation.
/// Within `provoking_access, the `AllocRange` refers the entire operation, and
/// the `Size` refers to the specific location in the `AllocRange` that we are
/// currently checking.
fn item_invalidated( fn item_invalidated(
item: &Item, item: &Item,
global: &GlobalStateInner, global: &GlobalStateInner,
@ -386,7 +187,7 @@ impl<'tcx> Stack {
tag: ProvenanceExtra, tag: ProvenanceExtra,
global: &GlobalStateInner, global: &GlobalStateInner,
dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>, dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
exposed_tags: &FxHashSet<SbTag>, exposed_tags: &FxHashSet<BorTag>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
// Two main steps: Find granting item, remove incompatible items above. // Two main steps: Find granting item, remove incompatible items above.
@ -442,23 +243,24 @@ impl<'tcx> Stack {
if granting_idx.is_none() || matches!(tag, ProvenanceExtra::Wildcard) { if granting_idx.is_none() || matches!(tag, ProvenanceExtra::Wildcard) {
// Compute the upper bound of the items that remain. // Compute the upper bound of the items that remain.
// (This is why we did all the work above: to reduce the items we have to consider here.) // (This is why we did all the work above: to reduce the items we have to consider here.)
let mut max = NonZeroU64::new(1).unwrap(); let mut max = BorTag::one();
for i in 0..self.len() { for i in 0..self.len() {
let item = self.get(i).unwrap(); let item = self.get(i).unwrap();
// Skip disabled items, they cannot be matched anyway. // Skip disabled items, they cannot be matched anyway.
if !matches!(item.perm(), Permission::Disabled) { if !matches!(item.perm(), Permission::Disabled) {
// We are looking for a strict upper bound, so add 1 to this tag. // We are looking for a strict upper bound, so add 1 to this tag.
max = cmp::max(item.tag().0.checked_add(1).unwrap(), max); max = cmp::max(item.tag().succ().unwrap(), max);
} }
} }
if let Some(unk) = self.unknown_bottom() { if let Some(unk) = self.unknown_bottom() {
max = cmp::max(unk.0, max); max = cmp::max(unk, max);
} }
// Use `max` as new strict upper bound for everything. // Use `max` as new strict upper bound for everything.
trace!( trace!(
"access: forgetting stack to upper bound {max} due to wildcard or unknown access" "access: forgetting stack to upper bound {max} due to wildcard or unknown access",
max = max.get(),
); );
self.set_unknown_bottom(SbTag(max)); self.set_unknown_bottom(max);
} }
// Done. // Done.
@ -472,7 +274,7 @@ impl<'tcx> Stack {
tag: ProvenanceExtra, tag: ProvenanceExtra,
global: &GlobalStateInner, global: &GlobalStateInner,
dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>, dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
exposed_tags: &FxHashSet<SbTag>, exposed_tags: &FxHashSet<BorTag>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
// Step 1: Make a write access. // Step 1: Make a write access.
// As part of this we do regular protector checking, i.e. even weakly protected items cause UB when popped. // As part of this we do regular protector checking, i.e. even weakly protected items cause UB when popped.
@ -497,7 +299,7 @@ impl<'tcx> Stack {
access: Option<AccessKind>, access: Option<AccessKind>,
global: &GlobalStateInner, global: &GlobalStateInner,
dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>, dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
exposed_tags: &FxHashSet<SbTag>, exposed_tags: &FxHashSet<BorTag>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
dcx.start_grant(new.perm()); dcx.start_grant(new.perm());
@ -550,9 +352,9 @@ impl<'tcx> Stack {
} }
// # Stacked Borrows Core End // # Stacked Borrows Core End
/// Integration with the SbTag garbage collector /// Integration with the BorTag garbage collector
impl Stacks { impl Stacks {
pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<SbTag>) { pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) {
if self.modified_since_last_gc { if self.modified_since_last_gc {
for stack in self.stacks.iter_mut_all() { for stack in self.stacks.iter_mut_all() {
if stack.len() > 64 { if stack.len() > 64 {
@ -565,7 +367,7 @@ impl Stacks {
} }
impl VisitTags for Stacks { impl VisitTags for Stacks {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
for tag in self.exposed_tags.iter().copied() { for tag in self.exposed_tags.iter().copied() {
visit(tag); visit(tag);
} }
@ -579,7 +381,7 @@ impl<'tcx> Stacks {
fn new( fn new(
size: Size, size: Size,
perm: Permission, perm: Permission,
tag: SbTag, tag: BorTag,
id: AllocId, id: AllocId,
machine: &MiriMachine<'_, '_>, machine: &MiriMachine<'_, '_>,
) -> Self { ) -> Self {
@ -602,7 +404,7 @@ impl<'tcx> Stacks {
mut f: impl FnMut( mut f: impl FnMut(
&mut Stack, &mut Stack,
&mut DiagnosticCx<'_, '_, '_, 'tcx>, &mut DiagnosticCx<'_, '_, '_, 'tcx>,
&mut FxHashSet<SbTag>, &mut FxHashSet<BorTag>,
) -> InterpResult<'tcx>, ) -> InterpResult<'tcx>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
self.modified_since_last_gc = true; self.modified_since_last_gc = true;
@ -620,20 +422,19 @@ impl Stacks {
pub fn new_allocation( pub fn new_allocation(
id: AllocId, id: AllocId,
size: Size, size: Size,
state: &GlobalState, state: &mut GlobalStateInner,
kind: MemoryKind<MiriMemoryKind>, kind: MemoryKind<MiriMemoryKind>,
machine: &MiriMachine<'_, '_>, machine: &MiriMachine<'_, '_>,
) -> Self { ) -> Self {
let mut extra = state.borrow_mut();
let (base_tag, perm) = match kind { let (base_tag, perm) = match kind {
// New unique borrow. This tag is not accessible by the program, // New unique borrow. This tag is not accessible by the program,
// so it will only ever be used when using the local directly (i.e., // so it will only ever be used when using the local directly (i.e.,
// not through a pointer). That is, whenever we directly write to a local, this will pop // not through a pointer). That is, whenever we directly write to a local, this will pop
// everything else off the stack, invalidating all previous pointers, // everything else off the stack, invalidating all previous pointers,
// and in particular, *all* raw pointers. // and in particular, *all* raw pointers.
MemoryKind::Stack => (extra.base_ptr_tag(id, machine), Permission::Unique), MemoryKind::Stack => (state.base_ptr_tag(id, machine), Permission::Unique),
// Everything else is shared by default. // Everything else is shared by default.
_ => (extra.base_ptr_tag(id, machine), Permission::SharedReadWrite), _ => (state.base_ptr_tag(id, machine), Permission::SharedReadWrite),
}; };
Stacks::new(size, perm, base_tag, id, machine) Stacks::new(size, perm, base_tag, id, machine)
} }
@ -656,7 +457,7 @@ impl Stacks {
range.size.bytes() range.size.bytes()
); );
let dcx = DiagnosticCxBuilder::read(machine, tag, range); let dcx = DiagnosticCxBuilder::read(machine, tag, range);
let state = machine.stacked_borrows.as_ref().unwrap().borrow(); let state = machine.borrow_tracker.as_ref().unwrap().borrow();
self.for_each(range, dcx, |stack, dcx, exposed_tags| { self.for_each(range, dcx, |stack, dcx, exposed_tags| {
stack.access(AccessKind::Read, tag, &state, dcx, exposed_tags) stack.access(AccessKind::Read, tag, &state, dcx, exposed_tags)
}) })
@ -677,7 +478,7 @@ impl Stacks {
range.size.bytes() range.size.bytes()
); );
let dcx = DiagnosticCxBuilder::write(machine, tag, range); let dcx = DiagnosticCxBuilder::write(machine, tag, range);
let state = machine.stacked_borrows.as_ref().unwrap().borrow(); let state = machine.borrow_tracker.as_ref().unwrap().borrow();
self.for_each(range, dcx, |stack, dcx, exposed_tags| { self.for_each(range, dcx, |stack, dcx, exposed_tags| {
stack.access(AccessKind::Write, tag, &state, dcx, exposed_tags) stack.access(AccessKind::Write, tag, &state, dcx, exposed_tags)
}) })
@ -693,7 +494,7 @@ impl Stacks {
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes()); trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes());
let dcx = DiagnosticCxBuilder::dealloc(machine, tag); let dcx = DiagnosticCxBuilder::dealloc(machine, tag);
let state = machine.stacked_borrows.as_ref().unwrap().borrow(); let state = machine.borrow_tracker.as_ref().unwrap().borrow();
self.for_each(range, dcx, |stack, dcx, exposed_tags| { self.for_each(range, dcx, |stack, dcx, exposed_tags| {
stack.dealloc(tag, &state, dcx, exposed_tags) stack.dealloc(tag, &state, dcx, exposed_tags)
})?; })?;
@ -710,13 +511,13 @@ impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx>
trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> { trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// Returns the `AllocId` the reborrow was done in, if some actual borrow stack manipulation /// Returns the `AllocId` the reborrow was done in, if some actual borrow stack manipulation
/// happened. /// happened.
fn reborrow( fn sb_reborrow(
&mut self, &mut self,
place: &MPlaceTy<'tcx, Provenance>, place: &MPlaceTy<'tcx, Provenance>,
size: Size, size: Size,
kind: RefKind, kind: RefKind,
retag_cause: RetagCause, // What caused this retag, for diagnostics only retag_cause: RetagCause, // What caused this retag, for diagnostics only
new_tag: SbTag, new_tag: BorTag,
protect: Option<ProtectorKind>, protect: Option<ProtectorKind>,
) -> InterpResult<'tcx, Option<AllocId>> { ) -> InterpResult<'tcx, Option<AllocId>> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
@ -725,7 +526,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
let log_creation = |this: &MiriInterpCx<'mir, 'tcx>, let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,
loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag
-> InterpResult<'tcx> { -> InterpResult<'tcx> {
let global = this.machine.stacked_borrows.as_ref().unwrap().borrow(); let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
let ty = place.layout.ty; let ty = place.layout.ty;
if global.tracked_pointer_tags.contains(&new_tag) { if global.tracked_pointer_tags.contains(&new_tag) {
let mut kind_str = format!("{kind}"); let mut kind_str = format!("{kind}");
@ -743,7 +544,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
_ => write!(kind_str, " (pointee type {ty})").unwrap(), _ => write!(kind_str, " (pointee type {ty})").unwrap(),
}; };
this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag( this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
new_tag.0, new_tag.inner(),
Some(kind_str), Some(kind_str),
loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, size), orig_tag)), loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, size), orig_tag)),
)); ));
@ -762,9 +563,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
// uncovers a non-supported `extern static`. // uncovers a non-supported `extern static`.
let extra = this.get_alloc_extra(alloc_id)?; let extra = this.get_alloc_extra(alloc_id)?;
let mut stacked_borrows = extra let mut stacked_borrows = extra
.stacked_borrows .borrow_tracker_sb()
.as_ref()
.expect("we should have Stacked Borrows data")
.borrow_mut(); .borrow_mut();
// Note that we create a *second* `DiagnosticCxBuilder` below for the actual retag. // Note that we create a *second* `DiagnosticCxBuilder` below for the actual retag.
// FIXME: can this be done cleaner? // FIXME: can this be done cleaner?
@ -780,7 +579,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
if protect.is_some() { if protect.is_some() {
dcx.log_protector(); dcx.log_protector();
} }
} },
AllocKind::Function | AllocKind::VTable | AllocKind::Dead => { AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
// No stacked borrows on these allocations. // No stacked borrows on these allocations.
} }
@ -839,9 +638,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
if let Some(protect) = protect { if let Some(protect) = protect {
// See comment in `Stack::item_invalidated` for why we store the tag twice. // See comment in `Stack::item_invalidated` for why we store the tag twice.
this.frame_mut().extra.stacked_borrows.as_mut().unwrap().protected_tags.push(new_tag); this.frame_mut().extra.borrow_tracker.as_mut().unwrap().protected_tags.push(new_tag);
this.machine this.machine
.stacked_borrows .borrow_tracker
.as_mut() .as_mut()
.unwrap() .unwrap()
.get_mut() .get_mut()
@ -875,11 +674,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
// We have to use shared references to alloc/memory_extra here since // We have to use shared references to alloc/memory_extra here since
// `visit_freeze_sensitive` needs to access the global state. // `visit_freeze_sensitive` needs to access the global state.
let alloc_extra = this.get_alloc_extra(alloc_id)?; let alloc_extra = this.get_alloc_extra(alloc_id)?;
let mut stacked_borrows = alloc_extra let mut stacked_borrows = alloc_extra.borrow_tracker_sb().borrow_mut();
.stacked_borrows
.as_ref()
.expect("we should have Stacked Borrows data")
.borrow_mut();
this.visit_freeze_sensitive(place, size, |mut range, frozen| { this.visit_freeze_sensitive(place, size, |mut range, frozen| {
// Adjust range. // Adjust range.
range.start += base_offset; range.start += base_offset;
@ -900,7 +695,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
false false
}; };
let item = Item::new(new_tag, perm, protected); let item = Item::new(new_tag, perm, protected);
let global = this.machine.stacked_borrows.as_ref().unwrap().borrow(); let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
let dcx = DiagnosticCxBuilder::retag( let dcx = DiagnosticCxBuilder::retag(
&this.machine, &this.machine,
retag_cause, retag_cause,
@ -929,14 +724,10 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
// Note that this asserts that the allocation is mutable -- but since we are creating a // Note that this asserts that the allocation is mutable -- but since we are creating a
// mutable pointer, that seems reasonable. // mutable pointer, that seems reasonable.
let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?; let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?;
let stacked_borrows = alloc_extra let stacked_borrows = alloc_extra.borrow_tracker_sb_mut().get_mut();
.stacked_borrows
.as_mut()
.expect("we should have Stacked Borrows data")
.get_mut();
let item = Item::new(new_tag, perm, protect.is_some()); let item = Item::new(new_tag, perm, protect.is_some());
let range = alloc_range(base_offset, size); let range = alloc_range(base_offset, size);
let global = machine.stacked_borrows.as_ref().unwrap().borrow(); let global = machine.borrow_tracker.as_ref().unwrap().borrow();
let dcx = DiagnosticCxBuilder::retag( let dcx = DiagnosticCxBuilder::retag(
machine, machine,
retag_cause, retag_cause,
@ -960,8 +751,8 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
} }
/// Retags an indidual pointer, returning the retagged version. /// Retags an indidual pointer, returning the retagged version.
/// `mutbl` can be `None` to make this a raw pointer. /// `kind` indicates what kind of reference is being created.
fn retag_reference( fn sb_retag_reference(
&mut self, &mut self,
val: &ImmTy<'tcx, Provenance>, val: &ImmTy<'tcx, Provenance>,
kind: RefKind, kind: RefKind,
@ -981,10 +772,10 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
}; };
// Compute new borrow. // Compute new borrow.
let new_tag = this.machine.stacked_borrows.as_mut().unwrap().get_mut().new_ptr(); let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
// Reborrow. // Reborrow.
let alloc_id = this.reborrow(&place, size, kind, retag_cause, new_tag, protect)?; let alloc_id = this.sb_reborrow(&place, size, kind, retag_cause, new_tag, protect)?;
// Adjust pointer. // Adjust pointer.
let new_place = place.map_provenance(|p| { let new_place = place.map_provenance(|p| {
@ -993,7 +784,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
Some(alloc_id) => { Some(alloc_id) => {
// If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one. // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one.
// Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation. // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation.
Provenance::Concrete { alloc_id, sb: new_tag } Provenance::Concrete { alloc_id, tag: new_tag }
} }
None => { None => {
// Looks like this has to stay a wildcard pointer. // Looks like this has to stay a wildcard pointer.
@ -1011,9 +802,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn retag(&mut self, kind: RetagKind, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { fn sb_retag(
&mut self,
kind: RetagKind,
place: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let retag_fields = this.machine.stacked_borrows.as_mut().unwrap().get_mut().retag_fields; let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
let retag_cause = match kind { let retag_cause = match kind {
RetagKind::TwoPhase { .. } => RetagCause::TwoPhase, RetagKind::TwoPhase { .. } => RetagCause::TwoPhase,
RetagKind::FnEntry => RetagCause::FnEntry, RetagKind::FnEntry => RetagCause::FnEntry,
@ -1039,7 +834,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
protector: Option<ProtectorKind>, protector: Option<ProtectorKind>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?; let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
let val = self.ecx.retag_reference(&val, ref_kind, retag_cause, protector)?; let val = self.ecx.sb_retag_reference(&val, ref_kind, retag_cause, protector)?;
self.ecx.write_immediate(*val, place)?; self.ecx.write_immediate(*val, place)?;
Ok(()) Ok(())
} }
@ -1138,7 +933,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// ///
/// This is a HACK because there is nothing in MIR that would make the retag /// This is a HACK because there is nothing in MIR that would make the retag
/// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>. /// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>.
fn retag_return_place(&mut self) -> InterpResult<'tcx> { fn sb_retag_return_place(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let return_place = &this.frame().return_place; let return_place = &this.frame().return_place;
if return_place.layout.is_zst() { if return_place.layout.is_zst() {
@ -1153,7 +948,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?; let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout); let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
// Reborrow it. With protection! That is part of the point. // Reborrow it. With protection! That is part of the point.
let val = this.retag_reference( let val = this.sb_retag_reference(
&val, &val,
RefKind::Unique { two_phase: false }, RefKind::Unique { two_phase: false },
RetagCause::FnReturn, RetagCause::FnReturn,
@ -1167,7 +962,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
} }
/// Mark the given tag as exposed. It was found on a pointer with the given AllocId. /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
fn expose_tag(&mut self, alloc_id: AllocId, tag: SbTag) -> InterpResult<'tcx> { fn sb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
// Function pointers and dead objects don't have an alloc_extra so we ignore them. // Function pointers and dead objects don't have an alloc_extra so we ignore them.
@ -1181,7 +976,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// uncovers a non-supported `extern static`. // uncovers a non-supported `extern static`.
let alloc_extra = this.get_alloc_extra(alloc_id)?; let alloc_extra = this.get_alloc_extra(alloc_id)?;
trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}"); trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
alloc_extra.stacked_borrows.as_ref().unwrap().borrow_mut().exposed_tags.insert(tag); alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag);
} }
AllocKind::Function | AllocKind::VTable | AllocKind::Dead => { AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
// No stacked borrows on these allocations. // No stacked borrows on these allocations.
@ -1193,7 +988,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> { fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let alloc_extra = this.get_alloc_extra(alloc_id)?; let alloc_extra = this.get_alloc_extra(alloc_id)?;
let stacks = alloc_extra.stacked_borrows.as_ref().unwrap().borrow(); let stacks = alloc_extra.borrow_tracker_sb().borrow();
for (range, stack) in stacks.stacks.iter_all() { for (range, stack) in stacks.stacks.iter_all() {
print!("{range:?}: ["); print!("{range:?}: [");
if let Some(bottom) = stack.unknown_bottom() { if let Some(bottom) = stack.unknown_bottom() {

View file

@ -3,11 +3,14 @@ use std::ops::Range;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use crate::stacked_borrows::{AccessKind, Item, Permission, SbTag}; use crate::borrow_tracker::{
stacked_borrows::{Item, Permission},
AccessKind, BorTag,
};
use crate::ProvenanceExtra; use crate::ProvenanceExtra;
/// Exactly what cache size we should use is a difficult tradeoff. There will always be some /// Exactly what cache size we should use is a difficult tradeoff. There will always be some
/// workload which has a `SbTag` working set which exceeds the size of the cache, and ends up /// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up
/// falling back to linear searches of the borrow stack very often. /// falling back to linear searches of the borrow stack very often.
/// The cost of making this value too large is that the loop in `Stack::insert` which ensures the /// The cost of making this value too large is that the loop in `Stack::insert` which ensures the
/// entries in the cache stay correct after an insert becomes expensive. /// entries in the cache stay correct after an insert becomes expensive.
@ -28,7 +31,7 @@ pub struct Stack {
/// than `id`. /// than `id`.
/// When the bottom is unknown, `borrows` always has a `SharedReadOnly` or `Unique` at the bottom; /// When the bottom is unknown, `borrows` always has a `SharedReadOnly` or `Unique` at the bottom;
/// we never have the unknown-to-known boundary in an SRW group. /// we never have the unknown-to-known boundary in an SRW group.
unknown_bottom: Option<SbTag>, unknown_bottom: Option<BorTag>,
/// A small LRU cache of searches of the borrow stack. /// A small LRU cache of searches of the borrow stack.
#[cfg(feature = "stack-cache")] #[cfg(feature = "stack-cache")]
@ -40,7 +43,7 @@ pub struct Stack {
} }
impl Stack { impl Stack {
pub fn retain(&mut self, tags: &FxHashSet<SbTag>) { pub fn retain(&mut self, tags: &FxHashSet<BorTag>) {
let mut first_removed = None; let mut first_removed = None;
// We never consider removing the bottom-most tag. For stacks without an unknown // We never consider removing the bottom-most tag. For stacks without an unknown
@ -185,7 +188,7 @@ impl<'tcx> Stack {
&mut self, &mut self,
access: AccessKind, access: AccessKind,
tag: ProvenanceExtra, tag: ProvenanceExtra,
exposed_tags: &FxHashSet<SbTag>, exposed_tags: &FxHashSet<BorTag>,
) -> Result<Option<usize>, ()> { ) -> Result<Option<usize>, ()> {
#[cfg(all(feature = "stack-cache", debug_assertions))] #[cfg(all(feature = "stack-cache", debug_assertions))]
self.verify_cache_consistency(); self.verify_cache_consistency();
@ -219,12 +222,12 @@ impl<'tcx> Stack {
// Couldn't find it in the stack; but if there is an unknown bottom it might be there. // Couldn't find it in the stack; but if there is an unknown bottom it might be there.
let found = self.unknown_bottom.is_some_and(|unknown_limit| { let found = self.unknown_bottom.is_some_and(|unknown_limit| {
tag.0 < unknown_limit.0 // unknown_limit is an upper bound for what can be in the unknown bottom. tag < unknown_limit // unknown_limit is an upper bound for what can be in the unknown bottom.
}); });
if found { Ok(None) } else { Err(()) } if found { Ok(None) } else { Err(()) }
} }
fn find_granting_tagged(&mut self, access: AccessKind, tag: SbTag) -> Option<usize> { fn find_granting_tagged(&mut self, access: AccessKind, tag: BorTag) -> Option<usize> {
#[cfg(feature = "stack-cache")] #[cfg(feature = "stack-cache")]
if let Some(idx) = self.find_granting_cache(access, tag) { if let Some(idx) = self.find_granting_cache(access, tag) {
return Some(idx); return Some(idx);
@ -243,7 +246,7 @@ impl<'tcx> Stack {
} }
#[cfg(feature = "stack-cache")] #[cfg(feature = "stack-cache")]
fn find_granting_cache(&mut self, access: AccessKind, tag: SbTag) -> Option<usize> { fn find_granting_cache(&mut self, access: AccessKind, tag: BorTag) -> Option<usize> {
// This looks like a common-sense optimization; we're going to do a linear search of the // This looks like a common-sense optimization; we're going to do a linear search of the
// cache or the borrow stack to scan the shorter of the two. This optimization is miniscule // cache or the borrow stack to scan the shorter of the two. This optimization is miniscule
// and this check actually ensures we do not access an invalid cache. // and this check actually ensures we do not access an invalid cache.
@ -349,11 +352,11 @@ impl<'tcx> Stack {
self.borrows.len() self.borrows.len()
} }
pub fn unknown_bottom(&self) -> Option<SbTag> { pub fn unknown_bottom(&self) -> Option<BorTag> {
self.unknown_bottom self.unknown_bottom
} }
pub fn set_unknown_bottom(&mut self, tag: SbTag) { pub fn set_unknown_bottom(&mut self, tag: BorTag) {
// We clear the borrow stack but the lookup cache doesn't support clearing per se. Instead, // We clear the borrow stack but the lookup cache doesn't support clearing per se. Instead,
// there is a check explained in `find_granting_cache` which protects against accessing the // there is a check explained in `find_granting_cache` which protects against accessing the
// cache when it has been cleared and not yet refilled. // cache when it has been cleared and not yet refilled.

View file

@ -59,7 +59,7 @@ use super::{
weak_memory::EvalContextExt as _, weak_memory::EvalContextExt as _,
}; };
pub type AllocExtra = VClockAlloc; pub type AllocState = VClockAlloc;
/// Valid atomic read-write orderings, alias of atomic::Ordering (not non-exhaustive). /// Valid atomic read-write orderings, alias of atomic::Ordering (not non-exhaustive).
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@ -670,7 +670,7 @@ pub struct VClockAlloc {
} }
impl VisitTags for VClockAlloc { impl VisitTags for VClockAlloc {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// No tags here. // No tags here.
} }
} }
@ -1220,7 +1220,7 @@ pub struct GlobalState {
} }
impl VisitTags for GlobalState { impl VisitTags for GlobalState {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// We don't have any tags. // We don't have any tags.
} }
} }

View file

@ -45,7 +45,7 @@ pub(super) struct InitOnce<'mir, 'tcx> {
} }
impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> { impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
for waiter in self.waiters.iter() { for waiter in self.waiters.iter() {
waiter.callback.visit_tags(visit); waiter.callback.visit_tags(visit);
} }

View file

@ -181,7 +181,7 @@ pub(crate) struct SynchronizationState<'mir, 'tcx> {
} }
impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> { impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
for init_once in self.init_onces.iter() { for init_once in self.init_onces.iter() {
init_once.visit_tags(visit); init_once.visit_tags(visit);
} }

View file

@ -3,6 +3,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::num::TryFromIntError; use std::num::TryFromIntError;
use std::task::Poll;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use log::trace; use log::trace;
@ -16,18 +17,17 @@ use rustc_target::spec::abi::Abi;
use crate::concurrency::data_race; use crate::concurrency::data_race;
use crate::concurrency::sync::SynchronizationState; use crate::concurrency::sync::SynchronizationState;
use crate::shims::tls;
use crate::*; use crate::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SchedulingAction { enum SchedulingAction {
/// Execute step on the active thread. /// Execute step on the active thread.
ExecuteStep, ExecuteStep,
/// Execute a timeout callback. /// Execute a timeout callback.
ExecuteTimeoutCallback, ExecuteTimeoutCallback,
/// Execute destructors of the active thread. /// Wait for a bit, until there is a timeout to be called.
ExecuteDtors, Sleep(Duration),
/// Stop the program.
Stop,
} }
/// Trait for callbacks that can be executed when some event happens, such as after a timeout. /// Trait for callbacks that can be executed when some event happens, such as after a timeout.
@ -41,9 +41,6 @@ type TimeoutCallback<'mir, 'tcx> = Box<dyn MachineCallback<'mir, 'tcx> + 'tcx>;
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct ThreadId(u32); pub struct ThreadId(u32);
/// The main thread. When it terminates, the whole application terminates.
const MAIN_THREAD: ThreadId = ThreadId(0);
impl ThreadId { impl ThreadId {
pub fn to_u32(self) -> u32 { pub fn to_u32(self) -> u32 {
self.0 self.0
@ -116,7 +113,13 @@ pub struct Thread<'mir, 'tcx> {
thread_name: Option<Vec<u8>>, thread_name: Option<Vec<u8>>,
/// The virtual call stack. /// The virtual call stack.
stack: Vec<Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>>, stack: Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>>,
/// The function to call when the stack ran empty, to figure out what to do next.
/// Conceptually, this is the interpreter implementation of the things that happen 'after' the
/// Rust language entry point for this thread returns (usually implemented by the C or OS runtime).
/// (`None` is an error, it means the callback has not been set up yet or is actively running.)
pub(crate) on_stack_empty: Option<StackEmptyCallback<'mir, 'tcx>>,
/// The index of the topmost user-relevant frame in `stack`. This field must contain /// The index of the topmost user-relevant frame in `stack`. This field must contain
/// the value produced by `get_top_user_relevant_frame`. /// the value produced by `get_top_user_relevant_frame`.
@ -137,19 +140,10 @@ pub struct Thread<'mir, 'tcx> {
pub(crate) last_error: Option<MPlaceTy<'tcx, Provenance>>, pub(crate) last_error: Option<MPlaceTy<'tcx, Provenance>>,
} }
impl<'mir, 'tcx> Thread<'mir, 'tcx> { pub type StackEmptyCallback<'mir, 'tcx> =
/// Check if the thread is done executing (no more stack frames). If yes, Box<dyn FnMut(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, Poll<()>>>;
/// change the state to terminated and return `true`.
fn check_terminated(&mut self) -> bool {
if self.state == ThreadState::Enabled {
if self.stack.is_empty() {
self.state = ThreadState::Terminated;
return true;
}
}
false
}
impl<'mir, 'tcx> Thread<'mir, 'tcx> {
/// Get the name of the current thread, or `<unnamed>` if it was not set. /// Get the name of the current thread, or `<unnamed>` if it was not set.
fn thread_name(&self) -> &[u8] { fn thread_name(&self) -> &[u8] {
if let Some(ref thread_name) = self.thread_name { thread_name } else { b"<unnamed>" } if let Some(ref thread_name) = self.thread_name { thread_name } else { b"<unnamed>" }
@ -202,30 +196,23 @@ impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> {
} }
} }
impl<'mir, 'tcx> Default for Thread<'mir, 'tcx> { impl<'mir, 'tcx> Thread<'mir, 'tcx> {
fn default() -> Self { fn new(name: Option<&str>, on_stack_empty: Option<StackEmptyCallback<'mir, 'tcx>>) -> Self {
Self { Self {
state: ThreadState::Enabled, state: ThreadState::Enabled,
thread_name: None, thread_name: name.map(|name| Vec::from(name.as_bytes())),
stack: Vec::new(), stack: Vec::new(),
top_user_relevant_frame: None, top_user_relevant_frame: None,
join_status: ThreadJoinStatus::Joinable, join_status: ThreadJoinStatus::Joinable,
panic_payload: None, panic_payload: None,
last_error: None, last_error: None,
on_stack_empty,
} }
} }
} }
impl<'mir, 'tcx> Thread<'mir, 'tcx> {
fn new(name: &str) -> Self {
let mut thread = Thread::default();
thread.thread_name = Some(Vec::from(name.as_bytes()));
thread
}
}
impl VisitTags for Thread<'_, '_> { impl VisitTags for Thread<'_, '_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Thread { let Thread {
panic_payload, panic_payload,
last_error, last_error,
@ -234,6 +221,7 @@ impl VisitTags for Thread<'_, '_> {
state: _, state: _,
thread_name: _, thread_name: _,
join_status: _, join_status: _,
on_stack_empty: _, // we assume the closure captures no GC-relevant state
} = self; } = self;
panic_payload.visit_tags(visit); panic_payload.visit_tags(visit);
@ -244,8 +232,8 @@ impl VisitTags for Thread<'_, '_> {
} }
} }
impl VisitTags for Frame<'_, '_, Provenance, FrameData<'_>> { impl VisitTags for Frame<'_, '_, Provenance, FrameExtra<'_>> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Frame { let Frame {
return_place, return_place,
locals, locals,
@ -327,24 +315,8 @@ pub struct ThreadManager<'mir, 'tcx> {
timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>, timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>,
} }
impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
fn default() -> Self {
let mut threads = IndexVec::new();
// Create the main thread and add it to the list of threads.
threads.push(Thread::new("main"));
Self {
active_thread: ThreadId::new(0),
threads,
sync: SynchronizationState::default(),
thread_local_alloc_ids: Default::default(),
yield_active_thread: false,
timeout_callbacks: FxHashMap::default(),
}
}
}
impl VisitTags for ThreadManager<'_, '_> { impl VisitTags for ThreadManager<'_, '_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let ThreadManager { let ThreadManager {
threads, threads,
thread_local_alloc_ids, thread_local_alloc_ids,
@ -367,8 +339,28 @@ impl VisitTags for ThreadManager<'_, '_> {
} }
} }
impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
fn default() -> Self {
let mut threads = IndexVec::new();
// Create the main thread and add it to the list of threads.
threads.push(Thread::new(Some("main"), None));
Self {
active_thread: ThreadId::new(0),
threads,
sync: SynchronizationState::default(),
thread_local_alloc_ids: Default::default(),
yield_active_thread: false,
timeout_callbacks: FxHashMap::default(),
}
}
}
impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
pub(crate) fn init(ecx: &mut MiriInterpCx<'mir, 'tcx>) { pub(crate) fn init(
ecx: &mut MiriInterpCx<'mir, 'tcx>,
on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>,
) {
ecx.machine.threads.threads[ThreadId::new(0)].on_stack_empty = Some(on_main_stack_empty);
if ecx.tcx.sess.target.os.as_ref() != "windows" { if ecx.tcx.sess.target.os.as_ref() != "windows" {
// The main thread can *not* be joined on except on windows. // The main thread can *not* be joined on except on windows.
ecx.machine.threads.threads[ThreadId::new(0)].join_status = ThreadJoinStatus::Detached; ecx.machine.threads.threads[ThreadId::new(0)].join_status = ThreadJoinStatus::Detached;
@ -393,27 +385,27 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
} }
/// Borrow the stack of the active thread. /// Borrow the stack of the active thread.
pub fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>] { pub fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>] {
&self.threads[self.active_thread].stack &self.threads[self.active_thread].stack
} }
/// Mutably borrow the stack of the active thread. /// Mutably borrow the stack of the active thread.
fn active_thread_stack_mut( fn active_thread_stack_mut(
&mut self, &mut self,
) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> { ) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
&mut self.threads[self.active_thread].stack &mut self.threads[self.active_thread].stack
} }
pub fn all_stacks( pub fn all_stacks(
&self, &self,
) -> impl Iterator<Item = &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>]> { ) -> impl Iterator<Item = &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>]> {
self.threads.iter().map(|t| &t.stack[..]) self.threads.iter().map(|t| &t.stack[..])
} }
/// Create a new thread and returns its id. /// Create a new thread and returns its id.
fn create_thread(&mut self) -> ThreadId { fn create_thread(&mut self, on_stack_empty: StackEmptyCallback<'mir, 'tcx>) -> ThreadId {
let new_thread_id = ThreadId::new(self.threads.len()); let new_thread_id = ThreadId::new(self.threads.len());
self.threads.push(Default::default()); self.threads.push(Thread::new(None, Some(on_stack_empty)));
new_thread_id new_thread_id
} }
@ -458,7 +450,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
} }
/// Get a mutable borrow of the currently active thread. /// Get a mutable borrow of the currently active thread.
fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> { pub fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> {
&mut self.threads[self.active_thread] &mut self.threads[self.active_thread]
} }
@ -669,18 +661,6 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
/// long as we can and switch only when we have to (the active thread was /// long as we can and switch only when we have to (the active thread was
/// blocked, terminated, or has explicitly asked to be preempted). /// blocked, terminated, or has explicitly asked to be preempted).
fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> { fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> {
// Check whether the thread has **just** terminated (`check_terminated`
// checks whether the thread has popped all its stack and if yes, sets
// the thread state to terminated).
if self.threads[self.active_thread].check_terminated() {
return Ok(SchedulingAction::ExecuteDtors);
}
// If we get here again and the thread is *still* terminated, there are no more dtors to run.
if self.threads[MAIN_THREAD].state == ThreadState::Terminated {
// The main thread terminated; stop the program.
// We do *not* run TLS dtors of remaining threads, which seems to match rustc behavior.
return Ok(SchedulingAction::Stop);
}
// This thread and the program can keep going. // This thread and the program can keep going.
if self.threads[self.active_thread].state == ThreadState::Enabled if self.threads[self.active_thread].state == ThreadState::Enabled
&& !self.yield_active_thread && !self.yield_active_thread
@ -688,18 +668,18 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
// The currently active thread is still enabled, just continue with it. // The currently active thread is still enabled, just continue with it.
return Ok(SchedulingAction::ExecuteStep); return Ok(SchedulingAction::ExecuteStep);
} }
// The active thread yielded. Let's see if there are any timeouts to take care of. We do // The active thread yielded or got terminated. Let's see if there are any timeouts to take
// this *before* running any other thread, to ensure that timeouts "in the past" fire before // care of. We do this *before* running any other thread, to ensure that timeouts "in the
// any other thread can take an action. This ensures that for `pthread_cond_timedwait`, "an // past" fire before any other thread can take an action. This ensures that for
// error is returned if [...] the absolute time specified by abstime has already been passed // `pthread_cond_timedwait`, "an error is returned if [...] the absolute time specified by
// at the time of the call". // abstime has already been passed at the time of the call".
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html> // <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
let potential_sleep_time = let potential_sleep_time =
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time(clock)).min(); self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time(clock)).min();
if potential_sleep_time == Some(Duration::new(0, 0)) { if potential_sleep_time == Some(Duration::new(0, 0)) {
return Ok(SchedulingAction::ExecuteTimeoutCallback); return Ok(SchedulingAction::ExecuteTimeoutCallback);
} }
// No callbacks scheduled, pick a regular thread to execute. // No callbacks immediately scheduled, pick a regular thread to execute.
// The active thread blocked or yielded. So we go search for another enabled thread. // The active thread blocked or yielded. So we go search for another enabled thread.
// Crucially, we start searching at the current active thread ID, rather than at 0, since we // Crucially, we start searching at the current active thread ID, rather than at 0, since we
// want to avoid always scheduling threads 0 and 1 without ever making progress in thread 2. // want to avoid always scheduling threads 0 and 1 without ever making progress in thread 2.
@ -730,15 +710,58 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
// All threads are currently blocked, but we have unexecuted // All threads are currently blocked, but we have unexecuted
// timeout_callbacks, which may unblock some of the threads. Hence, // timeout_callbacks, which may unblock some of the threads. Hence,
// sleep until the first callback. // sleep until the first callback.
Ok(SchedulingAction::Sleep(sleep_time))
clock.sleep(sleep_time);
Ok(SchedulingAction::ExecuteTimeoutCallback)
} else { } else {
throw_machine_stop!(TerminationInfo::Deadlock); throw_machine_stop!(TerminationInfo::Deadlock);
} }
} }
} }
impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {}
trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
/// Execute a timeout callback on the callback's thread.
#[inline]
fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let (thread, callback) = if let Some((thread, callback)) =
this.machine.threads.get_ready_callback(&this.machine.clock)
{
(thread, callback)
} else {
// get_ready_callback can return None if the computer's clock
// was shifted after calling the scheduler and before the call
// to get_ready_callback (see issue
// https://github.com/rust-lang/miri/issues/1763). In this case,
// just do nothing, which effectively just returns to the
// scheduler.
return Ok(());
};
// This back-and-forth with `set_active_thread` is here because of two
// design decisions:
// 1. Make the caller and not the callback responsible for changing
// thread.
// 2. Make the scheduler the only place that can change the active
// thread.
let old_thread = this.set_active_thread(thread);
callback.call(this)?;
this.set_active_thread(old_thread);
Ok(())
}
#[inline]
fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
let this = self.eval_context_mut();
let mut callback = this
.active_thread_mut()
.on_stack_empty
.take()
.expect("`on_stack_empty` not set up, or already running");
let res = callback(this)?;
this.active_thread_mut().on_stack_empty = Some(callback);
Ok(res)
}
}
// Public interface to thread management. // Public interface to thread management.
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
@ -773,18 +796,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
} }
} }
/// Start a regular (non-main) thread.
#[inline] #[inline]
fn create_thread(&mut self) -> ThreadId { fn start_regular_thread(
let this = self.eval_context_mut();
let id = this.machine.threads.create_thread();
if let Some(data_race) = &mut this.machine.data_race {
data_race.thread_created(&this.machine.threads, id);
}
id
}
#[inline]
fn start_thread(
&mut self, &mut self,
thread: Option<MPlaceTy<'tcx, Provenance>>, thread: Option<MPlaceTy<'tcx, Provenance>>,
start_routine: Pointer<Option<Provenance>>, start_routine: Pointer<Option<Provenance>>,
@ -795,7 +809,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
// Create the new thread // Create the new thread
let new_thread_id = this.create_thread(); let new_thread_id = this.machine.threads.create_thread({
let mut state = tls::TlsDtorsState::default();
Box::new(move |m| state.on_stack_empty(m))
});
if let Some(data_race) = &mut this.machine.data_race {
data_race.thread_created(&this.machine.threads, new_thread_id);
}
// Write the current thread-id, switch to the next thread later // Write the current thread-id, switch to the next thread later
// to treat this write operation as occuring on the current thread. // to treat this write operation as occuring on the current thread.
@ -888,12 +908,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.machine.threads.get_total_thread_count() this.machine.threads.get_total_thread_count()
} }
#[inline]
fn has_terminated(&self, thread_id: ThreadId) -> bool {
let this = self.eval_context_ref();
this.machine.threads.has_terminated(thread_id)
}
#[inline] #[inline]
fn have_all_terminated(&self) -> bool { fn have_all_terminated(&self) -> bool {
let this = self.eval_context_ref(); let this = self.eval_context_ref();
@ -907,7 +921,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
} }
#[inline] #[inline]
fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>] { fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>] {
let this = self.eval_context_ref(); let this = self.eval_context_ref();
this.machine.threads.active_thread_stack() this.machine.threads.active_thread_stack()
} }
@ -915,7 +929,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
#[inline] #[inline]
fn active_thread_stack_mut( fn active_thread_stack_mut(
&mut self, &mut self,
) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> { ) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
this.machine.threads.active_thread_stack_mut() this.machine.threads.active_thread_stack_mut()
} }
@ -943,26 +957,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
where where
'mir: 'c, 'mir: 'c,
{ {
let this = self.eval_context_ref(); self.eval_context_ref().machine.threads.get_thread_name(thread)
this.machine.threads.get_thread_name(thread)
} }
#[inline] #[inline]
fn block_thread(&mut self, thread: ThreadId) { fn block_thread(&mut self, thread: ThreadId) {
let this = self.eval_context_mut(); self.eval_context_mut().machine.threads.block_thread(thread);
this.machine.threads.block_thread(thread);
} }
#[inline] #[inline]
fn unblock_thread(&mut self, thread: ThreadId) { fn unblock_thread(&mut self, thread: ThreadId) {
let this = self.eval_context_mut(); self.eval_context_mut().machine.threads.unblock_thread(thread);
this.machine.threads.unblock_thread(thread);
} }
#[inline] #[inline]
fn yield_active_thread(&mut self) { fn yield_active_thread(&mut self) {
let this = self.eval_context_mut(); self.eval_context_mut().machine.threads.yield_active_thread();
this.machine.threads.yield_active_thread();
} }
#[inline] #[inline]
@ -995,49 +1005,42 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.machine.threads.unregister_timeout_callback_if_exists(thread); this.machine.threads.unregister_timeout_callback_if_exists(thread);
} }
/// Execute a timeout callback on the callback's thread. /// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program
#[inline] /// termination).
fn run_timeout_callback(&mut self) -> InterpResult<'tcx> { fn run_threads(&mut self) -> InterpResult<'tcx, !> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let (thread, callback) = if let Some((thread, callback)) = loop {
this.machine.threads.get_ready_callback(&this.machine.clock) match this.machine.threads.schedule(&this.machine.clock)? {
{ SchedulingAction::ExecuteStep => {
(thread, callback) if !this.step()? {
} else { // See if this thread can do something else.
// get_ready_callback can return None if the computer's clock match this.run_on_stack_empty()? {
// was shifted after calling the scheduler and before the call Poll::Pending => {} // keep going
// to get_ready_callback (see issue Poll::Ready(()) => this.terminate_active_thread()?,
// https://github.com/rust-lang/miri/issues/1763). In this case, }
// just do nothing, which effectively just returns to the }
// scheduler. }
return Ok(()); SchedulingAction::ExecuteTimeoutCallback => {
}; this.run_timeout_callback()?;
// This back-and-forth with `set_active_thread` is here because of two }
// design decisions: SchedulingAction::Sleep(duration) => {
// 1. Make the caller and not the callback responsible for changing this.machine.clock.sleep(duration);
// thread. }
// 2. Make the scheduler the only place that can change the active }
// thread. }
let old_thread = this.set_active_thread(thread);
callback.call(this)?;
this.set_active_thread(old_thread);
Ok(())
}
/// Decide which action to take next and on which thread.
#[inline]
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
let this = self.eval_context_mut();
this.machine.threads.schedule(&this.machine.clock)
} }
/// Handles thread termination of the active thread: wakes up threads joining on this one, /// Handles thread termination of the active thread: wakes up threads joining on this one,
/// and deallocated thread-local statics. /// and deallocated thread-local statics.
/// ///
/// This is called from `tls.rs` after handling the TLS dtors. /// This is called by the eval loop when a thread's on_stack_empty returns `Ready`.
#[inline] #[inline]
fn thread_terminated(&mut self) -> InterpResult<'tcx> { fn terminate_active_thread(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let thread = this.active_thread_mut();
assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated");
thread.state = ThreadState::Terminated;
for ptr in this.machine.threads.thread_terminated(this.machine.data_race.as_mut()) { for ptr in this.machine.threads.thread_terminated(this.machine.data_race.as_mut()) {
this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?; this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?;
} }

View file

@ -404,67 +404,49 @@ mod tests {
assert_eq!( assert_eq!(
alt_compare, alt_compare,
o.map(Ordering::reverse), o.map(Ordering::reverse),
"Invalid alt comparison\n l: {:?}\n r: {:?}", "Invalid alt comparison\n l: {l:?}\n r: {r:?}"
l,
r
); );
//Test operators with faster implementations //Test operators with faster implementations
assert_eq!( assert_eq!(
matches!(compare, Some(Ordering::Less)), matches!(compare, Some(Ordering::Less)),
l < r, l < r,
"Invalid (<):\n l: {:?}\n r: {:?}", "Invalid (<):\n l: {l:?}\n r: {r:?}"
l,
r
); );
assert_eq!( assert_eq!(
matches!(compare, Some(Ordering::Less) | Some(Ordering::Equal)), matches!(compare, Some(Ordering::Less) | Some(Ordering::Equal)),
l <= r, l <= r,
"Invalid (<=):\n l: {:?}\n r: {:?}", "Invalid (<=):\n l: {l:?}\n r: {r:?}"
l,
r
); );
assert_eq!( assert_eq!(
matches!(compare, Some(Ordering::Greater)), matches!(compare, Some(Ordering::Greater)),
l > r, l > r,
"Invalid (>):\n l: {:?}\n r: {:?}", "Invalid (>):\n l: {l:?}\n r: {r:?}"
l,
r
); );
assert_eq!( assert_eq!(
matches!(compare, Some(Ordering::Greater) | Some(Ordering::Equal)), matches!(compare, Some(Ordering::Greater) | Some(Ordering::Equal)),
l >= r, l >= r,
"Invalid (>=):\n l: {:?}\n r: {:?}", "Invalid (>=):\n l: {l:?}\n r: {r:?}"
l,
r
); );
assert_eq!( assert_eq!(
matches!(alt_compare, Some(Ordering::Less)), matches!(alt_compare, Some(Ordering::Less)),
r < l, r < l,
"Invalid alt (<):\n l: {:?}\n r: {:?}", "Invalid alt (<):\n l: {l:?}\n r: {r:?}"
l,
r
); );
assert_eq!( assert_eq!(
matches!(alt_compare, Some(Ordering::Less) | Some(Ordering::Equal)), matches!(alt_compare, Some(Ordering::Less) | Some(Ordering::Equal)),
r <= l, r <= l,
"Invalid alt (<=):\n l: {:?}\n r: {:?}", "Invalid alt (<=):\n l: {l:?}\n r: {r:?}"
l,
r
); );
assert_eq!( assert_eq!(
matches!(alt_compare, Some(Ordering::Greater)), matches!(alt_compare, Some(Ordering::Greater)),
r > l, r > l,
"Invalid alt (>):\n l: {:?}\n r: {:?}", "Invalid alt (>):\n l: {l:?}\n r: {r:?}"
l,
r
); );
assert_eq!( assert_eq!(
matches!(alt_compare, Some(Ordering::Greater) | Some(Ordering::Equal)), matches!(alt_compare, Some(Ordering::Greater) | Some(Ordering::Equal)),
r >= l, r >= l,
"Invalid alt (>=):\n l: {:?}\n r: {:?}", "Invalid alt (>=):\n l: {l:?}\n r: {r:?}"
l,
r
); );
} }
} }

View file

@ -93,7 +93,7 @@ use super::{
vector_clock::{VClock, VTimestamp, VectorIdx}, vector_clock::{VClock, VTimestamp, VectorIdx},
}; };
pub type AllocExtra = StoreBufferAlloc; pub type AllocState = StoreBufferAlloc;
// Each store buffer must be bounded otherwise it will grow indefinitely. // Each store buffer must be bounded otherwise it will grow indefinitely.
// However, bounding the store buffer means restricting the amount of weak // However, bounding the store buffer means restricting the amount of weak
@ -109,7 +109,7 @@ pub struct StoreBufferAlloc {
} }
impl VisitTags for StoreBufferAlloc { impl VisitTags for StoreBufferAlloc {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Self { store_buffers } = self; let Self { store_buffers } = self;
for val in store_buffers for val in store_buffers
.borrow() .borrow()

View file

@ -6,12 +6,15 @@ use log::trace;
use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol}; use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol};
use rustc_target::abi::{Align, Size}; use rustc_target::abi::{Align, Size};
use crate::stacked_borrows::{diagnostics::TagHistory, AccessKind}; use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory;
use crate::*; use crate::*;
/// Details of premature program termination. /// Details of premature program termination.
pub enum TerminationInfo { pub enum TerminationInfo {
Exit(i64), Exit {
code: i64,
leak_check: bool,
},
Abort(String), Abort(String),
UnsupportedInIsolation(String), UnsupportedInIsolation(String),
StackedBorrowsUb { StackedBorrowsUb {
@ -38,7 +41,7 @@ impl fmt::Display for TerminationInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TerminationInfo::*; use TerminationInfo::*;
match self { match self {
Exit(code) => write!(f, "the evaluated program completed with exit code {code}"), Exit { code, .. } => write!(f, "the evaluated program completed with exit code {code}"),
Abort(msg) => write!(f, "{msg}"), Abort(msg) => write!(f, "{msg}"),
UnsupportedInIsolation(msg) => write!(f, "{msg}"), UnsupportedInIsolation(msg) => write!(f, "{msg}"),
Int2PtrWithStrictProvenance => Int2PtrWithStrictProvenance =>
@ -64,9 +67,8 @@ pub enum NonHaltingDiagnostic {
/// ///
/// new_kind is `None` for base tags. /// new_kind is `None` for base tags.
CreatedPointerTag(NonZeroU64, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>), CreatedPointerTag(NonZeroU64, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>),
/// This `Item` was popped from the borrow stack, either due to an access with the given tag or /// This `Item` was popped from the borrow stack. The string explains the reason.
/// a deallocation when the second argument is `None`. PoppedPointerTag(Item, String),
PoppedPointerTag(Item, Option<(ProvenanceExtra, AccessKind)>),
CreatedCallId(CallId), CreatedCallId(CallId),
CreatedAlloc(AllocId, Size, Align, MemoryKind<MiriMemoryKind>), CreatedAlloc(AllocId, Size, Align, MemoryKind<MiriMemoryKind>),
FreedAlloc(AllocId), FreedAlloc(AllocId),
@ -148,11 +150,11 @@ fn prune_stacktrace<'tcx>(
/// Emit a custom diagnostic without going through the miri-engine machinery. /// Emit a custom diagnostic without going through the miri-engine machinery.
/// ///
/// Returns `Some` if this was regular program termination with a given exit code, `None` otherwise. /// Returns `Some` if this was regular program termination with a given exit code and a `bool` indicating whether a leak check should happen; `None` otherwise.
pub fn report_error<'tcx, 'mir>( pub fn report_error<'tcx, 'mir>(
ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
e: InterpErrorInfo<'tcx>, e: InterpErrorInfo<'tcx>,
) -> Option<i64> { ) -> Option<(i64, bool)> {
use InterpError::*; use InterpError::*;
let mut msg = vec![]; let mut msg = vec![];
@ -161,7 +163,7 @@ pub fn report_error<'tcx, 'mir>(
let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload"); let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
use TerminationInfo::*; use TerminationInfo::*;
let title = match info { let title = match info {
Exit(code) => return Some(*code), Exit { code, leak_check } => return Some((*code, *leak_check)),
Abort(_) => Some("abnormal termination"), Abort(_) => Some("abnormal termination"),
UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance => UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance =>
Some("unsupported operation"), Some("unsupported operation"),
@ -396,15 +398,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
format!( format!(
"created tag {tag:?} for {kind} at {alloc_id:?}{range:?} derived from {orig_tag:?}" "created tag {tag:?} for {kind} at {alloc_id:?}{range:?} derived from {orig_tag:?}"
), ),
PoppedPointerTag(item, tag) => PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
match tag {
None => format!("popped tracked tag for item {item:?} due to deallocation",),
Some((tag, access)) => {
format!(
"popped tracked tag for item {item:?} due to {access:?} access for {tag:?}",
)
}
},
CreatedCallId(id) => format!("function call with id {id}"), CreatedCallId(id) => format!("function call with id {id}"),
CreatedAlloc(AllocId(id), size, align, kind) => CreatedAlloc(AllocId(id), size, align, kind) =>
format!( format!(

View file

@ -4,10 +4,12 @@ use std::ffi::{OsStr, OsString};
use std::iter; use std::iter;
use std::panic::{self, AssertUnwindSafe}; use std::panic::{self, AssertUnwindSafe};
use std::path::PathBuf; use std::path::PathBuf;
use std::task::Poll;
use std::thread; use std::thread;
use log::info; use log::info;
use crate::borrow_tracker::RetagFields;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::Namespace; use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -20,8 +22,14 @@ use rustc_target::spec::abi::Abi;
use rustc_session::config::EntryFnType; use rustc_session::config::EntryFnType;
use crate::shims::tls;
use crate::*; use crate::*;
/// When the main thread would exit, we will yield to any other thread that is ready to execute.
/// But we must only do that a finite number of times, or a background thread running `loop {}`
/// will hang the program.
const MAIN_THREAD_YIELDS_AT_SHUTDOWN: u32 = 256;
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum AlignmentCheck { pub enum AlignmentCheck {
/// Do not check alignment. /// Do not check alignment.
@ -80,7 +88,7 @@ pub struct MiriConfig {
/// Determine if validity checking is enabled. /// Determine if validity checking is enabled.
pub validate: bool, pub validate: bool,
/// Determines if Stacked Borrows is enabled. /// Determines if Stacked Borrows is enabled.
pub stacked_borrows: bool, pub borrow_tracker: Option<BorrowTrackerMethod>,
/// Controls alignment checking. /// Controls alignment checking.
pub check_alignment: AlignmentCheck, pub check_alignment: AlignmentCheck,
/// Controls function [ABI](Abi) checking. /// Controls function [ABI](Abi) checking.
@ -96,7 +104,7 @@ pub struct MiriConfig {
/// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`). /// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
pub seed: Option<u64>, pub seed: Option<u64>,
/// The stacked borrows pointer ids to report about /// The stacked borrows pointer ids to report about
pub tracked_pointer_tags: FxHashSet<SbTag>, pub tracked_pointer_tags: FxHashSet<BorTag>,
/// The stacked borrows call IDs to report about /// The stacked borrows call IDs to report about
pub tracked_call_ids: FxHashSet<CallId>, pub tracked_call_ids: FxHashSet<CallId>,
/// The allocation ids to report about. /// The allocation ids to report about.
@ -131,7 +139,7 @@ pub struct MiriConfig {
/// The location of a shared object file to load when calling external functions /// The location of a shared object file to load when calling external functions
/// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory /// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory
pub external_so_file: Option<PathBuf>, pub external_so_file: Option<PathBuf>,
/// Run a garbage collector for SbTags every N basic blocks. /// Run a garbage collector for BorTags every N basic blocks.
pub gc_interval: u32, pub gc_interval: u32,
/// The number of CPUs to be reported by miri. /// The number of CPUs to be reported by miri.
pub num_cpus: u32, pub num_cpus: u32,
@ -142,7 +150,7 @@ impl Default for MiriConfig {
MiriConfig { MiriConfig {
env: vec![], env: vec![],
validate: true, validate: true,
stacked_borrows: true, borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows),
check_alignment: AlignmentCheck::Int, check_alignment: AlignmentCheck::Int,
check_abi: true, check_abi: true,
isolated_op: IsolatedOp::Reject(RejectOpWith::Abort), isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
@ -172,17 +180,79 @@ impl Default for MiriConfig {
} }
} }
/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing /// The state of the main thread. Implementation detail of `on_main_stack_empty`.
/// the location where the return value of the `start` function will be #[derive(Default, Debug)]
/// written to. enum MainThreadState {
#[default]
Running,
TlsDtors(tls::TlsDtorsState),
Yield {
remaining: u32,
},
Done,
}
impl MainThreadState {
fn on_main_stack_empty<'tcx>(
&mut self,
this: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, Poll<()>> {
use MainThreadState::*;
match self {
Running => {
*self = TlsDtors(Default::default());
}
TlsDtors(state) =>
match state.on_stack_empty(this)? {
Poll::Pending => {} // just keep going
Poll::Ready(()) => {
// Give background threads a chance to finish by yielding the main thread a
// couple of times -- but only if we would also preempt threads randomly.
if this.machine.preemption_rate > 0.0 {
// There is a non-zero chance they will yield back to us often enough to
// make Miri terminate eventually.
*self = Yield { remaining: MAIN_THREAD_YIELDS_AT_SHUTDOWN };
} else {
// The other threads did not get preempted, so no need to yield back to
// them.
*self = Done;
}
}
},
Yield { remaining } =>
match remaining.checked_sub(1) {
None => *self = Done,
Some(new_remaining) => {
*remaining = new_remaining;
this.yield_active_thread();
}
},
Done => {
// Figure out exit code.
let ret_place = MPlaceTy::from_aligned_ptr(
this.machine.main_fn_ret_place.unwrap().ptr,
this.machine.layouts.isize,
);
let exit_code = this.read_scalar(&ret_place.into())?.to_machine_isize(this)?;
// Need to call this ourselves since we are not going to return to the scheduler
// loop, and we want the main thread TLS to not show up as memory leaks.
this.terminate_active_thread()?;
// Stop interpreter loop.
throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
}
}
Ok(Poll::Pending)
}
}
/// Returns a freshly created `InterpCx`.
/// Public because this is also used by `priroda`. /// Public because this is also used by `priroda`.
pub fn create_ecx<'mir, 'tcx: 'mir>( pub fn create_ecx<'mir, 'tcx: 'mir>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
entry_id: DefId, entry_id: DefId,
entry_type: EntryFnType, entry_type: EntryFnType,
config: &MiriConfig, config: &MiriConfig,
) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, MPlaceTy<'tcx, Provenance>)> ) -> InterpResult<'tcx, InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>> {
{
let param_env = ty::ParamEnv::reveal_all(); let param_env = ty::ParamEnv::reveal_all();
let layout_cx = LayoutCx { tcx, param_env }; let layout_cx = LayoutCx { tcx, param_env };
let mut ecx = InterpCx::new( let mut ecx = InterpCx::new(
@ -193,7 +263,11 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
); );
// Some parts of initialization require a full `InterpCx`. // Some parts of initialization require a full `InterpCx`.
MiriMachine::late_init(&mut ecx, config)?; MiriMachine::late_init(&mut ecx, config, {
let mut state = MainThreadState::default();
// Cannot capture anything GC-relevant here.
Box::new(move |m| state.on_main_stack_empty(m))
})?;
// Make sure we have MIR. We check MIR for some stable monomorphic function in libcore. // Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"], Namespace::ValueNS); let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"], Namespace::ValueNS);
@ -274,6 +348,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
// Return place (in static memory so that it does not count as leak). // Return place (in static memory so that it does not count as leak).
let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?; let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
ecx.machine.main_fn_ret_place = Some(*ret_place);
// Call start function. // Call start function.
match entry_type { match entry_type {
@ -321,7 +396,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
} }
} }
Ok((ecx, ret_place)) Ok(ecx)
} }
/// Evaluates the entry function specified by `entry_id`. /// Evaluates the entry function specified by `entry_id`.
@ -337,7 +412,7 @@ pub fn eval_entry<'tcx>(
// Copy setting before we move `config`. // Copy setting before we move `config`.
let ignore_leaks = config.ignore_leaks; let ignore_leaks = config.ignore_leaks;
let (mut ecx, ret_place) = match create_ecx(tcx, entry_id, entry_type, &config) { let mut ecx = match create_ecx(tcx, entry_id, entry_type, &config) {
Ok(v) => v, Ok(v) => v,
Err(err) => { Err(err) => {
err.print_backtrace(); err.print_backtrace();
@ -346,34 +421,17 @@ pub fn eval_entry<'tcx>(
}; };
// Perform the main execution. // Perform the main execution.
let res: thread::Result<InterpResult<'_, i64>> = panic::catch_unwind(AssertUnwindSafe(|| { let res: thread::Result<InterpResult<'_, !>> =
// Main loop. panic::catch_unwind(AssertUnwindSafe(|| ecx.run_threads()));
loop {
match ecx.schedule()? {
SchedulingAction::ExecuteStep => {
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
}
SchedulingAction::ExecuteTimeoutCallback => {
ecx.run_timeout_callback()?;
}
SchedulingAction::ExecuteDtors => {
// This will either enable the thread again (so we go back
// to `ExecuteStep`), or determine that this thread is done
// for good.
ecx.schedule_next_tls_dtor_for_active_thread()?;
}
SchedulingAction::Stop => {
break;
}
}
}
let return_code = ecx.read_scalar(&ret_place.into())?.to_machine_isize(&ecx)?;
Ok(return_code)
}));
let res = res.unwrap_or_else(|panic_payload| { let res = res.unwrap_or_else(|panic_payload| {
ecx.handle_ice(); ecx.handle_ice();
panic::resume_unwind(panic_payload) panic::resume_unwind(panic_payload)
}); });
let res = match res {
Err(res) => res,
// `Ok` can never happen
Ok(never) => match never {},
};
// Machine cleanup. Only do this if all threads have terminated; threads that are still running // Machine cleanup. Only do this if all threads have terminated; threads that are still running
// might cause Stacked Borrows errors (https://github.com/rust-lang/miri/issues/2396). // might cause Stacked Borrows errors (https://github.com/rust-lang/miri/issues/2396).
@ -386,32 +444,26 @@ pub fn eval_entry<'tcx>(
} }
// Process the result. // Process the result.
match res { let (return_code, leak_check) = report_error(&ecx, res)?;
Ok(return_code) => { if leak_check && !ignore_leaks {
if !ignore_leaks { // Check for thread leaks.
// Check for thread leaks. if !ecx.have_all_terminated() {
if !ecx.have_all_terminated() { tcx.sess.err("the main thread terminated without waiting for all remaining threads");
tcx.sess.err( tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check");
"the main thread terminated without waiting for all remaining threads", return None;
); }
tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check"); // Check for memory leaks.
return None; info!("Additonal static roots: {:?}", ecx.machine.static_roots);
} let leaks = ecx.leak_report(&ecx.machine.static_roots);
// Check for memory leaks. if leaks != 0 {
info!("Additonal static roots: {:?}", ecx.machine.static_roots); tcx.sess.err("the evaluated program leaked memory");
let leaks = ecx.leak_report(&ecx.machine.static_roots); tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check");
if leaks != 0 { // Ignore the provided return code - let the reported error
tcx.sess.err("the evaluated program leaked memory"); // determine the return code.
tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check"); return None;
// Ignore the provided return code - let the reported error
// determine the return code.
return None;
}
}
Some(return_code)
} }
Err(e) => report_error(&ecx, e),
} }
Some(return_code)
} }
/// Turns an array of arguments into a Windows command line string. /// Turns an array of arguments into a Windows command line string.

View file

@ -554,9 +554,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
assert_eq!( assert_eq!(
self.eval_context_ref().tcx.sess.target.os, self.eval_context_ref().tcx.sess.target.os,
target_os, target_os,
"`{}` is only available on the `{}` target OS", "`{name}` is only available on the `{target_os}` target OS",
name,
target_os,
) )
} }
@ -566,8 +564,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn assert_target_os_is_unix(&self, name: &str) { fn assert_target_os_is_unix(&self, name: &str) {
assert!( assert!(
target_os_is_unix(self.eval_context_ref().tcx.sess.target.os.as_ref()), target_os_is_unix(self.eval_context_ref().tcx.sess.target.os.as_ref()),
"`{}` is only available for supported UNIX family targets", "`{name}` is only available for supported UNIX family targets",
name,
); );
} }
@ -988,7 +985,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
self.stack()[frame_idx].current_span() self.stack()[frame_idx].current_span()
} }
fn stack(&self) -> &[Frame<'mir, 'tcx, Provenance, machine::FrameData<'tcx>>] { fn stack(&self) -> &[Frame<'mir, 'tcx, Provenance, machine::FrameExtra<'tcx>>] {
self.threads.active_thread_stack() self.threads.active_thread_stack()
} }
@ -1019,8 +1016,7 @@ where
pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> { pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
"{} not available when isolation is enabled", "{name} not available when isolation is enabled",
name,
))) )))
} }

View file

@ -45,7 +45,7 @@ pub struct GlobalStateInner {
} }
impl VisitTags for GlobalStateInner { impl VisitTags for GlobalStateInner {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// Nothing to visit here. // Nothing to visit here.
} }
} }
@ -105,15 +105,15 @@ impl<'mir, 'tcx> GlobalStateInner {
pub fn expose_ptr( pub fn expose_ptr(
ecx: &mut MiriInterpCx<'mir, 'tcx>, ecx: &mut MiriInterpCx<'mir, 'tcx>,
alloc_id: AllocId, alloc_id: AllocId,
sb: SbTag, tag: BorTag,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let global_state = ecx.machine.intptrcast.get_mut(); let global_state = ecx.machine.intptrcast.get_mut();
// In strict mode, we don't need this, so we can save some cycles by not tracking it. // In strict mode, we don't need this, so we can save some cycles by not tracking it.
if global_state.provenance_mode != ProvenanceMode::Strict { if global_state.provenance_mode != ProvenanceMode::Strict {
trace!("Exposing allocation id {alloc_id:?}"); trace!("Exposing allocation id {alloc_id:?}");
global_state.exposed.insert(alloc_id); global_state.exposed.insert(alloc_id);
if ecx.machine.stacked_borrows.is_some() { if ecx.machine.borrow_tracker.is_some() {
ecx.expose_tag(alloc_id, sb)?; ecx.expose_tag(alloc_id, tag)?;
} }
} }
Ok(()) Ok(())

View file

@ -53,6 +53,7 @@ extern crate rustc_session;
extern crate rustc_span; extern crate rustc_span;
extern crate rustc_target; extern crate rustc_target;
mod borrow_tracker;
mod clock; mod clock;
mod concurrency; mod concurrency;
mod diagnostics; mod diagnostics;
@ -64,7 +65,6 @@ mod mono_hash_map;
mod operator; mod operator;
mod range_map; mod range_map;
mod shims; mod shims;
mod stacked_borrows;
mod tag_gc; mod tag_gc;
// Establish a "crate-wide prelude": we often import `crate::*`. // Establish a "crate-wide prelude": we often import `crate::*`.
@ -81,15 +81,21 @@ pub use crate::shims::intrinsics::EvalContextExt as _;
pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::os_str::EvalContextExt as _;
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
pub use crate::shims::time::EvalContextExt as _; pub use crate::shims::time::EvalContextExt as _;
pub use crate::shims::tls::{EvalContextExt as _, TlsData}; pub use crate::shims::tls::TlsData;
pub use crate::shims::EvalContextExt as _; pub use crate::shims::EvalContextExt as _;
pub use crate::borrow_tracker::stacked_borrows::{
EvalContextExt as _, Item, Permission, Stack, Stacks,
};
pub use crate::borrow_tracker::{
BorTag, BorrowTrackerMethod, CallId, EvalContextExt as _, RetagFields,
};
pub use crate::clock::{Clock, Instant}; pub use crate::clock::{Clock, Instant};
pub use crate::concurrency::{ pub use crate::concurrency::{
data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _}, data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
init_once::{EvalContextExt as _, InitOnceId}, init_once::{EvalContextExt as _, InitOnceId},
sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SyncId}, sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SyncId},
thread::{EvalContextExt as _, SchedulingAction, ThreadId, ThreadManager, ThreadState, Time}, thread::{EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, Time},
}; };
pub use crate::diagnostics::{ pub use crate::diagnostics::{
report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo,
@ -100,15 +106,12 @@ pub use crate::eval::{
pub use crate::helpers::EvalContextExt as _; pub use crate::helpers::EvalContextExt as _;
pub use crate::intptrcast::ProvenanceMode; pub use crate::intptrcast::ProvenanceMode;
pub use crate::machine::{ pub use crate::machine::{
AllocExtra, FrameData, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind, AllocExtra, FrameExtra, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind,
PrimitiveLayouts, Provenance, ProvenanceExtra, PAGE_SIZE, STACK_ADDR, STACK_SIZE, PrimitiveLayouts, Provenance, ProvenanceExtra, PAGE_SIZE, STACK_ADDR, STACK_SIZE,
}; };
pub use crate::mono_hash_map::MonoHashMap; pub use crate::mono_hash_map::MonoHashMap;
pub use crate::operator::EvalContextExt as _; pub use crate::operator::EvalContextExt as _;
pub use crate::range_map::RangeMap; pub use crate::range_map::RangeMap;
pub use crate::stacked_borrows::{
CallId, EvalContextExt as _, Item, Permission, RetagFields, SbTag, Stack, Stacks,
};
pub use crate::tag_gc::{EvalContextExt as _, VisitTags}; pub use crate::tag_gc::{EvalContextExt as _, VisitTags};
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be /// Insert rustc arguments at the beginning of the argument list that Miri wants to be

View file

@ -37,9 +37,9 @@ pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but
pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
/// Extra data stored with each stack frame /// Extra data stored with each stack frame
pub struct FrameData<'tcx> { pub struct FrameExtra<'tcx> {
/// Extra data for Stacked Borrows. /// Extra data for Stacked Borrows.
pub stacked_borrows: Option<stacked_borrows::FrameExtra>, pub borrow_tracker: Option<borrow_tracker::FrameState>,
/// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn` /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
/// called by `try`). When this frame is popped during unwinding a panic, /// called by `try`). When this frame is popped during unwinding a panic,
@ -58,23 +58,23 @@ pub struct FrameData<'tcx> {
pub is_user_relevant: bool, pub is_user_relevant: bool,
} }
impl<'tcx> std::fmt::Debug for FrameData<'tcx> { impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Omitting `timing`, it does not support `Debug`. // Omitting `timing`, it does not support `Debug`.
let FrameData { stacked_borrows, catch_unwind, timing: _, is_user_relevant: _ } = self; let FrameExtra { borrow_tracker, catch_unwind, timing: _, is_user_relevant: _ } = self;
f.debug_struct("FrameData") f.debug_struct("FrameData")
.field("stacked_borrows", stacked_borrows) .field("borrow_tracker", borrow_tracker)
.field("catch_unwind", catch_unwind) .field("catch_unwind", catch_unwind)
.finish() .finish()
} }
} }
impl VisitTags for FrameData<'_> { impl VisitTags for FrameExtra<'_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let FrameData { catch_unwind, stacked_borrows, timing: _, is_user_relevant: _ } = self; let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _ } = self;
catch_unwind.visit_tags(visit); catch_unwind.visit_tags(visit);
stacked_borrows.visit_tags(visit); borrow_tracker.visit_tags(visit);
} }
} }
@ -147,7 +147,7 @@ pub enum Provenance {
Concrete { Concrete {
alloc_id: AllocId, alloc_id: AllocId,
/// Stacked Borrows tag. /// Stacked Borrows tag.
sb: SbTag, tag: BorTag,
}, },
Wildcard, Wildcard,
} }
@ -173,7 +173,7 @@ impl std::hash::Hash for Provenance {
/// The "extra" information a pointer has over a regular AllocId. /// The "extra" information a pointer has over a regular AllocId.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum ProvenanceExtra { pub enum ProvenanceExtra {
Concrete(SbTag), Concrete(BorTag),
Wildcard, Wildcard,
} }
@ -188,7 +188,7 @@ static_assert_size!(Scalar<Provenance>, 32);
impl fmt::Debug for Provenance { impl fmt::Debug for Provenance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Provenance::Concrete { alloc_id, sb } => { Provenance::Concrete { alloc_id, tag } => {
// Forward `alternate` flag to `alloc_id` printing. // Forward `alternate` flag to `alloc_id` printing.
if f.alternate() { if f.alternate() {
write!(f, "[{alloc_id:#?}]")?; write!(f, "[{alloc_id:#?}]")?;
@ -196,7 +196,7 @@ impl fmt::Debug for Provenance {
write!(f, "[{alloc_id:?}]")?; write!(f, "[{alloc_id:?}]")?;
} }
// Print Stacked Borrows tag. // Print Stacked Borrows tag.
write!(f, "{sb:?}")?; write!(f, "{tag:?}")?;
} }
Provenance::Wildcard => { Provenance::Wildcard => {
write!(f, "[wildcard]")?; write!(f, "[wildcard]")?;
@ -221,9 +221,9 @@ impl interpret::Provenance for Provenance {
match (left, right) { match (left, right) {
// If both are the *same* concrete tag, that is the result. // If both are the *same* concrete tag, that is the result.
( (
Some(Provenance::Concrete { alloc_id: left_alloc, sb: left_sb }), Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }),
Some(Provenance::Concrete { alloc_id: right_alloc, sb: right_sb }), Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }),
) if left_alloc == right_alloc && left_sb == right_sb => left, ) if left_alloc == right_alloc && left_tag == right_tag => left,
// If one side is a wildcard, the best possible outcome is that it is equal to the other // If one side is a wildcard, the best possible outcome is that it is equal to the other
// one, and we use that. // one, and we use that.
(Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o, (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o,
@ -243,7 +243,7 @@ impl fmt::Debug for ProvenanceExtra {
} }
impl ProvenanceExtra { impl ProvenanceExtra {
pub fn and_then<T>(self, f: impl FnOnce(SbTag) -> Option<T>) -> Option<T> { pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> {
match self { match self {
ProvenanceExtra::Concrete(pid) => f(pid), ProvenanceExtra::Concrete(pid) => f(pid),
ProvenanceExtra::Wildcard => None, ProvenanceExtra::Wildcard => None,
@ -254,21 +254,21 @@ impl ProvenanceExtra {
/// Extra per-allocation data /// Extra per-allocation data
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AllocExtra { pub struct AllocExtra {
/// Stacked Borrows state is only added if it is enabled. /// Global state of the borrow tracker, if enabled.
pub stacked_borrows: Option<stacked_borrows::AllocExtra>, pub borrow_tracker: Option<borrow_tracker::AllocState>,
/// Data race detection via the use of a vector-clock, /// Data race detection via the use of a vector-clock,
/// this is only added if it is enabled. /// this is only added if it is enabled.
pub data_race: Option<data_race::AllocExtra>, pub data_race: Option<data_race::AllocState>,
/// Weak memory emulation via the use of store buffers, /// Weak memory emulation via the use of store buffers,
/// this is only added if it is enabled. /// this is only added if it is enabled.
pub weak_memory: Option<weak_memory::AllocExtra>, pub weak_memory: Option<weak_memory::AllocState>,
} }
impl VisitTags for AllocExtra { impl VisitTags for AllocExtra {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let AllocExtra { stacked_borrows, data_race, weak_memory } = self; let AllocExtra { borrow_tracker, data_race, weak_memory } = self;
stacked_borrows.visit_tags(visit); borrow_tracker.visit_tags(visit);
data_race.visit_tags(visit); data_race.visit_tags(visit);
weak_memory.visit_tags(visit); weak_memory.visit_tags(visit);
} }
@ -350,8 +350,8 @@ pub struct MiriMachine<'mir, 'tcx> {
// We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access. // We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access.
pub tcx: TyCtxt<'tcx>, pub tcx: TyCtxt<'tcx>,
/// Stacked Borrows global data. /// Global data for borrow tracking.
pub stacked_borrows: Option<stacked_borrows::GlobalState>, pub borrow_tracker: Option<borrow_tracker::GlobalState>,
/// Data race detector global data. /// Data race detector global data.
pub data_race: Option<data_race::GlobalState>, pub data_race: Option<data_race::GlobalState>,
@ -363,6 +363,9 @@ pub struct MiriMachine<'mir, 'tcx> {
/// Miri does not expose env vars from the host to the emulated program. /// Miri does not expose env vars from the host to the emulated program.
pub(crate) env_vars: EnvVars<'tcx>, pub(crate) env_vars: EnvVars<'tcx>,
/// Return place of the main function.
pub(crate) main_fn_ret_place: Option<MemPlace<Provenance>>,
/// Program arguments (`Option` because we can only initialize them after creating the ecx). /// Program arguments (`Option` because we can only initialize them after creating the ecx).
/// These are *pointers* to argc/argv because macOS. /// These are *pointers* to argc/argv because macOS.
/// We also need the full command line as one string because of Windows. /// We also need the full command line as one string because of Windows.
@ -460,9 +463,9 @@ pub struct MiriMachine<'mir, 'tcx> {
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
pub external_so_lib: Option<!>, pub external_so_lib: Option<!>,
/// Run a garbage collector for SbTags every N basic blocks. /// Run a garbage collector for BorTags every N basic blocks.
pub(crate) gc_interval: u32, pub(crate) gc_interval: u32,
/// The number of blocks that passed since the last SbTag GC pass. /// The number of blocks that passed since the last BorTag GC pass.
pub(crate) since_gc: u32, pub(crate) since_gc: u32,
/// The number of CPUs to be reported by miri. /// The number of CPUs to be reported by miri.
pub(crate) num_cpus: u32, pub(crate) num_cpus: u32,
@ -477,21 +480,16 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
measureme::Profiler::new(out).expect("Couldn't create `measureme` profiler") measureme::Profiler::new(out).expect("Couldn't create `measureme` profiler")
}); });
let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0)); let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
let stacked_borrows = config.stacked_borrows.then(|| { let borrow_tracker = config.borrow_tracker.map(|bt| bt.instanciate_global_state(config));
RefCell::new(stacked_borrows::GlobalStateInner::new(
config.tracked_pointer_tags.clone(),
config.tracked_call_ids.clone(),
config.retag_fields,
))
});
let data_race = config.data_race_detector.then(|| data_race::GlobalState::new(config)); let data_race = config.data_race_detector.then(|| data_race::GlobalState::new(config));
MiriMachine { MiriMachine {
tcx: layout_cx.tcx, tcx: layout_cx.tcx,
stacked_borrows, borrow_tracker,
data_race, data_race,
intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config)), intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config)),
// `env_vars` depends on a full interpreter so we cannot properly initialize it yet. // `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
env_vars: EnvVars::default(), env_vars: EnvVars::default(),
main_fn_ret_place: None,
argc: None, argc: None,
argv: None, argv: None,
cmd_line: None, cmd_line: None,
@ -556,10 +554,11 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
pub(crate) fn late_init( pub(crate) fn late_init(
this: &mut MiriInterpCx<'mir, 'tcx>, this: &mut MiriInterpCx<'mir, 'tcx>,
config: &MiriConfig, config: &MiriConfig,
on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
EnvVars::init(this, config)?; EnvVars::init(this, config)?;
MiriMachine::init_extern_statics(this)?; MiriMachine::init_extern_statics(this)?;
ThreadManager::init(this); ThreadManager::init(this, on_main_stack_empty);
Ok(()) Ok(())
} }
@ -651,18 +650,19 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
} }
impl VisitTags for MiriMachine<'_, '_> { impl VisitTags for MiriMachine<'_, '_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
#[rustfmt::skip] #[rustfmt::skip]
let MiriMachine { let MiriMachine {
threads, threads,
tls, tls,
env_vars, env_vars,
main_fn_ret_place,
argc, argc,
argv, argv,
cmd_line, cmd_line,
extern_statics, extern_statics,
dir_handler, dir_handler,
stacked_borrows, borrow_tracker,
data_race, data_race,
intptrcast, intptrcast,
file_handler, file_handler,
@ -700,8 +700,9 @@ impl VisitTags for MiriMachine<'_, '_> {
dir_handler.visit_tags(visit); dir_handler.visit_tags(visit);
file_handler.visit_tags(visit); file_handler.visit_tags(visit);
data_race.visit_tags(visit); data_race.visit_tags(visit);
stacked_borrows.visit_tags(visit); borrow_tracker.visit_tags(visit);
intptrcast.visit_tags(visit); intptrcast.visit_tags(visit);
main_fn_ret_place.visit_tags(visit);
argc.visit_tags(visit); argc.visit_tags(visit);
argv.visit_tags(visit); argv.visit_tags(visit);
cmd_line.visit_tags(visit); cmd_line.visit_tags(visit);
@ -735,7 +736,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
type MemoryKind = MiriMemoryKind; type MemoryKind = MiriMemoryKind;
type ExtraFnVal = Dlsym; type ExtraFnVal = Dlsym;
type FrameExtra = FrameData<'tcx>; type FrameExtra = FrameExtra<'tcx>;
type AllocExtra = AllocExtra; type AllocExtra = AllocExtra;
type Provenance = Provenance; type Provenance = Provenance;
@ -900,25 +901,24 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
} }
let alloc = alloc.into_owned(); let alloc = alloc.into_owned();
let stacks = ecx.machine.stacked_borrows.as_ref().map(|stacked_borrows| { let borrow_tracker = ecx
Stacks::new_allocation(id, alloc.size(), stacked_borrows, kind, &ecx.machine) .machine
}); .borrow_tracker
.as_ref()
.map(|bt| bt.borrow_mut().new_allocation(id, alloc.size(), kind, &ecx.machine));
let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| { let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| {
data_race::AllocExtra::new_allocation( data_race::AllocState::new_allocation(
data_race, data_race,
&ecx.machine.threads, &ecx.machine.threads,
alloc.size(), alloc.size(),
kind, kind,
) )
}); });
let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocExtra::new_allocation); let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation);
let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx( let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx(
&ecx.tcx, &ecx.tcx,
AllocExtra { AllocExtra { borrow_tracker, data_race: race_alloc, weak_memory: buffer_alloc },
stacked_borrows: stacks.map(RefCell::new),
data_race: race_alloc,
weak_memory: buffer_alloc,
},
|ptr| ecx.global_base_pointer(ptr), |ptr| ecx.global_base_pointer(ptr),
)?; )?;
Ok(Cow::Owned(alloc)) Ok(Cow::Owned(alloc))
@ -942,14 +942,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
} }
} }
let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr); let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr);
let sb_tag = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows { let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
stacked_borrows.borrow_mut().base_ptr_tag(ptr.provenance, &ecx.machine) borrow_tracker.borrow_mut().base_ptr_tag(ptr.provenance, &ecx.machine)
} else { } else {
// Value does not matter, SB is disabled // Value does not matter, SB is disabled
SbTag::default() BorTag::default()
}; };
Pointer::new( Pointer::new(
Provenance::Concrete { alloc_id: ptr.provenance, sb: sb_tag }, Provenance::Concrete { alloc_id: ptr.provenance, tag },
Size::from_bytes(absolute_addr), Size::from_bytes(absolute_addr),
) )
} }
@ -967,8 +967,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
ptr: Pointer<Self::Provenance>, ptr: Pointer<Self::Provenance>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
match ptr.provenance { match ptr.provenance {
Provenance::Concrete { alloc_id, sb } => Provenance::Concrete { alloc_id, tag } =>
intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb), intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, tag),
Provenance::Wildcard => { Provenance::Wildcard => {
// No need to do anything for wildcard pointers as // No need to do anything for wildcard pointers as
// their provenances have already been previously exposed. // their provenances have already been previously exposed.
@ -986,11 +986,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr); let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr);
rel.map(|(alloc_id, size)| { rel.map(|(alloc_id, size)| {
let sb = match ptr.provenance { let tag = match ptr.provenance {
Provenance::Concrete { sb, .. } => ProvenanceExtra::Concrete(sb), Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
Provenance::Wildcard => ProvenanceExtra::Wildcard, Provenance::Wildcard => ProvenanceExtra::Wildcard,
}; };
(alloc_id, size, sb) (alloc_id, size, tag)
}) })
} }
@ -1005,10 +1005,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
if let Some(data_race) = &alloc_extra.data_race { if let Some(data_race) = &alloc_extra.data_race {
data_race.read(alloc_id, range, machine)?; data_race.read(alloc_id, range, machine)?;
} }
if let Some(stacked_borrows) = &alloc_extra.stacked_borrows { if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
stacked_borrows borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
.borrow_mut()
.before_memory_read(alloc_id, prov_extra, range, machine)?;
} }
if let Some(weak_memory) = &alloc_extra.weak_memory { if let Some(weak_memory) = &alloc_extra.weak_memory {
weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap()); weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
@ -1027,8 +1025,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
if let Some(data_race) = &mut alloc_extra.data_race { if let Some(data_race) = &mut alloc_extra.data_race {
data_race.write(alloc_id, range, machine)?; data_race.write(alloc_id, range, machine)?;
} }
if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows { if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
stacked_borrows.get_mut().before_memory_write(alloc_id, prov_extra, range, machine)?; borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
} }
if let Some(weak_memory) = &alloc_extra.weak_memory { if let Some(weak_memory) = &alloc_extra.weak_memory {
weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap()); weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
@ -1050,16 +1048,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
if let Some(data_race) = &mut alloc_extra.data_race { if let Some(data_race) = &mut alloc_extra.data_race {
data_race.deallocate(alloc_id, range, machine)?; data_race.deallocate(alloc_id, range, machine)?;
} }
if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows { if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
stacked_borrows.get_mut().before_memory_deallocation( borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, range, machine)?;
alloc_id,
prove_extra,
range,
machine,
)
} else {
Ok(())
} }
Ok(())
} }
#[inline(always)] #[inline(always)]
@ -1068,14 +1060,17 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
kind: mir::RetagKind, kind: mir::RetagKind,
place: &PlaceTy<'tcx, Provenance>, place: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
if ecx.machine.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) } if ecx.machine.borrow_tracker.is_some() {
ecx.retag(kind, place)?;
}
Ok(())
} }
#[inline(always)] #[inline(always)]
fn init_frame_extra( fn init_frame_extra(
ecx: &mut InterpCx<'mir, 'tcx, Self>, ecx: &mut InterpCx<'mir, 'tcx, Self>,
frame: Frame<'mir, 'tcx, Provenance>, frame: Frame<'mir, 'tcx, Provenance>,
) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> { ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
// Start recording our event before doing anything else // Start recording our event before doing anything else
let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() { let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
let fn_name = frame.instance.to_string(); let fn_name = frame.instance.to_string();
@ -1091,10 +1086,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
None None
}; };
let stacked_borrows = ecx.machine.stacked_borrows.as_ref(); let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
let extra = FrameData { let extra = FrameExtra {
stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame(&ecx.machine)), borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame(&ecx.machine)),
catch_unwind: None, catch_unwind: None,
timing, timing,
is_user_relevant: ecx.machine.is_user_relevant(&frame), is_user_relevant: ecx.machine.is_user_relevant(&frame),
@ -1127,7 +1122,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
} }
} }
// Search for SbTags to find all live pointers, then remove all other tags from borrow // Search for BorTags to find all live pointers, then remove all other tags from borrow
// stacks. // stacks.
// When debug assertions are enabled, run the GC as often as possible so that any cases // When debug assertions are enabled, run the GC as often as possible so that any cases
// where it mistakenly removes an important tag become visible. // where it mistakenly removes an important tag become visible.
@ -1153,14 +1148,16 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
let stack_len = ecx.active_thread_stack().len(); let stack_len = ecx.active_thread_stack().len();
ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1); ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
} }
if ecx.machine.borrow_tracker.is_some() {
if ecx.machine.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) } ecx.retag_return_place()?;
}
Ok(())
} }
#[inline(always)] #[inline(always)]
fn after_stack_pop( fn after_stack_pop(
ecx: &mut InterpCx<'mir, 'tcx, Self>, ecx: &mut InterpCx<'mir, 'tcx, Self>,
mut frame: Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>, mut frame: Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>,
unwinding: bool, unwinding: bool,
) -> InterpResult<'tcx, StackPopJump> { ) -> InterpResult<'tcx, StackPopJump> {
if frame.extra.is_user_relevant { if frame.extra.is_user_relevant {
@ -1171,8 +1168,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
ecx.active_thread_mut().recompute_top_user_relevant_frame(); ecx.active_thread_mut().recompute_top_user_relevant_frame();
} }
let timing = frame.extra.timing.take(); let timing = frame.extra.timing.take();
if let Some(stacked_borrows) = &ecx.machine.stacked_borrows { if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
stacked_borrows.borrow_mut().end_call(&frame.extra); borrow_tracker.borrow_mut().end_call(&frame.extra);
} }
let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding); let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
if let Some(profiler) = ecx.machine.profiler.as_ref() { if let Some(profiler) = ecx.machine.profiler.as_ref() {

View file

@ -37,7 +37,7 @@ pub struct EnvVars<'tcx> {
} }
impl VisitTags for EnvVars<'_> { impl VisitTags for EnvVars<'_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let EnvVars { map, environ } = self; let EnvVars { map, environ } = self;
environ.visit_tags(visit); environ.visit_tags(visit);

Some files were not shown because too many files have changed in this diff Show more