diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 293e65ce87265..2c176828c841f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -3,11 +3,12 @@ //! Note that HIR pretty printing is layered on top of this crate. mod expr; +mod fixup; mod item; use crate::pp::Breaks::{Consistent, Inconsistent}; use crate::pp::{self, Breaks}; -use crate::pprust::state::expr::FixupContext; +use crate::pprust::state::fixup::FixupContext; use ast::TraitBoundModifiers; use rustc_ast::attr::AttrIdGenerator; use rustc_ast::ptr::P; @@ -15,7 +16,6 @@ use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, To use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; -use rustc_ast::util::parser; use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind}; use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term}; use rustc_ast::{GenericArg, GenericBound, SelfKind}; @@ -1252,22 +1252,14 @@ impl<'a> State<'a> { ast::StmtKind::Item(item) => self.print_item(item), ast::StmtKind::Expr(expr) => { self.space_if_not_bol(); - self.print_expr_outer_attr_style( - expr, - false, - FixupContext { stmt: true, ..FixupContext::default() }, - ); + self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt()); if classify::expr_requires_semi_to_be_stmt(expr) { self.word(";"); } } ast::StmtKind::Semi(expr) => { self.space_if_not_bol(); - self.print_expr_outer_attr_style( - expr, - false, - FixupContext { stmt: true, ..FixupContext::default() }, - ); + self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt()); self.word(";"); } ast::StmtKind::Empty => { @@ -1319,11 +1311,7 @@ impl<'a> State<'a> { ast::StmtKind::Expr(expr) if i == blk.stmts.len() - 1 => { self.maybe_print_comment(st.span.lo()); self.space_if_not_bol(); - self.print_expr_outer_attr_style( - expr, - false, - FixupContext { stmt: true, ..FixupContext::default() }, - ); + self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt()); self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi())); } _ => self.print_stmt(st), @@ -1367,8 +1355,7 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr_cond_paren( expr, - fixup.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr) - || parser::needs_par_as_let_scrutinee(expr.precedence().order()), + fixup.needs_par_as_let_scrutinee(expr), FixupContext::default(), ); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 6eff70410cbbb..4cbdc9f256dfe 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -1,10 +1,10 @@ use crate::pp::Breaks::Inconsistent; +use crate::pprust::state::fixup::FixupContext; use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; use ast::{ForLoopKind, MatchKind}; use itertools::{Itertools, Position}; use rustc_ast::ptr::P; use rustc_ast::token; -use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::parser::{self, AssocOp, Fixity}; use rustc_ast::{self as ast, BlockCheckMode}; @@ -14,78 +14,6 @@ use rustc_ast::{ }; use std::fmt::Write; -#[derive(Copy, Clone, Debug)] -pub(crate) struct FixupContext { - /// Print expression such that it can be parsed back as a statement - /// consisting of the original expression. - /// - /// The effect of this is for binary operators in statement position to set - /// `leftmost_subexpression_in_stmt` when printing their left-hand operand. - /// - /// ```ignore (illustrative) - /// (match x {}) - 1; // match needs parens when LHS of binary operator - /// - /// match x {}; // not when its own statement - /// ``` - pub stmt: bool, - - /// This is the difference between: - /// - /// ```ignore (illustrative) - /// (match x {}) - 1; // subexpression needs parens - /// - /// let _ = match x {} - 1; // no parens - /// ``` - /// - /// There are 3 distinguishable contexts in which `print_expr` might be - /// called with the expression `$match` as its argument, where `$match` - /// represents an expression of kind `ExprKind::Match`: - /// - /// - stmt=false leftmost_subexpression_in_stmt=false - /// - /// Example: `let _ = $match - 1;` - /// - /// No parentheses required. - /// - /// - stmt=false leftmost_subexpression_in_stmt=true - /// - /// Example: `$match - 1;` - /// - /// Must parenthesize `($match)`, otherwise parsing back the output as a - /// statement would terminate the statement after the closing brace of - /// the match, parsing `-1;` as a separate statement. - /// - /// - stmt=true leftmost_subexpression_in_stmt=false - /// - /// Example: `$match;` - /// - /// No parentheses required. - pub leftmost_subexpression_in_stmt: bool, - - /// This is the difference between: - /// - /// ```ignore (illustrative) - /// if let _ = (Struct {}) {} // needs parens - /// - /// match () { - /// () if let _ = Struct {} => {} // no parens - /// } - /// ``` - pub parenthesize_exterior_struct_lit: bool, -} - -/// The default amount of fixing is minimal fixing. Fixups should be turned on -/// in a targeted fashion where needed. -impl Default for FixupContext { - fn default() -> Self { - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - parenthesize_exterior_struct_lit: false, - } - } -} - impl<'a> State<'a> { fn print_else(&mut self, els: Option<&ast::Expr>) { if let Some(_else) = els { @@ -136,9 +64,7 @@ impl<'a> State<'a> { /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in /// `if cond { ... }`. fn print_expr_as_cond(&mut self, expr: &ast::Expr) { - let fixup = - FixupContext { parenthesize_exterior_struct_lit: true, ..FixupContext::default() }; - self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), fixup) + self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), FixupContext::new_cond()) } /// Does `expr` need parentheses when printed in a condition position? @@ -310,15 +236,7 @@ impl<'a> State<'a> { // because the latter is valid syntax but with the incorrect meaning. // It's a match-expression followed by tuple-expression, not a function // call. - self.print_expr_maybe_paren( - func, - prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(func, prec, fixup.leftmost_subexpression()); self.print_call_post(args) } @@ -387,33 +305,17 @@ impl<'a> State<'a> { _ => left_prec, }; - self.print_expr_maybe_paren( - lhs, - left_prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(lhs, left_prec, fixup.leftmost_subexpression()); self.space(); self.word_space(op.node.as_str()); - self.print_expr_maybe_paren( - rhs, - right_prec, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(rhs, right_prec, fixup.subsequent_subexpression()); } fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) { self.word(op.as_str()); - self.print_expr_maybe_paren( - expr, - parser::PREC_PREFIX, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression()); } fn print_expr_addr_of( @@ -431,11 +333,7 @@ impl<'a> State<'a> { self.print_mutability(mutability, true); } } - self.print_expr_maybe_paren( - expr, - parser::PREC_PREFIX, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression()); } pub(super) fn print_expr(&mut self, expr: &ast::Expr, fixup: FixupContext) { @@ -470,8 +368,7 @@ impl<'a> State<'a> { // // Same applies to a small set of other expression kinds which eagerly // terminate a statement which opens with them. - let needs_par = - fixup.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr); + let needs_par = fixup.would_cause_statement_boundary(expr); if needs_par { self.popen(); fixup = FixupContext::default(); @@ -519,16 +416,7 @@ impl<'a> State<'a> { } ast::ExprKind::Cast(expr, ty) => { let prec = AssocOp::As.precedence() as i8; - self.print_expr_maybe_paren( - expr, - prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(expr, prec, fixup.leftmost_subexpression()); self.space(); self.word_space("as"); self.print_type(ty); @@ -660,70 +548,34 @@ impl<'a> State<'a> { self.print_block_with_attrs(blk, attrs); } ast::ExprKind::Await(expr, _) => { - // Same fixups as ExprKind::MethodCall. self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup); self.word(".await"); } ast::ExprKind::Assign(lhs, rhs, _) => { - // Same fixups as ExprKind::Binary. let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren( - lhs, - prec + 1, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression()); self.space(); self.word_space("="); - self.print_expr_maybe_paren( - rhs, - prec, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression()); } ast::ExprKind::AssignOp(op, lhs, rhs) => { - // Same fixups as ExprKind::Binary. let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren( - lhs, - prec + 1, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression()); self.space(); self.word(op.node.as_str()); self.word_space("="); - self.print_expr_maybe_paren( - rhs, - prec, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression()); } ast::ExprKind::Field(expr, ident) => { - // Same fixups as ExprKind::MethodCall. self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup); self.word("."); self.print_ident(*ident); } ast::ExprKind::Index(expr, index, _) => { - // Same fixups as ExprKind::Call. self.print_expr_maybe_paren( expr, parser::PREC_POSTFIX, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, + fixup.leftmost_subexpression(), ); self.word("["); self.print_expr(index, FixupContext::default()); @@ -736,31 +588,14 @@ impl<'a> State<'a> { // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) let fake_prec = AssocOp::LOr.precedence() as i8; if let Some(e) = start { - self.print_expr_maybe_paren( - e, - fake_prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(e, fake_prec, fixup.leftmost_subexpression()); } match limits { ast::RangeLimits::HalfOpen => self.word(".."), ast::RangeLimits::Closed => self.word("..="), } if let Some(e) = end { - self.print_expr_maybe_paren( - e, - fake_prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, - ); + self.print_expr_maybe_paren(e, fake_prec, fixup.subsequent_subexpression()); } } ast::ExprKind::Underscore => self.word("_"), @@ -777,11 +612,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( expr, parser::PREC_JUMP, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, + fixup.subsequent_subexpression(), ); } } @@ -799,11 +630,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( expr, parser::PREC_JUMP, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, + fixup.subsequent_subexpression(), ); } } @@ -816,11 +643,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( expr, parser::PREC_JUMP, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, + fixup.subsequent_subexpression(), ); } } @@ -830,7 +653,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( result, parser::PREC_JUMP, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, + fixup.subsequent_subexpression(), ); } ast::ExprKind::InlineAsm(a) => { @@ -884,16 +707,11 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( expr, parser::PREC_JUMP, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, + fixup.subsequent_subexpression(), ); } } ast::ExprKind::Try(e) => { - // Same fixups as ExprKind::MethodCall. self.print_expr_maybe_paren(e, parser::PREC_POSTFIX, fixup); self.word("?") } @@ -961,7 +779,7 @@ impl<'a> State<'a> { } _ => { self.end(); // Close the ibox for the pattern. - self.print_expr(body, FixupContext { stmt: true, ..FixupContext::default() }); + self.print_expr(body, FixupContext::new_stmt()); self.word(","); } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs new file mode 100644 index 0000000000000..d21cb82f83b28 --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs @@ -0,0 +1,149 @@ +use rustc_ast::util::{classify, parser}; +use rustc_ast::Expr; + +#[derive(Copy, Clone, Debug)] +pub(crate) struct FixupContext { + /// Print expression such that it can be parsed back as a statement + /// consisting of the original expression. + /// + /// The effect of this is for binary operators in statement position to set + /// `leftmost_subexpression_in_stmt` when printing their left-hand operand. + /// + /// ```ignore (illustrative) + /// (match x {}) - 1; // match needs parens when LHS of binary operator + /// + /// match x {}; // not when its own statement + /// ``` + stmt: bool, + + /// This is the difference between: + /// + /// ```ignore (illustrative) + /// (match x {}) - 1; // subexpression needs parens + /// + /// let _ = match x {} - 1; // no parens + /// ``` + /// + /// There are 3 distinguishable contexts in which `print_expr` might be + /// called with the expression `$match` as its argument, where `$match` + /// represents an expression of kind `ExprKind::Match`: + /// + /// - stmt=false leftmost_subexpression_in_stmt=false + /// + /// Example: `let _ = $match - 1;` + /// + /// No parentheses required. + /// + /// - stmt=false leftmost_subexpression_in_stmt=true + /// + /// Example: `$match - 1;` + /// + /// Must parenthesize `($match)`, otherwise parsing back the output as a + /// statement would terminate the statement after the closing brace of + /// the match, parsing `-1;` as a separate statement. + /// + /// - stmt=true leftmost_subexpression_in_stmt=false + /// + /// Example: `$match;` + /// + /// No parentheses required. + leftmost_subexpression_in_stmt: bool, + + /// This is the difference between: + /// + /// ```ignore (illustrative) + /// if let _ = (Struct {}) {} // needs parens + /// + /// match () { + /// () if let _ = Struct {} => {} // no parens + /// } + /// ``` + parenthesize_exterior_struct_lit: bool, +} + +/// The default amount of fixing is minimal fixing. Fixups should be turned on +/// in a targeted fashion where needed. +impl Default for FixupContext { + fn default() -> Self { + FixupContext { + stmt: false, + leftmost_subexpression_in_stmt: false, + parenthesize_exterior_struct_lit: false, + } + } +} + +impl FixupContext { + /// Create the initial fixup for printing an expression in statement + /// position. + /// + /// This is currently also used for printing an expression as a match-arm, + /// but this is incorrect and leads to over-parenthesizing. + pub fn new_stmt() -> Self { + FixupContext { stmt: true, ..FixupContext::default() } + } + + /// Create the initial fixup for printing an expression as the "condition" + /// of an `if` or `while`. There are a few other positions which are + /// grammatically equivalent and also use this, such as the iterator + /// expression in `for` and the scrutinee in `match`. + pub fn new_cond() -> Self { + FixupContext { parenthesize_exterior_struct_lit: true, ..FixupContext::default() } + } + + /// Transform this fixup into the one that should apply when printing the + /// leftmost subexpression of the current expression. + /// + /// The leftmost subexpression is any subexpression that has the same first + /// token as the current expression, but has a different last token. + /// + /// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a + /// leftmost subexpression. + /// + /// Not every expression has a leftmost subexpression. For example neither + /// `-$a` nor `[$a]` have one. + pub fn leftmost_subexpression(self) -> Self { + FixupContext { + stmt: false, + leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt, + ..self + } + } + + /// Transform this fixup into the one that should apply when printing any + /// subexpression that is neither a leftmost subexpression nor surrounded in + /// delimiters. + /// + /// This is for any subexpression that has a different first token than the + /// current expression, and is not surrounded by a paren/bracket/brace. For + /// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or + /// `$a.f($b)`. + pub fn subsequent_subexpression(self) -> Self { + FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..self } + } + + /// Determine whether parentheses are needed around the given expression to + /// head off an unintended statement boundary. + /// + /// The documentation on `FixupContext::leftmost_subexpression_in_stmt` has + /// examples. + pub fn would_cause_statement_boundary(self, expr: &Expr) -> bool { + self.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr) + } + + /// Determine whether parentheses are needed around the given `let` + /// scrutinee. + /// + /// In `if let _ = $e {}`, some examples of `$e` that would need parentheses + /// are: + /// + /// - `Struct {}.f()`, because otherwise the `{` would be misinterpreted + /// as the opening of the if's then-block. + /// + /// - `true && false`, because otherwise this would be misinterpreted as a + /// "let chain". + pub fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool { + self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr) + || parser::needs_par_as_let_scrutinee(expr.precedence().order()) + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 13f27c1c95c2e..10886aace53f2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -1,5 +1,5 @@ use crate::pp::Breaks::Inconsistent; -use crate::pprust::state::expr::FixupContext; +use crate::pprust::state::fixup::FixupContext; use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; use ast::StaticItem; diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 8b0b9123ac7df..efa4be7e15ac3 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,4 +1,13 @@ -#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)] +#![feature( + no_core, + lang_items, + never_type, + linkage, + extern_types, + thread_local, + repr_simd, + raw_ref_op +)] #![no_core] #![allow(dead_code, non_camel_case_types, internal_features)] @@ -112,9 +121,7 @@ fn start( static mut NUM: u8 = 6 * 7; -// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint -#[allow(static_mut_refs)] -static NUM_REF: &'static u8 = unsafe { &NUM }; +static NUM_REF: &'static u8 = unsafe { &*&raw const NUM }; unsafe fn zeroed() -> T { let mut uninit = MaybeUninit { uninit: () }; diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index add77880716c8..5a7ddc4cd7fa5 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -2,7 +2,7 @@ #![feature( no_core, unboxed_closures, start, lang_items, never_type, linkage, - extern_types, thread_local + extern_types, thread_local, raw_ref_op )] #![no_core] #![allow(dead_code, internal_features, non_camel_case_types)] @@ -99,9 +99,7 @@ fn start( static mut NUM: u8 = 6 * 7; -// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint -#[allow(static_mut_refs)] -static NUM_REF: &'static u8 = unsafe { &NUM }; +static NUM_REF: &'static u8 = unsafe { &* &raw const NUM }; macro_rules! assert { ($e:expr) => { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5d345e788e94e..a17e0da47a5cf 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -730,7 +730,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } } #[rustc_lint_diagnostics] - fn highlighted_note(&mut self, msg: Vec) -> &mut Self { + pub fn highlighted_note(&mut self, msg: Vec) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 29c9f08a1660d..fdae9d84b5ff6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -61,7 +61,7 @@ use crate::traits::{ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, - ErrorGuaranteed, IntoDiagArg, + ErrorGuaranteed, IntoDiagArg, StringPart, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -1917,6 +1917,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); if !is_simple_error || terr.must_include_note() { diag.note_expected_found(&expected_label, expected, &found_label, found); + + if let Some(ty::Closure(_, args)) = + exp_found.map(|expected_type_found| expected_type_found.found.kind()) + { + diag.highlighted_note(vec![ + StringPart::normal("closure has signature: `"), + StringPart::highlighted( + self.tcx + .signature_unclosure( + args.as_closure().sig(), + rustc_hir::Unsafety::Normal, + ) + .to_string(), + ), + StringPart::normal("`"), + ]); + } } } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2a9de49962277..565bdc3af03dd 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1522,6 +1522,7 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { delete Bundle; } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, OperandBundleDef **OpBundlesIndirect, @@ -1601,6 +1602,7 @@ extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, @@ -1623,6 +1625,7 @@ LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, Name)); } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMBasicBlockRef DefaultDest, diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index e8e23f2a7ecc5..bda1ee6f45712 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -537,7 +537,11 @@ impl () {} /// ## 4. Get it from C. /// /// ``` -/// # #![feature(rustc_private)] +/// # mod libc { +/// # pub unsafe fn malloc(_size: usize) -> *mut core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() } +/// # pub unsafe fn free(_ptr: *mut core::ffi::c_void) {} +/// # } +/// # #[cfg(any())] /// #[allow(unused_extern_crates)] /// extern crate libc; /// @@ -548,7 +552,7 @@ impl () {} /// if my_num.is_null() { /// panic!("failed to allocate memory"); /// } -/// libc::free(my_num as *mut libc::c_void); +/// libc::free(my_num as *mut core::ffi::c_void); /// } /// ``` /// diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 1293abddaf364..409ead0e28433 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -214,7 +214,7 @@ pub struct Permissions(fs_imp::FilePermissions); /// A structure representing a type of file with accessors for each file type. /// It is returned by [`Metadata::file_type`] method. #[stable(feature = "file_type", since = "1.1.0")] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(not(test), rustc_diagnostic_item = "FileType")] pub struct FileType(fs_imp::FileType); @@ -1410,15 +1410,20 @@ impl Metadata { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Metadata { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Metadata") - .field("file_type", &self.file_type()) - .field("is_dir", &self.is_dir()) - .field("is_file", &self.is_file()) - .field("permissions", &self.permissions()) - .field("modified", &self.modified()) - .field("accessed", &self.accessed()) - .field("created", &self.created()) - .finish_non_exhaustive() + let mut debug = f.debug_struct("Metadata"); + debug.field("file_type", &self.file_type()); + debug.field("permissions", &self.permissions()); + debug.field("len", &self.len()); + if let Ok(modified) = self.modified() { + debug.field("modified", &modified); + } + if let Ok(accessed) = self.accessed() { + debug.field("accessed", &accessed); + } + if let Ok(created) = self.created() { + debug.field("created", &created); + } + debug.finish_non_exhaustive() } } @@ -1684,6 +1689,17 @@ impl FileType { } } +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FileType") + .field("is_file", &self.is_file()) + .field("is_dir", &self.is_dir()) + .field("is_symlink", &self.is_symlink()) + .finish_non_exhaustive() + } +} + impl AsInner for FileType { #[inline] fn as_inner(&self) -> &fs_imp::FileType { diff --git a/library/std/src/sys/thread_local/static_local.rs b/library/std/src/sys/thread_local/static_local.rs index 206e62bb5e2c8..162c3fbd97a0e 100644 --- a/library/std/src/sys/thread_local/static_local.rs +++ b/library/std/src/sys/thread_local/static_local.rs @@ -11,8 +11,6 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ #[inline] // see comments below #[deny(unsafe_op_in_unsafe_fn)] - // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint - #[allow(static_mut_refs)] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { @@ -25,7 +23,8 @@ pub macro thread_local_inner { // FIXME(#84224) this should come after the `target_thread_local` // block. static mut VAL: $t = INIT_EXPR; - unsafe { $crate::option::Option::Some(&VAL) } + // SAFETY: we only ever create shared references, so there's no mutable aliasing. + unsafe { $crate::option::Option::Some(&*$crate::ptr::addr_of!(VAL)) } } unsafe { diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 392a5a1196761..c223b9f43240f 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -464,7 +464,8 @@ impl Builder { | PkgType::LlvmTools | PkgType::RustAnalysis | PkgType::JsonDocs - | PkgType::RustcCodegenCranelift => { + | PkgType::RustcCodegenCranelift + | PkgType::LlvmBitcodeLinker => { extensions.push(host_component(pkg)); } PkgType::RustcDev | PkgType::RustcDocs => { diff --git a/src/tools/build-manifest/src/versions.rs b/src/tools/build-manifest/src/versions.rs index e4cdf965eb609..233a2670ed228 100644 --- a/src/tools/build-manifest/src/versions.rs +++ b/src/tools/build-manifest/src/versions.rs @@ -58,6 +58,7 @@ pkg_type! { Miri = "miri"; preview = true, JsonDocs = "rust-docs-json"; preview = true, RustcCodegenCranelift = "rustc-codegen-cranelift"; preview = true, + LlvmBitcodeLinker = "llvm-bitcode-linker"; preview = true, } impl PkgType { @@ -94,6 +95,7 @@ impl PkgType { PkgType::ReproducibleArtifacts => true, PkgType::RustMingw => true, PkgType::RustAnalysis => true, + PkgType::LlvmBitcodeLinker => true, } } @@ -121,6 +123,7 @@ impl PkgType { Rustfmt => HOSTS, RustAnalysis => TARGETS, LlvmTools => TARGETS, + LlvmBitcodeLinker => HOSTS, } } diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index f5d7ce1c5f9a0..8aafbb3e39926 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -244,7 +244,7 @@ mod directives { pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth"; pub const INCREMENTAL: &'static str = "incremental"; pub const KNOWN_BUG: &'static str = "known-bug"; - pub const MIR_UNIT_TEST: &'static str = "unit-test"; + pub const TEST_MIR_PASS: &'static str = "test-mir-pass"; pub const REMAP_SRC_BASE: &'static str = "remap-src-base"; pub const COMPARE_OUTPUT_LINES_BY_SUBSET: &'static str = "compare-output-lines-by-subset"; pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags"; @@ -549,7 +549,7 @@ impl TestProps { config.set_name_value_directive( ln, - MIR_UNIT_TEST, + TEST_MIR_PASS, &mut self.mir_unit_test, |s| s.trim().to_string(), ); @@ -922,7 +922,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "should-fail", "should-ice", "stderr-per-bitwidth", - "unit-test", + "test-mir-pass", "unset-exec-env", "unset-rustc-env", // tidy-alphabetical-end diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index 2566124a0375c..aca780e33f0ea 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -7,6 +7,43 @@ use walkdir::WalkDir; mod groups; +/// List of lints which have been renamed. +/// +/// These will get redirects in the output to the new name. The +/// format is `(level, [(old_name, new_name), ...])`. +/// +/// Note: This hard-coded list is a temporary hack. The intent is in the +/// future to have `rustc` expose this information in some way (like a `-Z` +/// flag spitting out JSON). Also, this does not yet support changing the +/// level of the lint, which will be more difficult to support, since rustc +/// currently does not track that historical information. +static RENAMES: &[(Level, &[(&str, &str)])] = &[ + ( + Level::Allow, + &[ + ("single-use-lifetime", "single-use-lifetimes"), + ("elided-lifetime-in-path", "elided-lifetimes-in-paths"), + ("async-idents", "keyword-idents"), + ("disjoint-capture-migration", "rust-2021-incompatible-closure-captures"), + ("or-patterns-back-compat", "rust-2021-incompatible-or-patterns"), + ], + ), + ( + Level::Warn, + &[ + ("bare-trait-object", "bare-trait-objects"), + ("unstable-name-collision", "unstable-name-collisions"), + ("unused-doc-comment", "unused-doc-comments"), + ("redundant-semicolon", "redundant-semicolons"), + ("overlapping-patterns", "overlapping-range-endpoints"), + ("non-fmt-panic", "non-fmt-panics"), + ("unused-tuple-struct-fields", "dead-code"), + ("static-mut-ref", "static-mut-refs"), + ], + ), + (Level::Deny, &[("exceeding-bitshifts", "arithmetic-overflow")]), +]; + pub struct LintExtractor<'a> { /// Path to the `src` directory, where it will scan for `.rs` files to /// find lint declarations. @@ -126,6 +163,7 @@ impl<'a> LintExtractor<'a> { ) })?; } + add_renamed_lints(&mut lints); self.save_lints_markdown(&lints)?; self.generate_group_docs(&lints)?; Ok(()) @@ -482,6 +520,7 @@ impl<'a> LintExtractor<'a> { } result.push('\n'); } + add_rename_redirect(level, &mut result); let out_path = self.out_path.join("listing").join(level.doc_filename()); // Delete the output because rustbuild uses hard links in its copies. let _ = fs::remove_file(&out_path); @@ -491,6 +530,56 @@ impl<'a> LintExtractor<'a> { } } +/// Adds `Lint`s that have been renamed. +fn add_renamed_lints(lints: &mut Vec) { + for (level, names) in RENAMES { + for (from, to) in *names { + lints.push(Lint { + name: from.to_string(), + doc: vec![format!("The lint `{from}` has been renamed to [`{to}`](#{to}).")], + level: *level, + path: PathBuf::new(), + lineno: 0, + }); + } + } +} + +// This uses DOMContentLoaded instead of running immediately because for some +// reason on Firefox (124 of this writing) doesn't update the `target` CSS +// selector if only the hash changes. +static RENAME_START: &str = " + +"; + +/// Adds the javascript redirection code to the given markdown output. +fn add_rename_redirect(level: Level, output: &mut String) { + for (rename_level, names) in RENAMES { + if *rename_level == level { + let filename = level.doc_filename().replace(".md", ".html"); + output.push_str(RENAME_START); + for (from, to) in *names { + write!(output, " \"#{from}\": \"{filename}#{to}\",\n").unwrap(); + } + output.push_str(RENAME_END); + } + } +} + /// Extracts the lint name (removing the visibility modifier, and checking validity). fn lint_name(line: &str) -> Result { // Skip over any potential `pub` visibility. diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 948f1ee6c6377..4254b9bb67dba 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -295,6 +295,16 @@ up the sysroot. If you are using `miri` (the Miri driver) directly, see the Miri adds its own set of `-Z` flags, which are usually set via the `MIRIFLAGS` environment variable. We first document the most relevant and most commonly used flags: +* `-Zmiri-address-reuse-rate=` changes the probability that a freed *non-stack* allocation + will be added to the pool for address reuse, and the probability that a new *non-stack* allocation + will be taken from the pool. Stack allocations never get added to or taken from the pool. The + default is `0.5`. +* `-Zmiri-address-reuse-cross-thread-rate=` changes the probability that an allocation which + attempts to reuse a previously freed block of memory will also consider blocks freed by *other + threads*. The default is `0.1`, which means by default, in 90% of the cases where an address reuse + attempt is made, only addresses from the same thread will be considered. Reusing an address from + another thread induces synchronization between those threads, which can mask data races and weak + memory bugs. * `-Zmiri-compare-exchange-weak-failure-rate=` changes the failure rate of `compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail). You can change it to any value between `0.0` and `1.0`, where `1.0` means it diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 6ad8fba723c89..a60acf44a401f 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -23d47dba319331d4418827cfbb8c1af283497d3c +c8d19a92aa9022eb690899cf6d54fd23cb6877e5 diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index b4983656adc10..2bbb34c9a4bf7 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -13,8 +13,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::Span; use rustc_target::abi::{Align, HasDataLayout, Size}; -use crate::*; -use reuse_pool::ReusePool; +use crate::{concurrency::VClock, *}; + +use self::reuse_pool::ReusePool; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ProvenanceMode { @@ -77,7 +78,7 @@ impl GlobalStateInner { GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), - reuse: ReusePool::new(), + reuse: ReusePool::new(config), exposed: FxHashSet::default(), next_base_addr: stack_addr, provenance_mode: config.provenance_mode, @@ -144,7 +145,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn addr_from_alloc_id( &self, alloc_id: AllocId, - _kind: MemoryKind, + memory_kind: MemoryKind, ) -> InterpResult<'tcx, u64> { let ecx = self.eval_context_ref(); let mut global_state = ecx.machine.alloc_addresses.borrow_mut(); @@ -163,9 +164,18 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert!(!matches!(kind, AllocKind::Dead)); // This allocation does not have a base address yet, pick or reuse one. - let base_addr = if let Some(reuse_addr) = - global_state.reuse.take_addr(&mut *rng, size, align) - { + let base_addr = if let Some((reuse_addr, clock)) = global_state.reuse.take_addr( + &mut *rng, + size, + align, + memory_kind, + ecx.get_active_thread(), + ) { + if let Some(clock) = clock + && let Some(data_race) = &ecx.machine.data_race + { + data_race.acquire_clock(&clock, ecx.get_active_thread()); + } reuse_addr } else { // We have to pick a fresh address. @@ -333,14 +343,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } -impl GlobalStateInner { - pub fn free_alloc_id( - &mut self, - rng: &mut impl Rng, - dead_id: AllocId, - size: Size, - align: Align, - ) { +impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { + pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) { + let global_state = self.alloc_addresses.get_mut(); + let rng = self.rng.get_mut(); + // We can *not* remove this from `base_addr`, since the interpreter design requires that we // be able to retrieve an AllocId + offset for any memory access *before* we check if the // access is valid. Specifically, `ptr_get_alloc` is called on each attempt at a memory @@ -353,15 +360,25 @@ impl GlobalStateInner { // returns a dead allocation. // To avoid a linear scan we first look up the address in `base_addr`, and then find it in // `int_to_ptr_map`. - let addr = *self.base_addr.get(&dead_id).unwrap(); - let pos = self.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap(); - let removed = self.int_to_ptr_map.remove(pos); + let addr = *global_state.base_addr.get(&dead_id).unwrap(); + let pos = + global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap(); + let removed = global_state.int_to_ptr_map.remove(pos); assert_eq!(removed, (addr, dead_id)); // double-check that we removed the right thing // We can also remove it from `exposed`, since this allocation can anyway not be returned by // `alloc_id_from_addr` any more. - self.exposed.remove(&dead_id); + global_state.exposed.remove(&dead_id); // Also remember this address for future reuse. - self.reuse.add_addr(rng, addr, size, align) + let thread = self.threads.get_active_thread_id(); + global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { + if let Some(data_race) = &self.data_race { + data_race + .release_clock(thread, self.threads.active_thread_ref().current_span()) + .clone() + } else { + VClock::default() + } + }) } } diff --git a/src/tools/miri/src/alloc_addresses/reuse_pool.rs b/src/tools/miri/src/alloc_addresses/reuse_pool.rs index 8374d0ec605a8..77fc9f53f9e37 100644 --- a/src/tools/miri/src/alloc_addresses/reuse_pool.rs +++ b/src/tools/miri/src/alloc_addresses/reuse_pool.rs @@ -4,11 +4,9 @@ use rand::Rng; use rustc_target::abi::{Align, Size}; -const MAX_POOL_SIZE: usize = 64; +use crate::{concurrency::VClock, MemoryKind, MiriConfig, ThreadId}; -// Just use fair coins, until we have evidence that other numbers are better. -const ADDR_REMEMBER_CHANCE: f64 = 0.5; -const ADDR_TAKE_CHANCE: f64 = 0.5; +const MAX_POOL_SIZE: usize = 64; /// The pool strikes a balance between exploring more possible executions and making it more likely /// to find bugs. The hypothesis is that bugs are more likely to occur when reuse happens for @@ -16,20 +14,29 @@ const ADDR_TAKE_CHANCE: f64 = 0.5; /// structure. Therefore we only reuse allocations when size and alignment match exactly. #[derive(Debug)] pub struct ReusePool { + address_reuse_rate: f64, + address_reuse_cross_thread_rate: f64, /// The i-th element in `pool` stores allocations of alignment `2^i`. We store these reusable - /// allocations as address-size pairs, the list must be sorted by the size. + /// allocations as address-size pairs, the list must be sorted by the size and then the thread ID. /// /// Each of these maps has at most MAX_POOL_SIZE elements, and since alignment is limited to /// less than 64 different possible value, that bounds the overall size of the pool. - pool: Vec>, + /// + /// We also store the ID and the data-race clock of the thread that donated this pool element, + /// to ensure synchronization with the thread that picks up this address. + pool: Vec>, } impl ReusePool { - pub fn new() -> Self { - ReusePool { pool: vec![] } + pub fn new(config: &MiriConfig) -> Self { + ReusePool { + address_reuse_rate: config.address_reuse_rate, + address_reuse_cross_thread_rate: config.address_reuse_cross_thread_rate, + pool: vec![], + } } - fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size)> { + fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size, ThreadId, VClock)> { let pool_idx: usize = align.bytes().trailing_zeros().try_into().unwrap(); if self.pool.len() <= pool_idx { self.pool.resize(pool_idx + 1, Vec::new()); @@ -37,40 +44,73 @@ impl ReusePool { &mut self.pool[pool_idx] } - pub fn add_addr(&mut self, rng: &mut impl Rng, addr: u64, size: Size, align: Align) { + pub fn add_addr( + &mut self, + rng: &mut impl Rng, + addr: u64, + size: Size, + align: Align, + kind: MemoryKind, + thread: ThreadId, + clock: impl FnOnce() -> VClock, + ) { // Let's see if we even want to remember this address. - if !rng.gen_bool(ADDR_REMEMBER_CHANCE) { + // We don't remember stack addresses: there's a lot of them (so the perf impact is big), + // and we only want to reuse stack slots within the same thread or else we'll add a lot of + // undesired synchronization. + if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { return; } + let clock = clock(); // Determine the pool to add this to, and where in the pool to put it. let subpool = self.subpool(align); - let pos = subpool.partition_point(|(_addr, other_size)| *other_size < size); + let pos = subpool.partition_point(|(_addr, other_size, other_thread, _)| { + (*other_size, *other_thread) < (size, thread) + }); // Make sure the pool does not grow too big. if subpool.len() >= MAX_POOL_SIZE { // Pool full. Replace existing element, or last one if this would be even bigger. let clamped_pos = pos.min(subpool.len() - 1); - subpool[clamped_pos] = (addr, size); + subpool[clamped_pos] = (addr, size, thread, clock); return; } // Add address to pool, at the right position. - subpool.insert(pos, (addr, size)); + subpool.insert(pos, (addr, size, thread, clock)); } - pub fn take_addr(&mut self, rng: &mut impl Rng, size: Size, align: Align) -> Option { - // Determine whether we'll even attempt a reuse. - if !rng.gen_bool(ADDR_TAKE_CHANCE) { + /// Returns the address to use and optionally a clock we have to synchronize with. + pub fn take_addr( + &mut self, + rng: &mut impl Rng, + size: Size, + align: Align, + kind: MemoryKind, + thread: ThreadId, + ) -> Option<(u64, Option)> { + // Determine whether we'll even attempt a reuse. As above, we don't do reuse for stack addresses. + if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { return None; } + let cross_thread_reuse = rng.gen_bool(self.address_reuse_cross_thread_rate); // Determine the pool to take this from. let subpool = self.subpool(align); // Let's see if we can find something of the right size. We want to find the full range of - // such items, beginning with the first, so we can't use `binary_search_by_key`. - let begin = subpool.partition_point(|(_addr, other_size)| *other_size < size); + // such items, beginning with the first, so we can't use `binary_search_by_key`. If we do + // *not* want to consider other thread's allocations, we effectively use the lexicographic + // order on `(size, thread)`. + let begin = subpool.partition_point(|(_addr, other_size, other_thread, _)| { + *other_size < size + || (*other_size == size && !cross_thread_reuse && *other_thread < thread) + }); let mut end = begin; - while let Some((_addr, other_size)) = subpool.get(end) { + while let Some((_addr, other_size, other_thread, _)) = subpool.get(end) { if *other_size != size { break; } + if !cross_thread_reuse && *other_thread != thread { + // We entered the allocations of another thread. + break; + } end += 1; } if end == begin { @@ -80,8 +120,10 @@ impl ReusePool { // Pick a random element with the desired size. let idx = rng.gen_range(begin..end); // Remove it from the pool and return. - let (chosen_addr, chosen_size) = subpool.remove(idx); + let (chosen_addr, chosen_size, chosen_thread, clock) = subpool.remove(idx); debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0); - Some(chosen_addr) + debug_assert!(cross_thread_reuse || chosen_thread == thread); + // No synchronization needed if we reused from the current thread. + Some((chosen_addr, if chosen_thread == thread { None } else { Some(clock) })) } } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 3f7a965e9df7d..db2cd01ce0bf6 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -307,6 +307,15 @@ fn parse_comma_list(input: &str) -> Result, T::Err> { input.split(',').map(str::parse::).collect() } +/// Parses the input as a float in the range from 0.0 to 1.0 (inclusive). +fn parse_rate(input: &str) -> Result { + match input.parse::() { + Ok(rate) if rate >= 0.0 && rate <= 1.0 => Ok(rate), + Ok(_) => Err("must be between `0.0` and `1.0`"), + Err(_) => Err("requires a `f64` between `0.0` and `1.0`"), + } +} + #[cfg(any(target_os = "linux", target_os = "macos"))] fn jemalloc_magic() { // These magic runes are copied from @@ -499,14 +508,9 @@ fn main() { } else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") { miri_config.forwarded_env_vars.push(param.to_owned()); } else if let Some(param) = arg.strip_prefix("-Zmiri-track-pointer-tag=") { - let ids: Vec = match parse_comma_list(param) { - Ok(ids) => ids, - Err(err) => - show_error!( - "-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {}", - err - ), - }; + let ids: Vec = parse_comma_list(param).unwrap_or_else(|err| { + show_error!("-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {err}") + }); for id in ids.into_iter().map(miri::BorTag::new) { if let Some(id) = id { miri_config.tracked_pointer_tags.insert(id); @@ -515,14 +519,9 @@ fn main() { } } } else if let Some(param) = arg.strip_prefix("-Zmiri-track-call-id=") { - let ids: Vec = match parse_comma_list(param) { - Ok(ids) => ids, - Err(err) => - show_error!( - "-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {}", - err - ), - }; + let ids: Vec = parse_comma_list(param).unwrap_or_else(|err| { + show_error!("-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {err}") + }); for id in ids.into_iter().map(miri::CallId::new) { if let Some(id) = id { miri_config.tracked_call_ids.insert(id); @@ -531,56 +530,37 @@ fn main() { } } } else if let Some(param) = arg.strip_prefix("-Zmiri-track-alloc-id=") { - let ids: Vec = match parse_comma_list::>(param) { - Ok(ids) => ids.into_iter().map(miri::AllocId).collect(), - Err(err) => - show_error!( - "-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {}", - err - ), - }; - miri_config.tracked_alloc_ids.extend(ids); + let ids = parse_comma_list::>(param).unwrap_or_else(|err| { + show_error!("-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {err}") + }); + miri_config.tracked_alloc_ids.extend(ids.into_iter().map(miri::AllocId)); } else if arg == "-Zmiri-track-alloc-accesses" { miri_config.track_alloc_accesses = true; + } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-rate=") { + miri_config.address_reuse_rate = parse_rate(param) + .unwrap_or_else(|err| show_error!("-Zmiri-address-reuse-rate {err}")); + } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-cross-thread-rate=") { + miri_config.address_reuse_cross_thread_rate = parse_rate(param) + .unwrap_or_else(|err| show_error!("-Zmiri-address-reuse-cross-thread-rate {err}")); } else if let Some(param) = arg.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=") { - let rate = match param.parse::() { - Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate, - Ok(_) => - show_error!( - "-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`" - ), - Err(err) => - show_error!( - "-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}", - err - ), - }; - miri_config.cmpxchg_weak_failure_rate = rate; + miri_config.cmpxchg_weak_failure_rate = parse_rate(param).unwrap_or_else(|err| { + show_error!("-Zmiri-compare-exchange-weak-failure-rate {err}") + }); } else if let Some(param) = arg.strip_prefix("-Zmiri-preemption-rate=") { - let rate = match param.parse::() { - Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate, - Ok(_) => show_error!("-Zmiri-preemption-rate must be between `0.0` and `1.0`"), - Err(err) => - show_error!( - "-Zmiri-preemption-rate requires a `f64` between `0.0` and `1.0`: {}", - err - ), - }; - miri_config.preemption_rate = rate; + miri_config.preemption_rate = + parse_rate(param).unwrap_or_else(|err| show_error!("-Zmiri-preemption-rate {err}")); } else if arg == "-Zmiri-report-progress" { // This makes it take a few seconds between progress reports on my laptop. miri_config.report_progress = Some(1_000_000); } else if let Some(param) = arg.strip_prefix("-Zmiri-report-progress=") { - let interval = match param.parse::() { - Ok(i) => i, - Err(err) => show_error!("-Zmiri-report-progress requires a `u32`: {}", err), - }; + let interval = param.parse::().unwrap_or_else(|err| { + show_error!("-Zmiri-report-progress requires a `u32`: {}", err) + }); miri_config.report_progress = Some(interval); } else if let Some(param) = arg.strip_prefix("-Zmiri-provenance-gc=") { - let interval = match param.parse::() { - Ok(i) => i, - Err(err) => show_error!("-Zmiri-provenance-gc requires a `u32`: {}", err), - }; + let interval = param.parse::().unwrap_or_else(|err| { + show_error!("-Zmiri-provenance-gc requires a `u32`: {}", err) + }); miri_config.gc_interval = interval; } else if let Some(param) = arg.strip_prefix("-Zmiri-measureme=") { miri_config.measureme_out = Some(param.to_string()); @@ -605,23 +585,20 @@ fn main() { show_error!("-Zmiri-extern-so-file `{}` does not exist", filename); } } else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") { - let num_cpus = match param.parse::() { - Ok(i) => i, - Err(err) => show_error!("-Zmiri-num-cpus requires a `u32`: {}", err), - }; - + let num_cpus = param + .parse::() + .unwrap_or_else(|err| show_error!("-Zmiri-num-cpus requires a `u32`: {}", err)); miri_config.num_cpus = num_cpus; } else if let Some(param) = arg.strip_prefix("-Zmiri-force-page-size=") { - let page_size = match param.parse::() { - Ok(i) => - if i.is_power_of_two() { - i * 1024 - } else { - show_error!("-Zmiri-force-page-size requires a power of 2: {}", i) - }, - Err(err) => show_error!("-Zmiri-force-page-size requires a `u64`: {}", err), + let page_size = param.parse::().unwrap_or_else(|err| { + show_error!("-Zmiri-force-page-size requires a `u64`: {}", err) + }); + // Convert from kilobytes to bytes. + let page_size = if page_size.is_power_of_two() { + page_size * 1024 + } else { + show_error!("-Zmiri-force-page-size requires a power of 2: {page_size}"); }; - miri_config.page_size = Some(page_size); } else { // Forward to rustc. diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index 6d4a5bd41b1eb..cb677b865311f 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -438,7 +438,7 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { .machine .threads .all_stacks() - .flatten() + .flat_map(|(_id, stack)| stack) .map(|frame| { frame.extra.borrow_tracker.as_ref().expect("we should have borrow tracking data") }) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/exhaustive.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/exhaustive.rs index daf3590358fd9..d50a22a91047d 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/exhaustive.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/exhaustive.rs @@ -2,7 +2,6 @@ //! (These are used in Tree Borrows `#[test]`s for thorough verification //! of the behavior of the state machine of permissions, //! but the contents of this file are extremely generic) -#![cfg(test)] pub trait Exhaustive: Sized { fn exhaustive() -> Box>; diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 95049b91cbad1..2281609a049d4 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -547,9 +547,9 @@ impl MemoryCellClocks { ) -> Result<(), DataRace> { trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks); if !current_span.is_dummy() { - thread_clocks.clock[index].span = current_span; + thread_clocks.clock.index_mut(index).span = current_span; } - thread_clocks.clock[index].set_read_type(read_type); + thread_clocks.clock.index_mut(index).set_read_type(read_type); if self.write_was_before(&thread_clocks.clock) { let race_free = if let Some(atomic) = self.atomic() { // We must be ordered-after all atomic accesses, reads and writes. @@ -577,7 +577,7 @@ impl MemoryCellClocks { ) -> Result<(), DataRace> { trace!("Unsynchronized write with vectors: {:#?} :: {:#?}", self, thread_clocks); if !current_span.is_dummy() { - thread_clocks.clock[index].span = current_span; + thread_clocks.clock.index_mut(index).span = current_span; } if self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock { let race_free = if let Some(atomic) = self.atomic() { @@ -1701,49 +1701,34 @@ impl GlobalState { format!("thread `{thread_name}`") } - /// Acquire a lock, express that the previous call of - /// `validate_lock_release` must happen before this. + /// Acquire the given clock into the given thread, establishing synchronization with + /// the moment when that clock snapshot was taken via `release_clock`. /// As this is an acquire operation, the thread timestamp is not /// incremented. - pub fn validate_lock_acquire(&self, lock: &VClock, thread: ThreadId) { - let (_, mut clocks) = self.load_thread_state_mut(thread); + pub fn acquire_clock(&self, lock: &VClock, thread: ThreadId) { + let (_, mut clocks) = self.thread_state_mut(thread); clocks.clock.join(lock); } - /// Release a lock handle, express that this happens-before - /// any subsequent calls to `validate_lock_acquire`. - /// For normal locks this should be equivalent to `validate_lock_release_shared` - /// since an acquire operation should have occurred before, however - /// for futex & condvar operations this is not the case and this - /// operation must be used. - pub fn validate_lock_release(&self, lock: &mut VClock, thread: ThreadId, current_span: Span) { - let (index, mut clocks) = self.load_thread_state_mut(thread); - lock.clone_from(&clocks.clock); - clocks.increment_clock(index, current_span); - } - - /// Release a lock handle, express that this happens-before - /// any subsequent calls to `validate_lock_acquire` as well - /// as any previous calls to this function after any - /// `validate_lock_release` calls. - /// For normal locks this should be equivalent to `validate_lock_release`. - /// This function only exists for joining over the set of concurrent readers - /// in a read-write lock and should not be used for anything else. - pub fn validate_lock_release_shared( - &self, - lock: &mut VClock, - thread: ThreadId, - current_span: Span, - ) { - let (index, mut clocks) = self.load_thread_state_mut(thread); - lock.join(&clocks.clock); + /// Returns the `release` clock of the given thread. + /// Other threads can acquire this clock in the future to establish synchronization + /// with this program point. + pub fn release_clock(&self, thread: ThreadId, current_span: Span) -> Ref<'_, VClock> { + // We increment the clock each time this happens, to ensure no two releases + // can be confused with each other. + let (index, mut clocks) = self.thread_state_mut(thread); clocks.increment_clock(index, current_span); + drop(clocks); + // To return a read-only view, we need to release the RefCell + // and borrow it again. + let (_index, clocks) = self.thread_state(thread); + Ref::map(clocks, |c| &c.clock) } /// Load the vector index used by the given thread as well as the set of vector clocks /// used by the thread. #[inline] - fn load_thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { + fn thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { let index = self.thread_info.borrow()[thread] .vector_index .expect("Loading thread state for thread with no assigned vector"); @@ -1752,6 +1737,18 @@ impl GlobalState { (index, clocks) } + /// Load the vector index used by the given thread as well as the set of vector clocks + /// used by the thread. + #[inline] + fn thread_state(&self, thread: ThreadId) -> (VectorIdx, Ref<'_, ThreadClockSet>) { + let index = self.thread_info.borrow()[thread] + .vector_index + .expect("Loading thread state for thread with no assigned vector"); + let ref_vector = self.vector_clocks.borrow(); + let clocks = Ref::map(ref_vector, |vec| &vec[index]); + (index, clocks) + } + /// Load the current vector clock in use and the current set of thread clocks /// in use for the vector. #[inline] @@ -1759,10 +1756,7 @@ impl GlobalState { &self, thread_mgr: &ThreadManager<'_, '_>, ) -> (VectorIdx, Ref<'_, ThreadClockSet>) { - let index = self.current_index(thread_mgr); - let ref_vector = self.vector_clocks.borrow(); - let clocks = Ref::map(ref_vector, |vec| &vec[index]); - (index, clocks) + self.thread_state(thread_mgr.get_active_thread_id()) } /// Load the current vector clock in use and the current set of thread clocks @@ -1772,10 +1766,7 @@ impl GlobalState { &self, thread_mgr: &ThreadManager<'_, '_>, ) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { - let index = self.current_index(thread_mgr); - let ref_vector = self.vector_clocks.borrow_mut(); - let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]); - (index, clocks) + self.thread_state_mut(thread_mgr.get_active_thread_id()) } /// Return the current thread, should be the same diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 35dcfecbbe334..a01b59c9165b7 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -41,7 +41,7 @@ pub enum InitOnceStatus { pub(super) struct InitOnce<'mir, 'tcx> { status: InitOnceStatus, waiters: VecDeque>, - data_race: VClock, + clock: VClock, } impl<'mir, 'tcx> VisitProvenance for InitOnce<'mir, 'tcx> { @@ -61,10 +61,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let current_thread = this.get_active_thread(); if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire( - &this.machine.threads.sync.init_onces[id].data_race, - current_thread, - ); + data_race + .acquire_clock(&this.machine.threads.sync.init_onces[id].clock, current_thread); } } @@ -77,7 +75,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let current_thread = this.get_active_thread(); - this.unblock_thread(waiter.thread); + this.unblock_thread(waiter.thread, BlockReason::InitOnce(id)); // Call callback, with the woken-up thread as `current`. this.set_active_thread(waiter.thread); @@ -142,7 +140,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let init_once = &mut this.machine.threads.sync.init_onces[id]; assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once"); init_once.waiters.push_back(InitOnceWaiter { thread, callback }); - this.block_thread(thread); + this.block_thread(thread, BlockReason::InitOnce(id)); } /// Begin initializing this InitOnce. Must only be called after checking that it is currently @@ -176,7 +174,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each complete happens-before the end of the wait if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release(&mut init_once.data_race, current_thread, current_span); + init_once.clock.clone_from(&data_race.release_clock(current_thread, current_span)); } // Wake up everyone. @@ -202,7 +200,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each complete happens-before the end of the wait if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release(&mut init_once.data_race, current_thread, current_span); + init_once.clock.clone_from(&data_race.release_clock(current_thread, current_span)); } // Wake up one waiting thread, so they can go ahead and try to init this. diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 45903107f1710..15e1a94d6db0e 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -6,3 +6,5 @@ pub mod init_once; pub mod thread; mod vector_clock; pub mod weak_memory; + +pub use vector_clock::VClock; diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 956a02ded0f13..d3cef8bf5f32b 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -69,12 +69,8 @@ struct Mutex { lock_count: usize, /// The queue of threads waiting for this mutex. queue: VecDeque, - /// Data race handle. This tracks the happens-before - /// relationship between each mutex access. It is - /// released to during unlock and acquired from during - /// locking, and therefore stores the clock of the last - /// thread to release this mutex. - data_race: VClock, + /// Mutex clock. This tracks the moment of the last unlock. + clock: VClock, } declare_id!(RwLockId); @@ -91,7 +87,7 @@ struct RwLock { writer_queue: VecDeque, /// The queue of reader threads waiting for this lock. reader_queue: VecDeque, - /// Data race handle for writers. Tracks the happens-before + /// Data race clock for writers. Tracks the happens-before /// ordering between each write access to a rwlock and is updated /// after a sequence of concurrent readers to track the happens- /// before ordering between the set of previous readers and @@ -99,8 +95,8 @@ struct RwLock { /// Contains the clock of the last thread to release a writer /// lock or the joined clock of the set of last threads to release /// shared reader locks. - data_race: VClock, - /// Data race handle for readers. This is temporary storage + clock_unlocked: VClock, + /// Data race clock for readers. This is temporary storage /// for the combined happens-before ordering for between all /// concurrent readers and the next writer, and the value /// is stored to the main data_race variable once all @@ -110,30 +106,18 @@ struct RwLock { /// add happens-before orderings between shared reader /// locks. /// This is only relevant when there is an active reader. - data_race_reader: VClock, + clock_current_readers: VClock, } declare_id!(CondvarId); -#[derive(Debug, Copy, Clone)] -pub enum RwLockMode { - Read, - Write, -} - -#[derive(Debug)] -pub enum CondvarLock { - Mutex(MutexId), - RwLock { id: RwLockId, mode: RwLockMode }, -} - /// A thread waiting on a conditional variable. #[derive(Debug)] struct CondvarWaiter { /// The thread that is waiting on this variable. thread: ThreadId, - /// The mutex or rwlock on which the thread is waiting. - lock: CondvarLock, + /// The mutex on which the thread is waiting. + lock: MutexId, } /// The conditional variable state. @@ -144,8 +128,8 @@ struct Condvar { /// between a cond-var signal and a cond-var /// wait during a non-spurious signal event. /// Contains the clock of the last thread to - /// perform a futex-signal. - data_race: VClock, + /// perform a condvar-signal. + clock: VClock, } /// The futex state. @@ -157,7 +141,7 @@ struct Futex { /// during a non-spurious wake event. /// Contains the clock of the last thread to /// perform a futex-wake. - data_race: VClock, + clock: VClock, } /// A thread waiting on a futex. @@ -232,7 +216,7 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: fn rwlock_dequeue_and_lock_reader(&mut self, id: RwLockId) -> bool { let this = self.eval_context_mut(); if let Some(reader) = this.machine.threads.sync.rwlocks[id].reader_queue.pop_front() { - this.unblock_thread(reader); + this.unblock_thread(reader, BlockReason::RwLock(id)); this.rwlock_reader_lock(id, reader); true } else { @@ -246,7 +230,7 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: fn rwlock_dequeue_and_lock_writer(&mut self, id: RwLockId) -> bool { let this = self.eval_context_mut(); if let Some(writer) = this.machine.threads.sync.rwlocks[id].writer_queue.pop_front() { - this.unblock_thread(writer); + this.unblock_thread(writer, BlockReason::RwLock(id)); this.rwlock_writer_lock(id, writer); true } else { @@ -260,7 +244,7 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: fn mutex_dequeue_and_lock(&mut self, id: MutexId) -> bool { let this = self.eval_context_mut(); if let Some(thread) = this.machine.threads.sync.mutexes[id].queue.pop_front() { - this.unblock_thread(thread); + this.unblock_thread(thread, BlockReason::Mutex(id)); this.mutex_lock(id, thread); true } else { @@ -358,7 +342,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } mutex.lock_count = mutex.lock_count.checked_add(1).unwrap(); if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire(&mutex.data_race, thread); + data_race.acquire_clock(&mutex.clock, thread); } } @@ -385,11 +369,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // The mutex is completely unlocked. Try transferring ownership // to another thread. if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release( - &mut mutex.data_race, - current_owner, - current_span, - ); + mutex.clock.clone_from(&data_race.release_clock(current_owner, current_span)); } this.mutex_dequeue_and_lock(id); } @@ -406,7 +386,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); assert!(this.mutex_is_locked(id), "queing on unlocked mutex"); this.machine.threads.sync.mutexes[id].queue.push_back(thread); - this.block_thread(thread); + this.block_thread(thread, BlockReason::Mutex(id)); } /// Provides the closure with the next RwLockId. Creates that RwLock if the closure returns None, @@ -460,7 +440,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let count = rwlock.readers.entry(reader).or_insert(0); *count = count.checked_add(1).expect("the reader counter overflowed"); if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire(&rwlock.data_race, reader); + data_race.acquire_clock(&rwlock.clock_unlocked, reader); } } @@ -486,20 +466,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } if let Some(data_race) = &this.machine.data_race { // Add this to the shared-release clock of all concurrent readers. - data_race.validate_lock_release_shared( - &mut rwlock.data_race_reader, - reader, - current_span, - ); + rwlock.clock_current_readers.join(&data_race.release_clock(reader, current_span)); } // The thread was a reader. If the lock is not held any more, give it to a writer. if this.rwlock_is_locked(id).not() { // All the readers are finished, so set the writer data-race handle to the value - // of the union of all reader data race handles, since the set of readers - // happen-before the writers + // of the union of all reader data race handles, since the set of readers + // happen-before the writers let rwlock = &mut this.machine.threads.sync.rwlocks[id]; - rwlock.data_race.clone_from(&rwlock.data_race_reader); + rwlock.clock_unlocked.clone_from(&rwlock.clock_current_readers); this.rwlock_dequeue_and_lock_writer(id); } true @@ -511,7 +487,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); assert!(this.rwlock_is_write_locked(id), "read-queueing on not write locked rwlock"); this.machine.threads.sync.rwlocks[id].reader_queue.push_back(reader); - this.block_thread(reader); + this.block_thread(reader, BlockReason::RwLock(id)); } /// Lock by setting the writer that owns the lock. @@ -523,7 +499,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let rwlock = &mut this.machine.threads.sync.rwlocks[id]; rwlock.writer = Some(writer); if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire(&rwlock.data_race, writer); + data_race.acquire_clock(&rwlock.clock_unlocked, writer); } } @@ -542,11 +518,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { trace!("rwlock_writer_unlock: {:?} unlocked by {:?}", id, expected_writer); // Release memory to next lock holder. if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release( - &mut rwlock.data_race, - current_writer, - current_span, - ); + rwlock + .clock_unlocked + .clone_from(&*data_race.release_clock(current_writer, current_span)); } // The thread was a writer. // @@ -573,7 +547,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock"); this.machine.threads.sync.rwlocks[id].writer_queue.push_back(writer); - this.block_thread(writer); + this.block_thread(writer, BlockReason::RwLock(id)); } /// Provides the closure with the next CondvarId. Creates that Condvar if the closure returns None, @@ -605,7 +579,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Mark that the thread is waiting on the conditional variable. - fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: CondvarLock) { + fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: MutexId) { let this = self.eval_context_mut(); let waiters = &mut this.machine.threads.sync.condvars[id].waiters; assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting"); @@ -614,7 +588,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Wake up some thread (if there is any) sleeping on the conditional /// variable. - fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, CondvarLock)> { + fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, MutexId)> { let this = self.eval_context_mut(); let current_thread = this.get_active_thread(); let current_span = this.machine.current_span(); @@ -623,11 +597,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each condvar signal happens-before the end of the condvar wake if let Some(data_race) = data_race { - data_race.validate_lock_release(&mut condvar.data_race, current_thread, current_span); + condvar.clock.clone_from(&*data_race.release_clock(current_thread, current_span)); } condvar.waiters.pop_front().map(|waiter| { if let Some(data_race) = data_race { - data_race.validate_lock_acquire(&condvar.data_race, waiter.thread); + data_race.acquire_clock(&condvar.clock, waiter.thread); } (waiter.thread, waiter.lock) }) @@ -657,14 +631,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each futex-wake happens-before the end of the futex wait if let Some(data_race) = data_race { - data_race.validate_lock_release(&mut futex.data_race, current_thread, current_span); + futex.clock.clone_from(&*data_race.release_clock(current_thread, current_span)); } // Wake up the first thread in the queue that matches any of the bits in the bitset. futex.waiters.iter().position(|w| w.bitset & bitset != 0).map(|i| { let waiter = futex.waiters.remove(i).unwrap(); if let Some(data_race) = data_race { - data_race.validate_lock_acquire(&futex.data_race, waiter.thread); + data_race.acquire_clock(&futex.clock, waiter.thread); } waiter.thread }) diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index d1136272f0108..2fabd39a74455 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -88,18 +88,33 @@ impl From for u64 { } } +/// Keeps track of what the thread is blocked on. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum BlockReason { + /// The thread tried to join the specified thread and is blocked until that + /// thread terminates. + Join(ThreadId), + /// Waiting for time to pass. + Sleep, + /// Blocked on a mutex. + Mutex(MutexId), + /// Blocked on a condition variable. + Condvar(CondvarId), + /// Blocked on a reader-writer lock. + RwLock(RwLockId), + /// Blocled on a Futex variable. + Futex { addr: u64 }, + /// Blocked on an InitOnce. + InitOnce(InitOnceId), +} + /// The state of a thread. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ThreadState { /// The thread is enabled and can be executed. Enabled, - /// The thread tried to join the specified thread and is blocked until that - /// thread terminates. - BlockedOnJoin(ThreadId), - /// The thread is blocked on some synchronization primitive. It is the - /// responsibility of the synchronization primitives to track threads that - /// are blocked by them. - BlockedOnSync, + /// The thread is blocked on something. + Blocked(BlockReason), /// The thread has terminated its execution. We do not delete terminated /// threads (FIXME: why?). Terminated, @@ -208,6 +223,12 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> { // empty stacks. self.top_user_relevant_frame.or_else(|| self.stack.len().checked_sub(1)) } + + pub fn current_span(&self) -> Span { + self.top_user_relevant_frame() + .map(|frame_idx| self.stack[frame_idx].current_span()) + .unwrap_or(rustc_span::DUMMY_SP) + } } impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> { @@ -296,17 +317,17 @@ impl VisitProvenance for Frame<'_, '_, Provenance, FrameExtra<'_>> { /// A specific moment in time. #[derive(Debug)] -pub enum Time { +pub enum CallbackTime { Monotonic(Instant), RealTime(SystemTime), } -impl Time { +impl CallbackTime { /// How long do we have to wait from now until the specified time? fn get_wait_time(&self, clock: &Clock) -> Duration { match self { - Time::Monotonic(instant) => instant.duration_since(clock.now()), - Time::RealTime(time) => + CallbackTime::Monotonic(instant) => instant.duration_since(clock.now()), + CallbackTime::RealTime(time) => time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)), } } @@ -318,7 +339,7 @@ impl Time { /// conditional variable, the signal handler deletes the callback. struct TimeoutCallbackInfo<'mir, 'tcx> { /// The callback should be called no earlier than this time. - call_time: Time, + call_time: CallbackTime, /// The called function. callback: TimeoutCallback<'mir, 'tcx>, } @@ -430,11 +451,10 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { ) -> &mut Vec>> { &mut self.threads[self.active_thread].stack } - pub fn all_stacks( &self, - ) -> impl Iterator>]> { - self.threads.iter().map(|t| &t.stack[..]) + ) -> impl Iterator>])> { + self.threads.iter_enumerated().map(|(id, t)| (id, &t.stack[..])) } /// Create a new thread and returns its id. @@ -539,7 +559,8 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { self.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined; if self.threads[joined_thread_id].state != ThreadState::Terminated { // The joined thread is still running, we need to wait for it. - self.active_thread_mut().state = ThreadState::BlockedOnJoin(joined_thread_id); + self.active_thread_mut().state = + ThreadState::Blocked(BlockReason::Join(joined_thread_id)); trace!( "{:?} blocked on {:?} when trying to join", self.active_thread, @@ -569,10 +590,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { throw_ub_format!("trying to join itself"); } + // Sanity check `join_status`. assert!( - self.threads - .iter() - .all(|thread| thread.state != ThreadState::BlockedOnJoin(joined_thread_id)), + self.threads.iter().all(|thread| { + thread.state != ThreadState::Blocked(BlockReason::Join(joined_thread_id)) + }), "this thread already has threads waiting for its termination" ); @@ -594,16 +616,17 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } /// Put the thread into the blocked state. - fn block_thread(&mut self, thread: ThreadId) { + fn block_thread(&mut self, thread: ThreadId, reason: BlockReason) { let state = &mut self.threads[thread].state; assert_eq!(*state, ThreadState::Enabled); - *state = ThreadState::BlockedOnSync; + *state = ThreadState::Blocked(reason); } /// Put the blocked thread into the enabled state. - fn unblock_thread(&mut self, thread: ThreadId) { + /// Sanity-checks that the thread previously was blocked for the right reason. + fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) { let state = &mut self.threads[thread].state; - assert_eq!(*state, ThreadState::BlockedOnSync); + assert_eq!(*state, ThreadState::Blocked(reason)); *state = ThreadState::Enabled; } @@ -622,7 +645,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { fn register_timeout_callback( &mut self, thread: ThreadId, - call_time: Time, + call_time: CallbackTime, callback: TimeoutCallback<'mir, 'tcx>, ) { self.timeout_callbacks @@ -683,7 +706,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { // Check if we need to unblock any threads. let mut joined_threads = vec![]; // store which threads joined, we'll need it for (i, thread) in self.threads.iter_enumerated_mut() { - if thread.state == ThreadState::BlockedOnJoin(self.active_thread) { + if thread.state == ThreadState::Blocked(BlockReason::Join(self.active_thread)) { // The thread has terminated, mark happens-before edge to joining thread if data_race.is_some() { joined_threads.push(i); @@ -999,13 +1022,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } #[inline] - fn block_thread(&mut self, thread: ThreadId) { - self.eval_context_mut().machine.threads.block_thread(thread); + fn block_thread(&mut self, thread: ThreadId, reason: BlockReason) { + self.eval_context_mut().machine.threads.block_thread(thread, reason); } #[inline] - fn unblock_thread(&mut self, thread: ThreadId) { - self.eval_context_mut().machine.threads.unblock_thread(thread); + fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) { + self.eval_context_mut().machine.threads.unblock_thread(thread, reason); } #[inline] @@ -1027,11 +1050,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn register_timeout_callback( &mut self, thread: ThreadId, - call_time: Time, + call_time: CallbackTime, callback: TimeoutCallback<'mir, 'tcx>, ) { let this = self.eval_context_mut(); - if !this.machine.communicate() && matches!(call_time, Time::RealTime(..)) { + if !this.machine.communicate() && matches!(call_time, CallbackTime::RealTime(..)) { panic!("cannot have `RealTime` callback with isolation enabled!") } this.machine.threads.register_timeout_callback(thread, call_time, callback); diff --git a/src/tools/miri/src/concurrency/vector_clock.rs b/src/tools/miri/src/concurrency/vector_clock.rs index fe719943dcb65..2cd3d031b1edd 100644 --- a/src/tools/miri/src/concurrency/vector_clock.rs +++ b/src/tools/miri/src/concurrency/vector_clock.rs @@ -4,7 +4,7 @@ use smallvec::SmallVec; use std::{ cmp::Ordering, fmt::Debug, - ops::{Index, IndexMut, Shr}, + ops::{Index, Shr}, }; use super::data_race::NaReadType; @@ -92,7 +92,7 @@ impl VTimestamp { } #[inline] - pub fn set_read_type(&mut self, read_type: NaReadType) { + pub(super) fn set_read_type(&mut self, read_type: NaReadType) { self.time_and_read_type = Self::encode_time_and_read_type(self.time(), read_type); } @@ -138,7 +138,7 @@ pub struct VClock(SmallVec<[VTimestamp; SMALL_VECTOR]>); impl VClock { /// Create a new vector-clock containing all zeros except /// for a value at the given index - pub fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock { + pub(super) fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock { let len = index.index() + 1; let mut vec = smallvec::smallvec![VTimestamp::ZERO; len]; vec[index.index()] = timestamp; @@ -147,12 +147,18 @@ impl VClock { /// Load the internal timestamp slice in the vector clock #[inline] - pub fn as_slice(&self) -> &[VTimestamp] { + pub(super) fn as_slice(&self) -> &[VTimestamp] { + debug_assert!(!self.0.last().is_some_and(|t| t.time() == 0)); self.0.as_slice() } + #[inline] + pub(super) fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp { + self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap() + } + /// Get a mutable slice to the internal vector with minimum `min_len` - /// elements, to preserve invariants this vector must modify + /// elements. To preserve invariants, the caller must modify /// the `min_len`-1 nth element to a non-zero value #[inline] fn get_mut_with_min_len(&mut self, min_len: usize) -> &mut [VTimestamp] { @@ -166,7 +172,7 @@ impl VClock { /// Increment the vector clock at a known index /// this will panic if the vector index overflows #[inline] - pub fn increment_index(&mut self, idx: VectorIdx, current_span: Span) { + pub(super) fn increment_index(&mut self, idx: VectorIdx, current_span: Span) { let idx = idx.index(); let mut_slice = self.get_mut_with_min_len(idx + 1); let idx_ref = &mut mut_slice[idx]; @@ -190,28 +196,36 @@ impl VClock { } } - /// Set the element at the current index of the vector - pub fn set_at_index(&mut self, other: &Self, idx: VectorIdx) { + /// Set the element at the current index of the vector. May only increase elements. + pub(super) fn set_at_index(&mut self, other: &Self, idx: VectorIdx) { + let new_timestamp = other[idx]; + // Setting to 0 is different, since the last element cannot be 0. + if new_timestamp.time() == 0 { + if idx.index() >= self.0.len() { + // This index does not even exist yet in our clock. Just do nothing. + return; + } + // This changes an existing element. Since it can only increase, that + // can never make the last element 0. + } + let mut_slice = self.get_mut_with_min_len(idx.index() + 1); + let mut_timestamp = &mut mut_slice[idx.index()]; - let prev_span = mut_slice[idx.index()].span; + let prev_span = mut_timestamp.span; - mut_slice[idx.index()] = other[idx]; + assert!(*mut_timestamp <= new_timestamp, "set_at_index: may only increase the timestamp"); + *mut_timestamp = new_timestamp; - let span = &mut mut_slice[idx.index()].span; + let span = &mut mut_timestamp.span; *span = span.substitute_dummy(prev_span); } /// Set the vector to the all-zero vector #[inline] - pub fn set_zero_vector(&mut self) { + pub(super) fn set_zero_vector(&mut self) { self.0.clear(); } - - /// Return if this vector is the all-zero vector - pub fn is_zero_vector(&self) -> bool { - self.0.is_empty() - } } impl Clone for VClock { @@ -407,13 +421,6 @@ impl Index for VClock { } } -impl IndexMut for VClock { - #[inline] - fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp { - self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap() - } -} - /// Test vector clock ordering operations /// data-race detection is tested in the external /// test suite @@ -553,4 +560,15 @@ mod tests { "Invalid alt (>=):\n l: {l:?}\n r: {r:?}" ); } + + #[test] + fn set_index_to_0() { + let mut clock1 = from_slice(&[0, 1, 2, 3]); + let clock2 = from_slice(&[0, 2, 3, 4, 0, 5]); + // Naively, this would extend clock1 with a new index and set it to 0, making + // the last index 0. Make sure that does not happen. + clock1.set_at_index(&clock2, VectorIdx(4)); + // This must not have made the last element 0. + assert!(clock1.0.last().unwrap().time() != 0); + } } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index a2b817ea0d571..d44d04e9bf839 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -361,9 +361,12 @@ pub fn report_error<'tcx, 'mir>( }; let stacktrace = ecx.generate_stacktrace(); - let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); + let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine); - // We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early. + let mut show_all_threads = false; + + // We want to dump the allocation if this is `InvalidUninitBytes`. + // Since `format_interp_error` consumes `e`, we compute the outut early. let mut extra = String::new(); match e.kind() { UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => { @@ -375,6 +378,15 @@ pub fn report_error<'tcx, 'mir>( .unwrap(); writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap(); } + MachineStop(info) => { + let info = info.downcast_ref::().expect("invalid MachineStop payload"); + match info { + TerminationInfo::Deadlock => { + show_all_threads = true; + } + _ => {} + } + } _ => {} } @@ -387,18 +399,39 @@ pub fn report_error<'tcx, 'mir>( vec![], helps, &stacktrace, + Some(ecx.get_active_thread()), &ecx.machine, ); + eprint!("{extra}"); // newlines are already in the string + + if show_all_threads { + for (thread, stack) in ecx.machine.threads.all_stacks() { + if thread != ecx.get_active_thread() { + let stacktrace = Frame::generate_stacktrace_from_stack(stack); + let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); + any_pruned |= was_pruned; + report_msg( + DiagLevel::Error, + format!("deadlock: the evaluated program deadlocked"), + vec![format!("the evaluated program deadlocked")], + vec![], + vec![], + &stacktrace, + Some(thread), + &ecx.machine, + ) + } + } + } + // Include a note like `std` does when we omit frames from a backtrace - if was_pruned { + if any_pruned { ecx.tcx.dcx().note( "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace", ); } - eprint!("{extra}"); // newlines are already in the string - // Debug-dump all locals. for (i, frame) in ecx.active_thread_stack().iter().enumerate() { trace!("-------------------"); @@ -435,6 +468,7 @@ pub fn report_leaks<'mir, 'tcx>( vec![], vec![], &backtrace, + None, // we don't know the thread this is from &ecx.machine, ); } @@ -457,6 +491,7 @@ pub fn report_msg<'tcx>( notes: Vec<(Option, String)>, helps: Vec<(Option, String)>, stacktrace: &[FrameInfo<'tcx>], + thread: Option, machine: &MiriMachine<'_, 'tcx>, ) { let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); @@ -506,12 +541,13 @@ pub fn report_msg<'tcx>( if extra_span { write!(backtrace_title, " (of the first span)").unwrap(); } - let thread_name = - machine.threads.get_thread_display_name(machine.threads.get_active_thread_id()); - if thread_name != "main" { - // Only print thread name if it is not `main`. - write!(backtrace_title, " on thread `{thread_name}`").unwrap(); - }; + if let Some(thread) = thread { + let thread_name = machine.threads.get_thread_display_name(thread); + if thread_name != "main" { + // Only print thread name if it is not `main`. + write!(backtrace_title, " on thread `{thread_name}`").unwrap(); + }; + } write!(backtrace_title, ":").unwrap(); err.note(backtrace_title); for (idx, frame_info) in stacktrace.iter().enumerate() { @@ -628,7 +664,16 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { _ => vec![], }; - report_msg(diag_level, title, vec![msg], notes, helps, &stacktrace, self); + report_msg( + diag_level, + title, + vec![msg], + notes, + helps, + &stacktrace, + Some(self.threads.get_active_thread_id()), + self, + ); } } @@ -654,6 +699,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { vec![], vec![], &stacktrace, + Some(this.get_active_thread()), &this.machine, ); } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index df0ede1e1b6a9..45dadb50f4b26 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -150,6 +150,10 @@ pub struct MiriConfig { pub page_size: Option, /// Whether to collect a backtrace when each allocation is created, just in case it leaks. pub collect_leak_backtraces: bool, + /// Probability for address reuse. + pub address_reuse_rate: f64, + /// Probability for address reuse across threads. + pub address_reuse_cross_thread_rate: f64, } impl Default for MiriConfig { @@ -186,6 +190,8 @@ impl Default for MiriConfig { num_cpus: 1, page_size: None, collect_leak_backtraces: true, + address_reuse_rate: 0.5, + address_reuse_cross_thread_rate: 0.1, } } } diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index e2c6769ccb553..92bdaf301704b 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -912,10 +912,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }) } + /// Read bytes from a byte slice. + fn read_byte_slice<'a>( + &'a self, + slice: &ImmTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, &'a [u8]> + where + 'mir: 'a, + { + let this = self.eval_context_ref(); + let (ptr, len) = slice.to_scalar_pair(); + let ptr = ptr.to_pointer(this)?; + let len = len.to_target_usize(this)?; + let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; + Ok(bytes) + } + /// Read a sequence of bytes until the first null terminator. fn read_c_str<'a>(&'a self, ptr: Pointer>) -> InterpResult<'tcx, &'a [u8]> where - 'tcx: 'a, 'mir: 'a, { let this = self.eval_context_ref(); @@ -1265,9 +1280,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// This function is backed by a cache, and can be assumed to be very fast. /// It will work even when the stack is empty. pub fn current_span(&self) -> Span { - self.top_user_relevant_frame() - .map(|frame_idx| self.stack()[frame_idx].current_span()) - .unwrap_or(rustc_span::DUMMY_SP) + self.threads.active_thread_ref().current_span() } /// Returns the span of the *caller* of the current operation, again @@ -1279,7 +1292,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { // We need to go down at least to the caller (len - 2), or however // far we have to go to find a frame in a local crate which is also not #[track_caller]. let frame_idx = self.top_user_relevant_frame().unwrap(); - let frame_idx = cmp::min(frame_idx, self.stack().len().checked_sub(2).unwrap()); + let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2)); self.stack()[frame_idx].current_span() } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 390340d711374..2e19c9ff71356 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -116,7 +116,9 @@ pub use crate::concurrency::{ data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _}, init_once::{EvalContextExt as _, InitOnceId}, sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SyncId}, - thread::{EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, Time}, + thread::{ + BlockReason, CallbackTime, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, + }, }; pub use crate::diagnostics::{ report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 26f8c0a3babba..cbe70cbffee55 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1282,7 +1282,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra), size: Size, align: Align, - _kind: MemoryKind, + kind: MemoryKind, ) -> InterpResult<'tcx> { if machine.tracked_alloc_ids.contains(&alloc_id) { machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); @@ -1303,12 +1303,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { { *deallocated_at = Some(machine.current_span()); } - machine.alloc_addresses.get_mut().free_alloc_id( - machine.rng.get_mut(), - alloc_id, - size, - align, - ); + machine.free_alloc_id(alloc_id, size, align, kind); Ok(()) } diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs new file mode 100644 index 0000000000000..b5ae06c2a492a --- /dev/null +++ b/src/tools/miri/src/shims/alloc.rs @@ -0,0 +1,152 @@ +use std::iter; + +use rustc_ast::expand::allocator::AllocatorKind; +use rustc_target::abi::{Align, Size}; + +use crate::*; +use shims::foreign_items::EmulateForeignItemResult; + +/// Check some basic requirements for this allocation request: +/// non-zero size, power-of-two alignment. +pub(super) fn check_alloc_request<'tcx>(size: u64, align: u64) -> InterpResult<'tcx> { + if size == 0 { + throw_ub_format!("creating allocation with size 0"); + } + if !align.is_power_of_two() { + throw_ub_format!("creating allocation with non-power-of-two alignment {}", align); + } + Ok(()) +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// Returns the minimum alignment for the target architecture for allocations of the given size. + fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align { + let this = self.eval_context_ref(); + // List taken from `library/std/src/sys/pal/common/alloc.rs`. + // This list should be kept in sync with the one from libstd. + let min_align = match this.tcx.sess.target.arch.as_ref() { + "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8, + "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" => + 16, + arch => bug!("unsupported target architecture for malloc: `{}`", arch), + }; + // Windows always aligns, even small allocations. + // Source: + // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big. + if kind == MiriMemoryKind::WinHeap || size >= min_align { + return Align::from_bytes(min_align).unwrap(); + } + // We have `size < min_align`. Round `size` *down* to the next power of two and use that. + fn prev_power_of_two(x: u64) -> u64 { + let next_pow2 = x.next_power_of_two(); + if next_pow2 == x { + // x *is* a power of two, just use that. + x + } else { + // x is between two powers, so next = 2*prev. + next_pow2 / 2 + } + } + Align::from_bytes(prev_power_of_two(size)).unwrap() + } + + /// Emulates calling the internal __rust_* allocator functions + fn emulate_allocator( + &mut self, + default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>, + ) -> InterpResult<'tcx, EmulateForeignItemResult> { + let this = self.eval_context_mut(); + + let Some(allocator_kind) = this.tcx.allocator_kind(()) else { + // in real code, this symbol does not exist without an allocator + return Ok(EmulateForeignItemResult::NotSupported); + }; + + match allocator_kind { + AllocatorKind::Global => { + // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion + // of this attribute. As such we have to call an exported Rust function, + // and not execute any Miri shim. Somewhat unintuitively doing so is done + // by returning `NotSupported`, which triggers the `lookup_exported_symbol` + // fallback case in `emulate_foreign_item`. + return Ok(EmulateForeignItemResult::NotSupported); + } + AllocatorKind::Default => { + default(this)?; + Ok(EmulateForeignItemResult::NeedsJumping) + } + } + } + + fn malloc( + &mut self, + size: u64, + zero_init: bool, + kind: MiriMemoryKind, + ) -> InterpResult<'tcx, Pointer>> { + let this = self.eval_context_mut(); + if size == 0 { + Ok(Pointer::null()) + } else { + let align = this.min_align(size, kind); + let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; + if zero_init { + // We just allocated this, the access is definitely in-bounds and fits into our address space. + this.write_bytes_ptr( + ptr.into(), + iter::repeat(0u8).take(usize::try_from(size).unwrap()), + ) + .unwrap(); + } + Ok(ptr.into()) + } + } + + fn free( + &mut self, + ptr: Pointer>, + kind: MiriMemoryKind, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + if !this.ptr_is_null(ptr)? { + this.deallocate_ptr(ptr, None, kind.into())?; + } + Ok(()) + } + + fn realloc( + &mut self, + old_ptr: Pointer>, + new_size: u64, + kind: MiriMemoryKind, + ) -> InterpResult<'tcx, Pointer>> { + let this = self.eval_context_mut(); + let new_align = this.min_align(new_size, kind); + if this.ptr_is_null(old_ptr)? { + // Here we must behave like `malloc`. + if new_size == 0 { + Ok(Pointer::null()) + } else { + let new_ptr = + this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?; + Ok(new_ptr.into()) + } + } else { + if new_size == 0 { + // C, in their infinite wisdom, made this UB. + // + throw_ub_format!("`realloc` with a size of zero"); + } else { + let new_ptr = this.reallocate_ptr( + old_ptr, + None, + Size::from_bytes(new_size), + new_align, + kind.into(), + )?; + Ok(new_ptr.into()) + } + } + } +} diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs index 0284e5b606ced..7c4a54fb461a8 100644 --- a/src/tools/miri/src/shims/extern_static.rs +++ b/src/tools/miri/src/shims/extern_static.rs @@ -32,9 +32,14 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// Sets up the "extern statics" for this machine. pub fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { // "__rust_no_alloc_shim_is_unstable" - let val = ImmTy::from_int(0, this.machine.layouts.u8); + let val = ImmTy::from_int(0, this.machine.layouts.u8); // always 0, value does not matter Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?; + // "__rust_alloc_error_handler_should_panic" + let val = this.tcx.sess.opts.unstable_opts.oom.should_panic(); + let val = ImmTy::from_int(val, this.machine.layouts.u8); + Self::alloc_extern_static(this, "__rust_alloc_error_handler_should_panic", val)?; + match this.tcx.sess.target.os.as_ref() { "linux" => { Self::null_ptr_extern_statics( diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 6b0797f6da1ac..636361148a469 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -1,7 +1,7 @@ use std::{collections::hash_map::Entry, io::Write, iter, path::Path}; use rustc_apfloat::Float; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::alloc_error_handler_name; use rustc_hir::{def::DefKind, def_id::CrateNum}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; @@ -12,6 +12,7 @@ use rustc_target::{ spec::abi::Abi, }; +use super::alloc::{check_alloc_request, EvalContextExt as _}; use super::backtrace::EvalContextExt as _; use crate::*; use helpers::{ToHost, ToSoft}; @@ -80,6 +81,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { panic_impl_instance, ))); } + "__rust_alloc_error_handler" => { + // Forward to the right symbol that implements this function. + let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else { + // in real code, this symbol does not exist without an allocator + throw_unsup_format!( + "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set" + ); + }; + let name = alloc_error_handler_name(handler_kind); + let handler = this + .lookup_exported_symbol(Symbol::intern(name))? + .expect("missing alloc error handler symbol"); + return Ok(Some(handler)); + } #[rustfmt::skip] | "exit" | "ExitProcess" @@ -218,151 +233,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Some(instance) => Ok(Some((this.load_mir(instance.def, None)?, instance))), } } - - fn malloc( - &mut self, - size: u64, - zero_init: bool, - kind: MiriMemoryKind, - ) -> InterpResult<'tcx, Pointer>> { - let this = self.eval_context_mut(); - if size == 0 { - Ok(Pointer::null()) - } else { - let align = this.min_align(size, kind); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; - if zero_init { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); - } - Ok(ptr.into()) - } - } - - fn free( - &mut self, - ptr: Pointer>, - kind: MiriMemoryKind, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - if !this.ptr_is_null(ptr)? { - this.deallocate_ptr(ptr, None, kind.into())?; - } - Ok(()) - } - - fn realloc( - &mut self, - old_ptr: Pointer>, - new_size: u64, - kind: MiriMemoryKind, - ) -> InterpResult<'tcx, Pointer>> { - let this = self.eval_context_mut(); - let new_align = this.min_align(new_size, kind); - if this.ptr_is_null(old_ptr)? { - if new_size == 0 { - Ok(Pointer::null()) - } else { - let new_ptr = - this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?; - Ok(new_ptr.into()) - } - } else { - if new_size == 0 { - this.deallocate_ptr(old_ptr, None, kind.into())?; - Ok(Pointer::null()) - } else { - let new_ptr = this.reallocate_ptr( - old_ptr, - None, - Size::from_bytes(new_size), - new_align, - kind.into(), - )?; - Ok(new_ptr.into()) - } - } - } } impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Read bytes from a `(ptr, len)` argument - fn read_byte_slice<'i>(&'i self, bytes: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, &'i [u8]> - where - 'mir: 'i, - { - let this = self.eval_context_ref(); - let (ptr, len) = this.read_immediate(bytes)?.to_scalar_pair(); - let ptr = ptr.to_pointer(this)?; - let len = len.to_target_usize(this)?; - let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; - Ok(bytes) - } - - /// Returns the minimum alignment for the target architecture for allocations of the given size. - fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align { - let this = self.eval_context_ref(); - // List taken from `library/std/src/sys/pal/common/alloc.rs`. - // This list should be kept in sync with the one from libstd. - let min_align = match this.tcx.sess.target.arch.as_ref() { - "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8, - "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" => - 16, - arch => bug!("unsupported target architecture for malloc: `{}`", arch), - }; - // Windows always aligns, even small allocations. - // Source: - // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big. - if kind == MiriMemoryKind::WinHeap || size >= min_align { - return Align::from_bytes(min_align).unwrap(); - } - // We have `size < min_align`. Round `size` *down* to the next power of two and use that. - fn prev_power_of_two(x: u64) -> u64 { - let next_pow2 = x.next_power_of_two(); - if next_pow2 == x { - // x *is* a power of two, just use that. - x - } else { - // x is between two powers, so next = 2*prev. - next_pow2 / 2 - } - } - Align::from_bytes(prev_power_of_two(size)).unwrap() - } - - /// Emulates calling the internal __rust_* allocator functions - fn emulate_allocator( - &mut self, - default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { - let this = self.eval_context_mut(); - - let Some(allocator_kind) = this.tcx.allocator_kind(()) else { - // in real code, this symbol does not exist without an allocator - return Ok(EmulateForeignItemResult::NotSupported); - }; - - match allocator_kind { - AllocatorKind::Global => { - // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion - // of this attribute. As such we have to call an exported Rust function, - // and not execute any Miri shim. Somewhat unintuitively doing so is done - // by returning `NotSupported`, which triggers the `lookup_exported_symbol` - // fallback case in `emulate_foreign_item`. - return Ok(EmulateForeignItemResult::NotSupported); - } - AllocatorKind::Default => { - default(this)?; - Ok(EmulateForeignItemResult::NeedsJumping) - } - } - } - fn emulate_foreign_item_inner( &mut self, link_name: Symbol, @@ -452,7 +326,9 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [ptr, nth_parent, name] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let nth_parent = this.read_scalar(nth_parent)?.to_u8()?; - let name = this.read_byte_slice(name)?; + let name = this.read_immediate(name)?; + + let name = this.read_byte_slice(&name)?; // We must make `name` owned because we need to // end the shared borrow from `read_byte_slice` before we can // start the mutable borrow for `give_pointer_debug_name`. @@ -513,7 +389,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // README for details. "miri_write_to_stdout" | "miri_write_to_stderr" => { let [msg] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let msg = this.read_byte_slice(msg)?; + let msg = this.read_immediate(msg)?; + let msg = this.read_byte_slice(&msg)?; // Note: we're ignoring errors writing to host stdout/stderr. let _ignore = match link_name.as_str() { "miri_write_to_stdout" => std::io::stdout().write_all(msg), @@ -606,7 +483,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = this.read_target_usize(size)?; let align = this.read_target_usize(align)?; - Self::check_alloc_request(size, align)?; + check_alloc_request(size, align)?; let memory_kind = match link_name.as_str() { "__rust_alloc" => MiriMemoryKind::Rust, @@ -640,7 +517,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = this.read_target_usize(size)?; let align = this.read_target_usize(align)?; - Self::check_alloc_request(size, align)?; + check_alloc_request(size, align)?; let ptr = this.allocate_ptr( Size::from_bytes(size), @@ -704,7 +581,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let new_size = this.read_target_usize(new_size)?; // No need to check old_size; we anyway check that they match the allocation. - Self::check_alloc_request(new_size, align)?; + check_alloc_request(new_size, align)?; let align = Align::from_bytes(align).unwrap(); let new_ptr = this.reallocate_ptr( @@ -1096,16 +973,4 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // i.e., if we actually emulated the function with one of the shims. Ok(EmulateForeignItemResult::NeedsJumping) } - - /// Check some basic requirements for this allocation request: - /// non-zero size, power-of-two alignment. - fn check_alloc_request(size: u64, align: u64) -> InterpResult<'tcx> { - if size == 0 { - throw_ub_format!("creating allocation with size 0"); - } - if !align.is_power_of_two() { - throw_ub_format!("creating allocation with non-power-of-two alignment {}", align); - } - Ok(()) - } } diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index ea6120f757947..85c9a202f7daf 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -1,5 +1,6 @@ #![warn(clippy::arithmetic_side_effects)] +mod alloc; mod backtrace; #[cfg(target_os = "linux")] pub mod ffi_support; diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 0409e31d65a8a..3e8c35d48aee0 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -251,7 +251,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.alloc_os_str_as_wide_str(&os_str, memkind) } - #[allow(clippy::get_first)] fn convert_path<'a>( &self, os_str: Cow<'a, OsStr>, @@ -260,100 +259,97 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_ref(); let target_os = &this.tcx.sess.target.os; + /// Adjust a Windows path to Unix conventions such that it un-does everything that + /// `unix_to_windows` did, and such that if the Windows input path was absolute, then the + /// Unix output path is absolute. + fn windows_to_unix(path: &mut Vec) + where + T: From + Copy + Eq, + { + let sep = T::from(b'/'); + // Make sure all path separators are `/`. + for c in path.iter_mut() { + if *c == b'\\'.into() { + *c = sep; + } + } + // If this starts with `//?/`, it was probably produced by `unix_to_windows`` and we + // remove the `//?` that got added to get the Unix path back out. + if path.get(0..4) == Some(&[sep, sep, b'?'.into(), sep]) { + // Remove first 3 characters. It still starts with `/` so it is absolute on Unix. + path.splice(0..3, std::iter::empty()); + } + // If it starts with a drive letter (`X:/`), convert it to an absolute Unix path. + else if path.get(1..3) == Some(&[b':'.into(), sep]) { + // We add a `/` at the beginning, to store the absolute Windows + // path in something that looks like an absolute Unix path. + path.insert(0, sep); + } + } + + /// Adjust a Unix path to Windows conventions such that it un-does everything that + /// `windows_to_unix` did, and such that if the Unix input path was absolute, then the + /// Windows output path is absolute. + fn unix_to_windows(path: &mut Vec) + where + T: From + Copy + Eq, + { + let sep = T::from(b'\\'); + // Make sure all path separators are `\`. + for c in path.iter_mut() { + if *c == b'/'.into() { + *c = sep; + } + } + // If the path is `\X:\`, the leading separator was probably added by `windows_to_unix` + // and we should get rid of it again. + if path.get(2..4) == Some(&[b':'.into(), sep]) && path[0] == sep { + // The new path is still absolute on Windows. + path.remove(0); + } + // If this starts withs a `\` but not a `\\`, then this was absolute on Unix but is + // relative on Windows (relative to "the root of the current directory", e.g. the + // drive letter). + else if path.first() == Some(&sep) && path.get(1) != Some(&sep) { + // We add `\\?` so it starts with `\\?\` which is some magic path on Windows + // that *is* considered absolute. This way we store the absolute Unix path + // in something that looks like an absolute Windows path. + path.splice(0..0, [sep, sep, b'?'.into()]); + } + } + + // Below we assume that everything non-Windows works like Unix, at least + // when it comes to file system path conventions. #[cfg(windows)] return if target_os == "windows" { // Windows-on-Windows, all fine. os_str } else { // Unix target, Windows host. - let (from, to) = match direction { - PathConversion::HostToTarget => ('\\', '/'), - PathConversion::TargetToHost => ('/', '\\'), - }; - let mut converted = os_str - .encode_wide() - .map(|wchar| if wchar == from as u16 { to as u16 } else { wchar }) - .collect::>(); - // We also have to ensure that absolute paths remain absolute. + let mut path: Vec = os_str.encode_wide().collect(); match direction { PathConversion::HostToTarget => { - // If this is an absolute Windows path that starts with a drive letter (`C:/...` - // after separator conversion), it would not be considered absolute by Unix - // target code. - if converted.get(1).copied() == Some(b':' as u16) - && converted.get(2).copied() == Some(b'/' as u16) - { - // We add a `/` at the beginning, to store the absolute Windows - // path in something that looks like an absolute Unix path. - converted.insert(0, b'/' as u16); - } + windows_to_unix(&mut path); } PathConversion::TargetToHost => { - // If the path is `\C:\`, the leading backslash was probably added by the above code - // and we should get rid of it again. - if converted.get(0).copied() == Some(b'\\' as u16) - && converted.get(2).copied() == Some(b':' as u16) - && converted.get(3).copied() == Some(b'\\' as u16) - { - converted.remove(0); - } + unix_to_windows(&mut path); } } - Cow::Owned(OsString::from_wide(&converted)) + Cow::Owned(OsString::from_wide(&path)) }; #[cfg(unix)] return if target_os == "windows" { // Windows target, Unix host. - let (from, to) = match direction { - PathConversion::HostToTarget => (b'/', b'\\'), - PathConversion::TargetToHost => (b'\\', b'/'), - }; - let mut converted = os_str - .as_bytes() - .iter() - .map(|&wchar| if wchar == from { to } else { wchar }) - .collect::>(); - // We also have to ensure that absolute paths remain absolute. + let mut path: Vec = os_str.into_owned().into_encoded_bytes(); match direction { PathConversion::HostToTarget => { - // If the path is `/C:/`, the leading backslash was probably added by the below - // driver letter handling and we should get rid of it again. - if converted.get(0).copied() == Some(b'\\') - && converted.get(2).copied() == Some(b':') - && converted.get(3).copied() == Some(b'\\') - { - converted.remove(0); - } - // If this start withs a `\` but not a `\\`, then for Windows this is a relative - // path. But the host path is absolute as it started with `/`. We add `\\?` so - // it starts with `\\?\` which is some magic path on Windows that *is* - // considered absolute. - else if converted.get(0).copied() == Some(b'\\') - && converted.get(1).copied() != Some(b'\\') - { - converted.splice(0..0, b"\\\\?".iter().copied()); - } + unix_to_windows(&mut path); } PathConversion::TargetToHost => { - // If this starts with `//?/`, it was probably produced by the above code and we - // remove the `//?` that got added to get the Unix path back out. - if converted.get(0).copied() == Some(b'/') - && converted.get(1).copied() == Some(b'/') - && converted.get(2).copied() == Some(b'?') - && converted.get(3).copied() == Some(b'/') - { - // Remove first 3 characters - converted.splice(0..3, std::iter::empty()); - } - // If it starts with a drive letter, convert it to an absolute Unix path. - else if converted.get(1).copied() == Some(b':') - && converted.get(2).copied() == Some(b'/') - { - converted.insert(0, b'/'); - } + windows_to_unix(&mut path); } } - Cow::Owned(OsString::from_vec(converted)) + Cow::Owned(OsString::from_vec(path)) } else { // Unix-on-Unix, all is fine. os_str diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 4535bcf6dfedd..1126c900226df 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -236,11 +236,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .unwrap_or_else(|| now.checked_add(Duration::from_secs(3600)).unwrap()); let active_thread = this.get_active_thread(); - this.block_thread(active_thread); + this.block_thread(active_thread, BlockReason::Sleep); this.register_timeout_callback( active_thread, - Time::Monotonic(timeout_time), + CallbackTime::Monotonic(timeout_time), Box::new(UnblockCallback { thread_to_unblock: active_thread }), ); @@ -259,11 +259,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let timeout_time = this.machine.clock.now().checked_add(duration).unwrap(); let active_thread = this.get_active_thread(); - this.block_thread(active_thread); + this.block_thread(active_thread, BlockReason::Sleep); this.register_timeout_callback( active_thread, - Time::Monotonic(timeout_time), + CallbackTime::Monotonic(timeout_time), Box::new(UnblockCallback { thread_to_unblock: active_thread }), ); @@ -281,7 +281,7 @@ impl VisitProvenance for UnblockCallback { impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback { fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - ecx.unblock_thread(self.thread_to_unblock); + ecx.unblock_thread(self.thread_to_unblock, BlockReason::Sleep); Ok(()) } } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 3a56aa9138b45..c72d3bb3df4a8 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -6,6 +6,7 @@ use rustc_span::Symbol; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; +use crate::shims::alloc::EvalContextExt as _; use crate::shims::unix::*; use crate::*; use shims::foreign_items::EmulateForeignItemResult; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 31076fdfaf626..ebf9f43c19ef6 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -196,13 +196,12 @@ struct OpenDir { read_dir: ReadDir, /// The most recent entry returned by readdir(). /// Will be freed by the next call. - entry: Pointer>, + entry: Option>>, } impl OpenDir { fn new(read_dir: ReadDir) -> Self { - // We rely on `free` being a NOP on null pointers. - Self { read_dir, entry: Pointer::null() } + Self { read_dir, entry: None } } } @@ -924,8 +923,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes(); let size = d_name_offset.checked_add(name_len).unwrap(); - let entry = - this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::Runtime)?; + let entry = this.allocate_ptr( + Size::from_bytes(size), + dirent64_layout.align.abi, + MiriMemoryKind::Runtime.into(), + )?; + let entry: Pointer> = entry.into(); // If the host is a Unix system, fill in the inode number with its real value. // If not, use 0 as a fallback value. @@ -949,23 +952,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?; this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?; - entry + Some(entry) } None => { // end of stream: return NULL - Pointer::null() + None } Some(Err(e)) => { this.set_last_error_from_io_error(e.kind())?; - Pointer::null() + None } }; let open_dir = this.machine.dirs.streams.get_mut(&dirp).unwrap(); let old_entry = std::mem::replace(&mut open_dir.entry, entry); - this.free(old_entry, MiriMemoryKind::Runtime)?; + if let Some(old_entry) = old_entry { + this.deallocate_ptr(old_entry, None, MiriMemoryKind::Runtime.into())?; + } - Ok(Scalar::from_maybe_pointer(entry, this)) + Ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this)) } fn macos_fbsd_readdir_r( @@ -1106,7 +1111,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { - this.free(open_dir.entry, MiriMemoryKind::Runtime)?; + if let Some(entry) = open_dir.entry { + this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; + } drop(open_dir); Ok(0) } else { diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index ed27066aa6a0f..d4a6cd96f48df 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -107,16 +107,22 @@ pub fn futex<'tcx>( Some(if wait_bitset { // FUTEX_WAIT_BITSET uses an absolute timestamp. if realtime { - Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()) + CallbackTime::RealTime( + SystemTime::UNIX_EPOCH.checked_add(duration).unwrap(), + ) } else { - Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap()) + CallbackTime::Monotonic( + this.machine.clock.anchor().checked_add(duration).unwrap(), + ) } } else { // FUTEX_WAIT uses a relative timestamp. if realtime { - Time::RealTime(SystemTime::now().checked_add(duration).unwrap()) + CallbackTime::RealTime(SystemTime::now().checked_add(duration).unwrap()) } else { - Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap()) + CallbackTime::Monotonic( + this.machine.clock.now().checked_add(duration).unwrap(), + ) } }) }; @@ -169,7 +175,7 @@ pub fn futex<'tcx>( let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Relaxed)?.to_i32()?; if val == futex_val { // The value still matches, so we block the thread make it wait for FUTEX_WAKE. - this.block_thread(thread); + this.block_thread(thread, BlockReason::Futex { addr: addr_usize }); this.futex_wait(addr_usize, thread, bitset); // Succesfully waking up from FUTEX_WAIT always returns zero. this.write_scalar(Scalar::from_target_isize(0, this), dest)?; @@ -191,7 +197,10 @@ pub fn futex<'tcx>( impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> { fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - this.unblock_thread(self.thread); + this.unblock_thread( + self.thread, + BlockReason::Futex { addr: self.addr_usize }, + ); this.futex_remove_waiter(self.addr_usize, self.thread); let etimedout = this.eval_libc("ETIMEDOUT"); this.set_last_error(etimedout)?; @@ -249,7 +258,7 @@ pub fn futex<'tcx>( #[allow(clippy::arithmetic_side_effects)] for _ in 0..val { if let Some(thread) = this.futex_wake(addr_usize, bitset) { - this.unblock_thread(thread); + this.unblock_thread(thread, BlockReason::Futex { addr: addr_usize }); this.unregister_timeout_callback_if_exists(thread); n += 1; } else { diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index dd301f9ee6d36..e50a8934e09d0 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -1,6 +1,5 @@ use std::time::SystemTime; -use crate::concurrency::sync::CondvarLock; use crate::concurrency::thread::MachineCallback; use crate::*; @@ -225,9 +224,10 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>( fn reacquire_cond_mutex<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, thread: ThreadId, + condvar: CondvarId, mutex: MutexId, ) -> InterpResult<'tcx> { - ecx.unblock_thread(thread); + ecx.unblock_thread(thread, BlockReason::Condvar(condvar)); if ecx.mutex_is_locked(mutex) { ecx.mutex_enqueue_and_block(mutex, thread); } else { @@ -242,9 +242,10 @@ fn reacquire_cond_mutex<'mir, 'tcx: 'mir>( fn post_cond_signal<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, thread: ThreadId, + condvar: CondvarId, mutex: MutexId, ) -> InterpResult<'tcx> { - reacquire_cond_mutex(ecx, thread, mutex)?; + reacquire_cond_mutex(ecx, thread, condvar, mutex)?; // Waiting for the mutex is not included in the waiting time because we need // to acquire the mutex always even if we get a timeout. ecx.unregister_timeout_callback_if_exists(thread); @@ -256,6 +257,7 @@ fn post_cond_signal<'mir, 'tcx: 'mir>( fn release_cond_mutex_and_block<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, active_thread: ThreadId, + condvar: CondvarId, mutex: MutexId, ) -> InterpResult<'tcx> { if let Some(old_locked_count) = ecx.mutex_unlock(mutex, active_thread) { @@ -265,7 +267,7 @@ fn release_cond_mutex_and_block<'mir, 'tcx: 'mir>( } else { throw_ub_format!("awaiting on unlocked or owned by a different thread mutex"); } - ecx.block_thread(active_thread); + ecx.block_thread(active_thread, BlockReason::Condvar(condvar)); Ok(()) } @@ -792,12 +794,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; - if let Some((thread, lock)) = this.condvar_signal(id) { - if let CondvarLock::Mutex(mutex) = lock { - post_cond_signal(this, thread, mutex)?; - } else { - panic!("condvar should not have an rwlock on unix"); - } + if let Some((thread, mutex)) = this.condvar_signal(id) { + post_cond_signal(this, thread, id, mutex)?; } Ok(0) @@ -810,12 +808,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; - while let Some((thread, lock)) = this.condvar_signal(id) { - if let CondvarLock::Mutex(mutex) = lock { - post_cond_signal(this, thread, mutex)?; - } else { - panic!("condvar should not have an rwlock on unix"); - } + while let Some((thread, mutex)) = this.condvar_signal(id) { + post_cond_signal(this, thread, id, mutex)?; } Ok(0) @@ -832,8 +826,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mutex_id = mutex_get_id(this, mutex_op)?; let active_thread = this.get_active_thread(); - release_cond_mutex_and_block(this, active_thread, mutex_id)?; - this.condvar_wait(id, active_thread, CondvarLock::Mutex(mutex_id)); + release_cond_mutex_and_block(this, active_thread, id, mutex_id)?; + this.condvar_wait(id, active_thread, mutex_id); Ok(0) } @@ -866,15 +860,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME") { this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; - Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()) + CallbackTime::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()) } else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") { - Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap()) + CallbackTime::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap()) } else { throw_unsup_format!("unsupported clock id: {}", clock_id); }; - release_cond_mutex_and_block(this, active_thread, mutex_id)?; - this.condvar_wait(id, active_thread, CondvarLock::Mutex(mutex_id)); + release_cond_mutex_and_block(this, active_thread, id, mutex_id)?; + this.condvar_wait(id, active_thread, mutex_id); // We return success for now and override it in the timeout callback. this.write_scalar(Scalar::from_i32(0), dest)?; @@ -897,7 +891,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { // We are not waiting for the condvar any more, wait for the // mutex instead. - reacquire_cond_mutex(ecx, self.active_thread, self.mutex_id)?; + reacquire_cond_mutex(ecx, self.active_thread, self.id, self.mutex_id)?; // Remove the thread from the conditional variable. ecx.condvar_remove_waiter(self.id, self.active_thread); diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index de80df3c80d6c..ec4c61014877e 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -8,6 +8,7 @@ use rustc_span::Symbol; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; +use crate::shims::alloc::EvalContextExt as _; use crate::shims::os_str::bytes_to_os_str; use crate::*; use shims::foreign_items::EmulateForeignItemResult; diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index f02939f888ec3..836b9e9259591 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -170,7 +170,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None } else { let duration = Duration::from_millis(timeout_ms.into()); - Some(Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())) + Some(CallbackTime::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())) }; // See the Linux futex implementation for why this fence exists. @@ -183,7 +183,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if futex_val == compare_val { // If the values are the same, we have to block. - this.block_thread(thread); + this.block_thread(thread, BlockReason::Futex { addr }); this.futex_wait(addr, thread, u32::MAX); if let Some(timeout_time) = timeout_time { @@ -202,7 +202,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> { fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - this.unblock_thread(self.thread); + this.unblock_thread(self.thread, BlockReason::Futex { addr: self.addr }); this.futex_remove_waiter(self.addr, self.thread); let error_timeout = this.eval_windows("c", "ERROR_TIMEOUT"); this.set_last_error(error_timeout)?; @@ -233,8 +233,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // See the Linux futex implementation for why this fence exists. this.atomic_fence(AtomicFenceOrd::SeqCst)?; - if let Some(thread) = this.futex_wake(ptr.addr().bytes(), u32::MAX) { - this.unblock_thread(thread); + let addr = ptr.addr().bytes(); + if let Some(thread) = this.futex_wake(addr, u32::MAX) { + this.unblock_thread(thread, BlockReason::Futex { addr }); this.unregister_timeout_callback_if_exists(thread); } @@ -248,8 +249,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // See the Linux futex implementation for why this fence exists. this.atomic_fence(AtomicFenceOrd::SeqCst)?; - while let Some(thread) = this.futex_wake(ptr.addr().bytes(), u32::MAX) { - this.unblock_thread(thread); + let addr = ptr.addr().bytes(); + while let Some(thread) = this.futex_wake(addr, u32::MAX) { + this.unblock_thread(thread, BlockReason::Futex { addr }); this.unregister_timeout_callback_if_exists(thread); } diff --git a/src/tools/miri/test-cargo-miri/test.bin-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.bin-target.stdout.ref index 5264530160bc5..6f480259965b9 100644 --- a/src/tools/miri/test-cargo-miri/test.bin-target.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.bin-target.stdout.ref @@ -3,5 +3,5 @@ running 2 tests test test::dev_dependency ... ok test test::exported_symbol ... ok -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref index 8c543e479f4e0..2ef124e4de8f7 100644 --- a/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref @@ -1,11 +1,11 @@ running 2 tests .. -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME imported main running 6 tests ...i.. -test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/src/tools/miri/test-cargo-miri/test.default.stdout.ref b/src/tools/miri/test-cargo-miri/test.default.stdout.ref index 922d2120bed0f..2d74d82f769ba 100644 --- a/src/tools/miri/test-cargo-miri/test.default.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.default.stdout.ref @@ -1,13 +1,13 @@ running 2 tests .. -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME imported main running 6 tests ...i.. -test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME running 5 tests diff --git a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref index bb0282d6c9167..59b4deb1ff32b 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref @@ -1,12 +1,12 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in $TIME imported main running 1 test test simple ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref index 5c819dd532366..b68bc983276f7 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref @@ -1,14 +1,14 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in $TIME imported main running 1 test test simple ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME running 0 tests diff --git a/src/tools/miri/test-cargo-miri/test.subcrate.stdout.ref b/src/tools/miri/test-cargo-miri/test.subcrate.stdout.ref index 67e5c7f8e920c..e50838ebc838b 100644 --- a/src/tools/miri/test-cargo-miri/test.subcrate.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.subcrate.stdout.ref @@ -1,6 +1,6 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME subcrate testing diff --git a/src/tools/miri/test-cargo-miri/test.test-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.test-target.stdout.ref index dd59b32b780c8..38b3f5c0989e7 100644 --- a/src/tools/miri/test-cargo-miri/test.test-target.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.test-target.stdout.ref @@ -7,5 +7,5 @@ test does_not_work_on_miri ... ignored test fail_index_check - should panic ... ok test simple ... ok -test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs index 910e06222ee76..532bda201364f 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs @@ -1,6 +1,7 @@ //@only-target-windows: Uses win32 api functions // We are making scheduler assumptions here. //@compile-flags: -Zmiri-preemption-rate=0 +//@error-in-other-file: deadlock // On windows, joining main is not UB, but it will block a thread forever. diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index d9137ee74376c..12f35fdeb02a7 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -8,7 +8,28 @@ LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJ = note: inside closure at RUSTLIB/core/src/macros/mod.rs:LL:CC = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/windows_join_main.rs:LL:CC + | +LL | / thread::spawn(|| { +LL | | unsafe { +LL | | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0); +LL | | } +LL | | }) +LL | | .join() + | |___________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs index a7c8faf5a98ba..a64265ca0ca5e 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs @@ -1,6 +1,7 @@ //@only-target-windows: Uses win32 api functions // We are making scheduler assumptions here. //@compile-flags: -Zmiri-preemption-rate=0 +//@error-in-other-file: deadlock // On windows, a thread joining itself is not UB, but it will deadlock. diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr index 74699a0317fff..8d26c35de8ab2 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr @@ -7,7 +7,29 @@ LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0 = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at $DIR/windows_join_self.rs:LL:CC +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/windows_join_self.rs:LL:CC + | +LL | / thread::spawn(|| { +LL | | unsafe { +LL | | let native = GetCurrentThread(); +LL | | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0); +LL | | } +LL | | }) +LL | | .join() + | |___________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/realloc-zero.rs b/src/tools/miri/tests/fail-dep/realloc-zero.rs new file mode 100644 index 0000000000000..1482798e90c08 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/realloc-zero.rs @@ -0,0 +1,10 @@ +//@ignore-target-windows: No libc on Windows + +fn main() { + unsafe { + let p1 = libc::malloc(20); + // C made this UB... + let p2 = libc::realloc(p1, 0); //~ERROR: `realloc` with a size of zero + assert!(p2.is_null()); + } +} diff --git a/src/tools/miri/tests/fail-dep/realloc-zero.stderr b/src/tools/miri/tests/fail-dep/realloc-zero.stderr new file mode 100644 index 0000000000000..749a61f7396e7 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/realloc-zero.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `realloc` with a size of zero + --> $DIR/realloc-zero.rs:LL:CC + | +LL | let p2 = libc::realloc(p1, 0); + | ^^^^^^^^^^^^^^^^^^^^ `realloc` with a size of zero + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/realloc-zero.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs index 6c3cb738e2997..60d56d41fd986 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs @@ -1,4 +1,5 @@ //@ignore-target-windows: No libc on Windows +//@error-in-other-file: deadlock use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr index 76b1d26bd3326..987d0fc4c2d04 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr @@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _ = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at $DIR/libc_pthread_mutex_deadlock.rs:LL:CC +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/libc_pthread_mutex_deadlock.rs:LL:CC + | +LL | / thread::spawn(move || { +LL | | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0); +LL | | }) +LL | | .join() + | |_______________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs index 201844615e182..0f02c3231a688 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs @@ -1,4 +1,5 @@ //@ignore-target-windows: No libc on Windows +//@error-in-other-file: deadlock use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr index 5501dab81aca3..bc9b15f293ef8 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr @@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC + | +LL | / thread::spawn(move || { +LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); +LL | | }) +LL | | .join() + | |_______________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs index b1d7e0492e5a2..10be5b3375230 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs @@ -1,4 +1,5 @@ //@ignore-target-windows: No libc on Windows +//@error-in-other-file: deadlock use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr index 815d85af502c9..66c142bbc5c80 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr @@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC + | +LL | / thread::spawn(move || { +LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); +LL | | }) +LL | | .join() + | |_______________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs new file mode 100644 index 0000000000000..2097126e16b9b --- /dev/null +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs @@ -0,0 +1,10 @@ +//@error-in-other-file: aborted +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +#![feature(allocator_api)] + +use std::alloc::*; + +fn main() { + handle_alloc_error(Layout::for_value(&0)); +} diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr new file mode 100644 index 0000000000000..d1731a0f4206f --- /dev/null +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr @@ -0,0 +1,24 @@ +memory allocation of 4 bytes failed +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC + = note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC + = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC +note: inside `main` + --> $DIR/alloc_error_handler.rs:LL:CC + | +LL | handle_alloc_error(Layout::for_value(&0)); + | ^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.rs new file mode 100644 index 0000000000000..babdb73f093a1 --- /dev/null +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.rs @@ -0,0 +1,49 @@ +//@compile-flags: -Cpanic=abort +#![feature(start, core_intrinsics)] +#![feature(alloc_error_handler)] +#![feature(allocator_api)] +#![no_std] + +extern crate alloc; + +use alloc::alloc::*; +use core::fmt::Write; + +#[path = "../../utils/mod.no_std.rs"] +mod utils; + +#[alloc_error_handler] +fn alloc_error_handler(layout: Layout) -> ! { + let _ = writeln!(utils::MiriStderr, "custom alloc error handler: {layout:?}"); + core::intrinsics::abort(); //~ERROR: aborted +} + +// rustc requires us to provide some more things that aren't actually used by this test +mod plumbing { + use super::*; + + #[panic_handler] + fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} + } + + struct NoAlloc; + + unsafe impl GlobalAlloc for NoAlloc { + unsafe fn alloc(&self, _: Layout) -> *mut u8 { + unreachable!(); + } + + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + unreachable!(); + } + } + + #[global_allocator] + static GLOBAL: NoAlloc = NoAlloc; +} + +#[start] +fn start(_: isize, _: *const *const u8) -> isize { + handle_alloc_error(Layout::for_value(&0)); +} diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr new file mode 100644 index 0000000000000..5d9c2e2fb4c38 --- /dev/null +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr @@ -0,0 +1,27 @@ +custom alloc error handler: Layout { size: 4, align: 4 (1 << 2) } +error: abnormal termination: the program aborted execution + --> $DIR/alloc_error_handler_custom.rs:LL:CC + | +LL | core::intrinsics::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution + | + = note: BACKTRACE: + = note: inside `alloc_error_handler` at $DIR/alloc_error_handler_custom.rs:LL:CC +note: inside `_::__rg_oom` + --> $DIR/alloc_error_handler_custom.rs:LL:CC + | +LL | #[alloc_error_handler] + | ---------------------- in this procedural macro expansion +LL | fn alloc_error_handler(layout: Layout) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC +note: inside `start` + --> $DIR/alloc_error_handler_custom.rs:LL:CC + | +LL | handle_alloc_error(Layout::for_value(&0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.rs new file mode 100644 index 0000000000000..18a8a61f22f65 --- /dev/null +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Cpanic=abort +#![feature(start, core_intrinsics)] +#![feature(alloc_error_handler)] +#![feature(allocator_api)] +#![no_std] + +extern crate alloc; + +use alloc::alloc::*; +use core::fmt::Write; + +#[path = "../../utils/mod.no_std.rs"] +mod utils; + +// The default no_std alloc_error_handler is a panic. + +#[panic_handler] +fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { + let _ = writeln!(utils::MiriStderr, "custom panic handler called!"); + let _ = writeln!(utils::MiriStderr, "{panic_info}"); + core::intrinsics::abort(); //~ERROR: aborted +} + +// rustc requires us to provide some more things that aren't actually used by this test +mod plumbing { + use super::*; + + struct NoAlloc; + + unsafe impl GlobalAlloc for NoAlloc { + unsafe fn alloc(&self, _: Layout) -> *mut u8 { + unreachable!(); + } + + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + unreachable!(); + } + } + + #[global_allocator] + static GLOBAL: NoAlloc = NoAlloc; +} + +#[start] +fn start(_: isize, _: *const *const u8) -> isize { + handle_alloc_error(Layout::for_value(&0)); +} diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr new file mode 100644 index 0000000000000..6b98f6f75d853 --- /dev/null +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr @@ -0,0 +1,24 @@ +custom panic handler called! +panicked at RUSTLIB/alloc/src/alloc.rs:LL:CC: +memory allocation of 4 bytes failed +error: abnormal termination: the program aborted execution + --> $DIR/alloc_error_handler_no_std.rs:LL:CC + | +LL | core::intrinsics::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution + | + = note: BACKTRACE: + = note: inside `panic_handler` at $DIR/alloc_error_handler_no_std.rs:LL:CC + = note: inside `alloc::alloc::__alloc_error_handler::__rdl_oom` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC +note: inside `start` + --> $DIR/alloc_error_handler_no_std.rs:LL:CC + | +LL | handle_alloc_error(Layout::for_value(&0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs index 3edaf10f3dc69..0061679eaa4e7 100644 --- a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs +++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs @@ -1,6 +1,8 @@ //! Make sure that a retag acts like a write for the data race model. //@revisions: stack tree //@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 //@[tree]compile-flags: -Zmiri-tree-borrows #[derive(Copy, Clone)] struct SendPtr(*mut u8); diff --git a/src/tools/miri/tests/fail/data_race/alloc_read_race.rs b/src/tools/miri/tests/fail/data_race/alloc_read_race.rs index 2cf366069073a..c85c0ebe24451 100644 --- a/src/tools/miri/tests/fail/data_race/alloc_read_race.rs +++ b/src/tools/miri/tests/fail/data_race/alloc_read_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 #![feature(new_uninit)] use std::mem::MaybeUninit; diff --git a/src/tools/miri/tests/fail/data_race/alloc_write_race.rs b/src/tools/miri/tests/fail/data_race/alloc_write_race.rs index e95e0e1a841d6..9e2a430dd94f5 100644 --- a/src/tools/miri/tests/fail/data_race/alloc_write_race.rs +++ b/src/tools/miri/tests/fail/data_race/alloc_write_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 #![feature(new_uninit)] use std::ptr::null_mut; diff --git a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs index a256267bcda09..4003892f0a609 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs index cc6a0742c23eb..8bceba9380adf 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs index 7392781e6c6b1..1a2746a26f4d8 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs index f681ce0c051ad..e0876a93fdd8c 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs index 47a3ef5d16897..1010216a4976b 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs index 8bba4a88924e6..b494bd3a003d3 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; diff --git a/src/tools/miri/tests/fail/data_race/dangling_thread_async_race.rs b/src/tools/miri/tests/fail/data_race/dangling_thread_async_race.rs index 5b9005606e0e7..dffafe3cfaa97 100644 --- a/src/tools/miri/tests/fail/data_race/dangling_thread_async_race.rs +++ b/src/tools/miri/tests/fail/data_race/dangling_thread_async_race.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::mem; use std::thread::{sleep, spawn}; diff --git a/src/tools/miri/tests/fail/data_race/dangling_thread_race.rs b/src/tools/miri/tests/fail/data_race/dangling_thread_race.rs index 91c1191e03636..8dc35c7ea720e 100644 --- a/src/tools/miri/tests/fail/data_race/dangling_thread_race.rs +++ b/src/tools/miri/tests/fail/data_race/dangling_thread_race.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::mem; use std::thread::{sleep, spawn}; diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs index 5928e47176050..f174909e9d56b 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs index c5f82cc9a74e5..1edfbf5e61c0d 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs index 1095f1e4e82fa..c67e03d362b05 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs index b5911e5111b3d..7605f1911db77 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs index 7a2c882f7ecc4..4f3819bd636b2 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs index 5ee4cc04a8fb0..8e63bc1dc7b41 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs b/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs index f2da45d7275b0..53050608d2715 100644 --- a/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs +++ b/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/fence_after_load.rs b/src/tools/miri/tests/fail/data_race/fence_after_load.rs index 683e3b9c7ac66..92cb4ccccf530 100644 --- a/src/tools/miri/tests/fail/data_race/fence_after_load.rs +++ b/src/tools/miri/tests/fail/data_race/fence_after_load.rs @@ -1,5 +1,8 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{fence, AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read.rs b/src/tools/miri/tests/fail/data_race/mixed_size_read.rs index 091a47070bafb..61af972b3dca8 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_read.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_read.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; use std::thread; diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_write.rs b/src/tools/miri/tests/fail/data_race/mixed_size_write.rs index 49fb6c1d5c3e3..12e51bb94296a 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_write.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_write.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; use std::thread; diff --git a/src/tools/miri/tests/fail/data_race/read_read_race1.rs b/src/tools/miri/tests/fail/data_race/read_read_race1.rs index f66b5ca3d53af..02aa4e4b716e3 100644 --- a/src/tools/miri/tests/fail/data_race/read_read_race1.rs +++ b/src/tools/miri/tests/fail/data_race/read_read_race1.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{AtomicU16, Ordering}; use std::thread; diff --git a/src/tools/miri/tests/fail/data_race/read_read_race2.rs b/src/tools/miri/tests/fail/data_race/read_read_race2.rs index d87b667d91287..3b94c9143f3b2 100644 --- a/src/tools/miri/tests/fail/data_race/read_read_race2.rs +++ b/src/tools/miri/tests/fail/data_race/read_read_race2.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{AtomicU16, Ordering}; use std::thread; diff --git a/src/tools/miri/tests/fail/data_race/read_write_race.rs b/src/tools/miri/tests/fail/data_race/read_write_race.rs index 70971b59ffe89..adf19dda9d3db 100644 --- a/src/tools/miri/tests/fail/data_race/read_write_race.rs +++ b/src/tools/miri/tests/fail/data_race/read_write_race.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs index 9fec3ceee07fb..f411767f7b57e 100644 --- a/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs +++ b/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs b/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs index be4450794ca6c..c4f943808229c 100644 --- a/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs +++ b/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/release_seq_race.rs b/src/tools/miri/tests/fail/data_race/release_seq_race.rs index 9810832413ec5..f03ab3efa0624 100644 --- a/src/tools/miri/tests/fail/data_race/release_seq_race.rs +++ b/src/tools/miri/tests/fail/data_race/release_seq_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::{sleep, spawn}; diff --git a/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs b/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs index 93cbc2a57d6be..88ae01b3ca1c7 100644 --- a/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs +++ b/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/rmw_race.rs b/src/tools/miri/tests/fail/data_race/rmw_race.rs index 982e9c1c4109d..d738caa105879 100644 --- a/src/tools/miri/tests/fail/data_race/rmw_race.rs +++ b/src/tools/miri/tests/fail/data_race/rmw_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/stack_pop_race.rs b/src/tools/miri/tests/fail/data_race/stack_pop_race.rs index 68d82bc30a58d..762a8e51f692a 100644 --- a/src/tools/miri/tests/fail/data_race/stack_pop_race.rs +++ b/src/tools/miri/tests/fail/data_race/stack_pop_race.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::thread; #[derive(Copy, Clone)] diff --git a/src/tools/miri/tests/fail/data_race/write_write_race.rs b/src/tools/miri/tests/fail/data_race/write_write_race.rs index e8924702af818..993d8d25b4c17 100644 --- a/src/tools/miri/tests/fail/data_race/write_write_race.rs +++ b/src/tools/miri/tests/fail/data_race/write_write_race.rs @@ -1,5 +1,7 @@ // We want to control preemption here. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs index 984ae2ee83dce..8070a7f4fc2a0 100644 --- a/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs +++ b/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/src/tools/miri/tests/fail/panic/no_std.rs b/src/tools/miri/tests/fail/panic/no_std.rs index bad425804dc0a..4d32b6d746190 100644 --- a/src/tools/miri/tests/fail/panic/no_std.rs +++ b/src/tools/miri/tests/fail/panic/no_std.rs @@ -1,31 +1,11 @@ +//@compile-flags: -Cpanic=abort #![feature(start, core_intrinsics)] #![no_std] -//@compile-flags: -Cpanic=abort -// windows tls dtors go through libstd right now, thus this test -// cannot pass. When windows tls dtors go through the special magic -// windows linker section, we can run this test on windows again. -//@ignore-target-windows: no-std not supported on Windows - -// Plumbing to let us use `writeln!` to host stderr: - -extern "Rust" { - fn miri_write_to_stderr(bytes: &[u8]); -} - -struct HostErr; use core::fmt::Write; -impl Write for HostErr { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - unsafe { - miri_write_to_stderr(s.as_bytes()); - } - Ok(()) - } -} - -// Aaaand the test: +#[path = "../../utils/mod.no_std.rs"] +mod utils; #[start] fn start(_: isize, _: *const *const u8) -> isize { @@ -34,6 +14,6 @@ fn start(_: isize, _: *const *const u8) -> isize { #[panic_handler] fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { - writeln!(HostErr, "{panic_info}").ok(); + writeln!(utils::MiriStderr, "{panic_info}").ok(); core::intrinsics::abort(); //~ ERROR: the program aborted execution } diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs index 3de517055ec66..a6ee7b40c340b 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs @@ -1,4 +1,5 @@ -//@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 use std::thread; #[derive(Copy, Clone)] diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs index 25c92ddf6ca08..949f659e7e8e5 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs @@ -1,5 +1,6 @@ //! Make sure that a retag acts like a read for the data race model. -//@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 #[derive(Copy, Clone)] struct SendPtr(*mut u8); diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr index cca17a07ec208..960cae9012484 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr @@ -15,13 +15,13 @@ note: inside `main` LL | drop(slice1.cmp(slice2)); | ^^^^^^^^^^^^^^^^^^ -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - Uninitialized memory occurred at ALLOC[0x4..0x10], in this allocation: ALLOC (Rust heap, size: 32, align: 8) { 0x00 │ 41 42 43 44 __ __ __ __ __ __ __ __ __ __ __ __ │ ABCD░░░░░░░░░░░░ 0x10 │ 00 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ .░░░░░░░░░░░░░░░ } +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr index 4dc2d27ead433..5439418f26771 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr @@ -15,8 +15,6 @@ note: inside `main` LL | drop(slice1.cmp(slice2)); | ^^^^^^^^^^^^^^^^^^ -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation: ALLOC (Rust heap, size: 16, align: 8) { ╾42[ALLOC] (1 ptr byte)╼ 12 13 ╾43[ALLOC] (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░ @@ -28,5 +26,7 @@ ALLOC (global (static or const), size: 1, align: 1) { 00 │ . } +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.rs b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.rs index dfe9397a4c46a..1193dddc57784 100644 --- a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.rs +++ b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.rs @@ -1,5 +1,6 @@ // We want to control preemption here. -//@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 #![feature(core_intrinsics)] diff --git a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.rs b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.rs index b946a75c3abb3..0a0e372f1f3d2 100644 --- a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.rs +++ b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.rs @@ -1,5 +1,6 @@ // We want to control preemption here. -//@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::Ordering::*; use std::sync::atomic::{AtomicU16, AtomicU32}; diff --git a/src/tools/miri/tests/panic/alloc_error_handler_hook.rs b/src/tools/miri/tests/panic/alloc_error_handler_hook.rs new file mode 100644 index 0000000000000..a1eadb45fd13b --- /dev/null +++ b/src/tools/miri/tests/panic/alloc_error_handler_hook.rs @@ -0,0 +1,20 @@ +#![feature(allocator_api, alloc_error_hook)] + +use std::alloc::*; + +struct Bomb; +impl Drop for Bomb { + fn drop(&mut self) { + eprintln!("yes we are unwinding!"); + } +} + +#[allow(unreachable_code, unused_variables)] +fn main() { + // This is a particularly tricky hook, since it unwinds, which the default one does not. + set_alloc_error_hook(|_layout| panic!("alloc error hook called")); + + let bomb = Bomb; + handle_alloc_error(Layout::for_value(&0)); + std::mem::forget(bomb); // defuse unwinding bomb +} diff --git a/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr b/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr new file mode 100644 index 0000000000000..5b309ed09bb42 --- /dev/null +++ b/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr @@ -0,0 +1,5 @@ +thread 'main' panicked at $DIR/alloc_error_handler_hook.rs:LL:CC: +alloc error hook called +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect +yes we are unwinding! diff --git a/src/tools/miri/tests/panic/alloc_error_handler_panic.rs b/src/tools/miri/tests/panic/alloc_error_handler_panic.rs new file mode 100644 index 0000000000000..c434e8d3227a2 --- /dev/null +++ b/src/tools/miri/tests/panic/alloc_error_handler_panic.rs @@ -0,0 +1,18 @@ +//@compile-flags: -Zoom=panic +#![feature(allocator_api)] + +use std::alloc::*; + +struct Bomb; +impl Drop for Bomb { + fn drop(&mut self) { + eprintln!("yes we are unwinding!"); + } +} + +#[allow(unreachable_code, unused_variables)] +fn main() { + let bomb = Bomb; + handle_alloc_error(Layout::for_value(&0)); + std::mem::forget(bomb); // defuse unwinding bomb +} diff --git a/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr b/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr new file mode 100644 index 0000000000000..3d5457799f60d --- /dev/null +++ b/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr @@ -0,0 +1,5 @@ +thread 'main' panicked at RUSTLIB/std/src/alloc.rs:LL:CC: +memory allocation of 4 bytes failed +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect +yes we are unwinding! diff --git a/src/tools/miri/tests/panic/mir-validation.rs b/src/tools/miri/tests/panic/mir-validation.rs new file mode 100644 index 0000000000000..5c1519f2eb9db --- /dev/null +++ b/src/tools/miri/tests/panic/mir-validation.rs @@ -0,0 +1,23 @@ +//! Ensure that the MIR validator runs on Miri's input. +//@rustc-env:RUSTC_ICE=0 +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" +//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\]" -> "" +//@normalize-stderr-test: "\n[ =]*note:.*" -> "" +//@normalize-stderr-test: "DefId\([^()]*\)" -> "DefId" +#![feature(custom_mir, core_intrinsics)] +use core::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn main() { + mir! { + let x: i32; + let tuple: (*mut i32,); + { + tuple.0 = core::ptr::addr_of_mut!(x); + // Deref at the wrong place! + *(tuple.0) = 1; + Return() + } + } +} diff --git a/src/tools/miri/tests/panic/mir-validation.stderr b/src/tools/miri/tests/panic/mir-validation.stderr new file mode 100644 index 0000000000000..d158c996dc3de --- /dev/null +++ b/src/tools/miri/tests/panic/mir-validation.stderr @@ -0,0 +1,21 @@ +thread 'rustc' panicked at compiler/rustc_const_eval/src/transform/validate.rs:LL:CC: +broken MIR in Item(DefId) (after phase change to runtime-optimized) at bb0[1]: +(*(_2.0: *mut i32)), has deref at the wrong place +stack backtrace: + +error: the compiler unexpectedly panicked. this is a bug. + + + + +query stack during panic: +#0 [optimized_mir] optimizing MIR for `main` +end of query stack + +Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic: + --> RUSTLIB/core/src/ops/function.rs:LL:CC + | +LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + diff --git a/src/tools/miri/tests/pass-dep/malloc.rs b/src/tools/miri/tests/pass-dep/malloc.rs index f5e014c000d15..35cd137931fd6 100644 --- a/src/tools/miri/tests/pass-dep/malloc.rs +++ b/src/tools/miri/tests/pass-dep/malloc.rs @@ -34,9 +34,8 @@ fn main() { } unsafe { - let p1 = libc::malloc(20); - - let p2 = libc::realloc(p1, 0); + // Realloc with size 0 is okay for the null pointer + let p2 = libc::realloc(ptr::null_mut(), 0); assert!(p2.is_null()); } diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.rs b/src/tools/miri/tests/pass/alloc-access-tracking.rs index 5c782fca2df47..29c1ee2f7b765 100644 --- a/src/tools/miri/tests/pass/alloc-access-tracking.rs +++ b/src/tools/miri/tests/pass/alloc-access-tracking.rs @@ -1,6 +1,6 @@ #![feature(start)] #![no_std] -//@compile-flags: -Zmiri-track-alloc-id=17 -Zmiri-track-alloc-accesses -Cpanic=abort +//@compile-flags: -Zmiri-track-alloc-id=18 -Zmiri-track-alloc-accesses -Cpanic=abort //@only-target-linux: alloc IDs differ between OSes for some reason extern "Rust" { diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.stderr b/src/tools/miri/tests/pass/alloc-access-tracking.stderr index 5e219fa1bedd0..bef13701ea2c4 100644 --- a/src/tools/miri/tests/pass/alloc-access-tracking.stderr +++ b/src/tools/miri/tests/pass/alloc-access-tracking.stderr @@ -2,7 +2,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | let ptr = miri_alloc(123, 1); - | ^^^^^^^^^^^^^^^^^^ created Miri bare-metal heap allocation of 123 bytes (alignment ALIGN bytes) with id 17 + | ^^^^^^^^^^^^^^^^^^ created Miri bare-metal heap allocation of 123 bytes (alignment ALIGN bytes) with id 18 | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC @@ -11,7 +11,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | *ptr = 42; // Crucially, only a write is printed here, no read! - | ^^^^^^^^^ write access to allocation with id 17 + | ^^^^^^^^^ write access to allocation with id 18 | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC @@ -20,7 +20,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | assert_eq!(*ptr, 42); - | ^^^^^^^^^^^^^^^^^^^^ read access to allocation with id 17 + | ^^^^^^^^^^^^^^^^^^^^ read access to allocation with id 18 | = note: BACKTRACE: = note: inside `start` at RUSTLIB/core/src/macros/mod.rs:LL:CC @@ -30,7 +30,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | miri_dealloc(ptr, 123, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ freed allocation with id 17 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ freed allocation with id 18 | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC diff --git a/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs new file mode 100644 index 0000000000000..cfc1ef7ae455a --- /dev/null +++ b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs @@ -0,0 +1,61 @@ +//! Regression test for : +//! When the address gets reused, there should be a happens-before relation. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=1.0 +#![feature(strict_provenance)] +#![feature(sync_unsafe_cell)] + +use std::cell::SyncUnsafeCell; +use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use std::thread; + +static ADDR: AtomicUsize = AtomicUsize::new(0); +static VAL: SyncUnsafeCell = SyncUnsafeCell::new(0); + +fn addr() -> usize { + let alloc = Box::new(42); + <*const i32>::addr(&*alloc) +} + +fn thread1() { + unsafe { + VAL.get().write(42); + } + let alloc = addr(); + ADDR.store(alloc, Relaxed); +} + +fn thread2() -> bool { + // We try to get an allocation at the same address as the global `ADDR`. If we fail too often, + // just bail. `main` will try again with a different allocation. + for _ in 0..16 { + let alloc = addr(); + let addr = ADDR.load(Relaxed); + if alloc == addr { + // We got a reuse! + // If the new allocation is at the same address as the old one, there must be a + // happens-before relationship between them. Therefore, we can read VAL without racing + // and must observe the write above. + let val = unsafe { VAL.get().read() }; + assert_eq!(val, 42); + return true; + } + } + + false +} + +fn main() { + let mut success = false; + while !success { + let t1 = thread::spawn(thread1); + let t2 = thread::spawn(thread2); + t1.join().unwrap(); + success = t2.join().unwrap(); + + // Reset everything. + ADDR.store(0, Relaxed); + unsafe { + VAL.get().write(0); + } + } +} diff --git a/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs b/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs index 049b5e7f49871..354a4bef932e9 100644 --- a/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs +++ b/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-data-race-detector +// Avoid non-determinism +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs new file mode 100644 index 0000000000000..43ddc8a4d8b55 --- /dev/null +++ b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs @@ -0,0 +1,21 @@ +#![feature(custom_mir, core_intrinsics, strict_provenance)] +use std::intrinsics::mir::*; + +// The `Drop` terminator on a type with no drop glue should be a NOP. + +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn drop_in_place_with_terminator(ptr: *mut i32) { + mir! { + { + Drop(*ptr, ReturnTo(after_call), UnwindContinue()) + } + after_call = { + Return() + } + } +} + +pub fn main() { + drop_in_place_with_terminator(std::ptr::without_provenance_mut(0)); + drop_in_place_with_terminator(std::ptr::without_provenance_mut(1)); +} diff --git a/src/tools/miri/tests/pass/no_std.rs b/src/tools/miri/tests/pass/no_std.rs index 3c98ee50aa9c0..fc1c16f5fb956 100644 --- a/src/tools/miri/tests/pass/no_std.rs +++ b/src/tools/miri/tests/pass/no_std.rs @@ -2,30 +2,14 @@ #![feature(start)] #![no_std] -// Plumbing to let us use `writeln!` to host stdout: - -extern "Rust" { - fn miri_write_to_stdout(bytes: &[u8]); -} - -struct Host; - use core::fmt::Write; -impl Write for Host { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - unsafe { - miri_write_to_stdout(s.as_bytes()); - } - Ok(()) - } -} - -// Aaaand the test: +#[path = "../utils/mod.no_std.rs"] +mod utils; #[start] fn start(_: isize, _: *const *const u8) -> isize { - writeln!(Host, "hello, world!").unwrap(); + writeln!(utils::MiriStdout, "hello, world!").unwrap(); 0 } diff --git a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs index 43ba490d5bb42..c75824d7f9be0 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs @@ -265,13 +265,15 @@ fn write_does_not_invalidate_all_aliases() { assert_eq!(*x, 1337); // oops, the value changed! I guess not all pointers were invalidated } -fn box_into_raw_allows_interior_mutable_alias() { unsafe { - let b = Box::new(std::cell::Cell::new(42)); - let raw = Box::into_raw(b); - let c = &*raw; - let d = raw.cast::(); // bypassing `Cell` -- only okay in Miri tests - // `c` and `d` should permit arbitrary aliasing with each other now. - *d = 1; - c.set(2); - drop(Box::from_raw(raw)); -} } +fn box_into_raw_allows_interior_mutable_alias() { + unsafe { + let b = Box::new(std::cell::Cell::new(42)); + let raw = Box::into_raw(b); + let c = &*raw; + let d = raw.cast::(); // bypassing `Cell` -- only okay in Miri tests + // `c` and `d` should permit arbitrary aliasing with each other now. + *d = 1; + c.set(2); + drop(Box::from_raw(raw)); + } +} diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.rs b/src/tools/miri/tests/pass/tree_borrows/reserved.rs index 87ce91a809f22..f93cac8361e31 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reserved.rs +++ b/src/tools/miri/tests/pass/tree_borrows/reserved.rs @@ -27,9 +27,8 @@ fn main() { } } -unsafe fn print(msg: &str) { - utils::miri_write_to_stderr(msg.as_bytes()); - utils::miri_write_to_stderr("\n".as_bytes()); +fn print(msg: &str) { + eprintln!("{msg}"); } unsafe fn read_second(x: &mut T, y: *mut u8) { diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs index e10ccc277f6f1..dac63eeeb0b24 100644 --- a/src/tools/miri/tests/pass/weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/weak_memory/weak.rs @@ -37,6 +37,8 @@ fn relaxed() -> bool { let x = static_atomic(0); let j1 = spawn(move || { x.store(1, Relaxed); + // Preemption is disabled, so the store above will never be the + // latest store visible to another thread. x.store(2, Relaxed); }); @@ -138,6 +140,7 @@ fn faa_replaced_by_load() -> bool { } /// Asserts that the function returns true at least once in 100 runs +#[track_caller] fn assert_once(f: fn() -> bool) { assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x)); } diff --git a/src/tools/miri/tests/utils/io.rs b/src/tools/miri/tests/utils/io.rs new file mode 100644 index 0000000000000..e3eaa6c468a1a --- /dev/null +++ b/src/tools/miri/tests/utils/io.rs @@ -0,0 +1,25 @@ +use core::fmt::{self, Write}; + +use super::miri_extern; + +pub struct MiriStderr; + +impl Write for MiriStderr { + fn write_str(&mut self, s: &str) -> fmt::Result { + unsafe { + miri_extern::miri_write_to_stderr(s.as_bytes()); + } + Ok(()) + } +} + +pub struct MiriStdout; + +impl Write for MiriStdout { + fn write_str(&mut self, s: &str) -> fmt::Result { + unsafe { + miri_extern::miri_write_to_stdout(s.as_bytes()); + } + Ok(()) + } +} diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index e2983f6c71a7e..d6c43b1882195 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -133,8 +133,8 @@ extern "Rust" { /// with a null terminator. /// Returns 0 if the `out` buffer was large enough, and the required size otherwise. pub fn miri_host_to_target_path( - path: *const std::ffi::c_char, - out: *mut std::ffi::c_char, + path: *const core::ffi::c_char, + out: *mut core::ffi::c_char, out_size: usize, ) -> usize; diff --git a/src/tools/miri/tests/utils/mod.no_std.rs b/src/tools/miri/tests/utils/mod.no_std.rs new file mode 100644 index 0000000000000..aaf2bf50c4e67 --- /dev/null +++ b/src/tools/miri/tests/utils/mod.no_std.rs @@ -0,0 +1,11 @@ +#![allow(dead_code)] +#![allow(unused_imports)] + +#[macro_use] +mod macros; + +mod io; +mod miri_extern; + +pub use self::io::*; +pub use self::miri_extern::*; diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs index cb9380f5753ce..138ada4e20d7a 100644 --- a/src/tools/miri/tests/utils/mod.rs +++ b/src/tools/miri/tests/utils/mod.rs @@ -5,9 +5,11 @@ mod macros; mod fs; +mod io; mod miri_extern; pub use self::fs::*; +pub use self::io::*; pub use self::miri_extern::*; pub fn run_provenance_gc() { diff --git a/tests/coverage/branch_generics.cov-map b/tests/coverage/branch/generics.cov-map similarity index 94% rename from tests/coverage/branch_generics.cov-map rename to tests/coverage/branch/generics.cov-map index 719e97efad455..d729b0c260a54 100644 --- a/tests/coverage/branch_generics.cov-map +++ b/tests/coverage/branch/generics.cov-map @@ -1,4 +1,4 @@ -Function name: branch_generics::print_size::<()> +Function name: generics::print_size::<()> Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 @@ -16,7 +16,7 @@ Number of file 0 mappings: 5 - Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) = (c1 + (c0 - c1)) -Function name: branch_generics::print_size:: +Function name: generics::print_size:: Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 @@ -34,7 +34,7 @@ Number of file 0 mappings: 5 - Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) = (c1 + (c0 - c1)) -Function name: branch_generics::print_size:: +Function name: generics::print_size:: Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 diff --git a/tests/coverage/branch_generics.coverage b/tests/coverage/branch/generics.coverage similarity index 94% rename from tests/coverage/branch_generics.coverage rename to tests/coverage/branch/generics.coverage index e7cec151ce62c..85f73d45f65e0 100644 --- a/tests/coverage/branch_generics.coverage +++ b/tests/coverage/branch/generics.coverage @@ -16,7 +16,7 @@ LL| 2| } LL| 3|} ------------------ - | branch_generics::print_size::<()>: + | generics::print_size::<()>: | LL| 1|fn print_size() { | LL| 1| if std::mem::size_of::() > 4 { | ------------------ @@ -28,7 +28,7 @@ | LL| 1| } | LL| 1|} ------------------ - | branch_generics::print_size::: + | generics::print_size::: | LL| 1|fn print_size() { | LL| 1| if std::mem::size_of::() > 4 { | ------------------ @@ -40,7 +40,7 @@ | LL| 1| } | LL| 1|} ------------------ - | branch_generics::print_size::: + | generics::print_size::: | LL| 1|fn print_size() { | LL| 1| if std::mem::size_of::() > 4 { | ------------------ diff --git a/tests/coverage/branch_generics.rs b/tests/coverage/branch/generics.rs similarity index 100% rename from tests/coverage/branch_generics.rs rename to tests/coverage/branch/generics.rs diff --git a/tests/coverage/branch_guard.cov-map b/tests/coverage/branch/guard.cov-map similarity index 97% rename from tests/coverage/branch_guard.cov-map rename to tests/coverage/branch/guard.cov-map index 0b3622f6347a8..d67c3d349a14e 100644 --- a/tests/coverage/branch_guard.cov-map +++ b/tests/coverage/branch/guard.cov-map @@ -1,4 +1,4 @@ -Function name: branch_guard::branch_match_guard +Function name: guard::branch_match_guard Raw bytes (85): 0x[01, 01, 06, 19, 0d, 05, 09, 0f, 15, 13, 11, 17, 0d, 05, 09, 0d, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 15, 01, 14, 02, 0a, 0d, 03, 0e, 00, 0f, 19, 00, 14, 00, 19, 20, 0d, 02, 00, 14, 00, 1e, 0d, 00, 1d, 02, 0a, 11, 03, 0e, 00, 0f, 1d, 00, 14, 00, 19, 20, 11, 09, 00, 14, 00, 1e, 11, 00, 1d, 02, 0a, 17, 03, 0e, 02, 0a, 0b, 04, 01, 00, 02] Number of files: 1 - file 0 => global file 1 diff --git a/tests/coverage/branch_guard.coverage b/tests/coverage/branch/guard.coverage similarity index 100% rename from tests/coverage/branch_guard.coverage rename to tests/coverage/branch/guard.coverage diff --git a/tests/coverage/branch_guard.rs b/tests/coverage/branch/guard.rs similarity index 100% rename from tests/coverage/branch_guard.rs rename to tests/coverage/branch/guard.rs diff --git a/tests/coverage/branch_if.cov-map b/tests/coverage/branch/if.cov-map similarity index 98% rename from tests/coverage/branch_if.cov-map rename to tests/coverage/branch/if.cov-map index 0dbfd92541b03..50f6216e06980 100644 --- a/tests/coverage/branch_if.cov-map +++ b/tests/coverage/branch/if.cov-map @@ -1,4 +1,4 @@ -Function name: branch_if::branch_and +Function name: if::branch_and Raw bytes (56): 0x[01, 01, 04, 05, 09, 0d, 02, 11, 0f, 0d, 02, 08, 01, 2b, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 00, 0d, 00, 0e, 20, 11, 0d, 00, 0d, 00, 0e, 11, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 @@ -23,7 +23,7 @@ Number of file 0 mappings: 8 - Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2) = (c4 + (c3 + (c1 - c2))) -Function name: branch_if::branch_not +Function name: if::branch_not Raw bytes (224): 0x[01, 01, 29, 05, 09, 09, 02, a3, 01, 0d, 09, 02, a3, 01, 0d, 09, 02, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 15, 8e, 01, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, a3, 01, 01, 08, 00, 0a, 20, 9e, 01, 0d, 00, 08, 00, 0a, 9e, 01, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 9b, 01, 01, 08, 00, 0b, 20, 11, 96, 01, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 96, 01, 02, 06, 00, 07, 93, 01, 01, 08, 00, 0c, 20, 8e, 01, 15, 00, 08, 00, 0c, 8e, 01, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 8b, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 @@ -105,7 +105,7 @@ Number of file 0 mappings: 18 - Code(Expression(34, Add)) at (prev + 1, 1) to (start + 0, 2) = (c5 + ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5)) -Function name: branch_if::branch_not_as +Function name: if::branch_not_as Raw bytes (124): 0x[01, 01, 16, 05, 09, 09, 02, 57, 0d, 09, 02, 57, 0d, 09, 02, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 11, 4a, 4f, 11, 0d, 52, 57, 0d, 09, 02, 0e, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 20, 02, 09, 00, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 57, 01, 08, 00, 15, 20, 0d, 52, 00, 08, 00, 15, 0d, 00, 16, 02, 06, 52, 02, 06, 00, 07, 4f, 01, 08, 00, 16, 20, 4a, 11, 00, 08, 00, 16, 4a, 00, 17, 02, 06, 11, 02, 06, 00, 07, 47, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 @@ -160,7 +160,7 @@ Number of file 0 mappings: 14 - Code(Expression(17, Add)) at (prev + 1, 1) to (start + 0, 2) = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) -Function name: branch_if::branch_or +Function name: if::branch_or Raw bytes (56): 0x[01, 01, 04, 05, 09, 09, 0d, 0f, 11, 09, 0d, 08, 01, 35, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 20, 0d, 11, 00, 0d, 00, 0e, 0f, 00, 0f, 02, 06, 11, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 diff --git a/tests/coverage/branch_if.coverage b/tests/coverage/branch/if.coverage similarity index 100% rename from tests/coverage/branch_if.coverage rename to tests/coverage/branch/if.coverage diff --git a/tests/coverage/branch_if.rs b/tests/coverage/branch/if.rs similarity index 100% rename from tests/coverage/branch_if.rs rename to tests/coverage/branch/if.rs diff --git a/tests/coverage/branch/lazy-boolean.cov-map b/tests/coverage/branch/lazy-boolean.cov-map new file mode 100644 index 0000000000000..e2d731022d7cd --- /dev/null +++ b/tests/coverage/branch/lazy-boolean.cov-map @@ -0,0 +1,177 @@ +Function name: lazy_boolean::branch_and +Raw bytes (42): 0x[01, 01, 03, 09, 0a, 05, 09, 05, 09, 06, 01, 13, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 0e, 20, 09, 0a, 00, 0d, 00, 0e, 09, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Counter(2), rhs = Expression(2, Sub) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 19, 1) to (start + 1, 16) +- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10) + = (c2 + (c1 - c2)) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c2 + false = (c1 - c2) +- Code(Counter(2)) at (prev + 0, 18) to (start + 0, 19) +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = (c2 + (c1 - c2)) + +Function name: lazy_boolean::branch_or +Raw bytes (44): 0x[01, 01, 04, 09, 0e, 05, 09, 05, 09, 05, 09, 06, 01, 1b, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 0e, 20, 09, 0e, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(2), rhs = Expression(3, Sub) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 27, 1) to (start + 1, 16) +- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10) + = (c2 + (c1 - c2)) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(2), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c2 + false = (c1 - c2) +- Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c1 - c2) +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = (c2 + (c1 - c2)) + +Function name: lazy_boolean::chain +Raw bytes (149): 0x[01, 01, 13, 11, 07, 0b, 16, 15, 1a, 09, 0d, 05, 09, 05, 09, 09, 0d, 47, 25, 4b, 21, 19, 1d, 03, 19, 03, 19, 3e, 1d, 03, 19, 3e, 1d, 03, 19, 47, 25, 4b, 21, 19, 1d, 13, 01, 24, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 12, 20, 09, 16, 00, 0d, 00, 12, 09, 00, 16, 00, 1b, 20, 0d, 1a, 00, 16, 00, 1b, 0d, 00, 1f, 00, 24, 20, 11, 15, 00, 1f, 00, 24, 11, 00, 28, 00, 2d, 03, 01, 05, 00, 11, 43, 03, 09, 00, 0a, 03, 00, 0d, 00, 12, 20, 19, 3e, 00, 0d, 00, 12, 3e, 00, 16, 00, 1b, 20, 1d, 3a, 00, 16, 00, 1b, 3a, 00, 1f, 00, 24, 20, 21, 25, 00, 1f, 00, 24, 25, 00, 28, 00, 2d, 43, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 19 +- expression 0 operands: lhs = Counter(4), rhs = Expression(1, Add) +- expression 1 operands: lhs = Expression(2, Add), rhs = Expression(5, Sub) +- expression 2 operands: lhs = Counter(5), rhs = Expression(6, Sub) +- expression 3 operands: lhs = Counter(2), rhs = Counter(3) +- expression 4 operands: lhs = Counter(1), rhs = Counter(2) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Counter(2), rhs = Counter(3) +- expression 7 operands: lhs = Expression(17, Add), rhs = Counter(9) +- expression 8 operands: lhs = Expression(18, Add), rhs = Counter(8) +- expression 9 operands: lhs = Counter(6), rhs = Counter(7) +- expression 10 operands: lhs = Expression(0, Add), rhs = Counter(6) +- expression 11 operands: lhs = Expression(0, Add), rhs = Counter(6) +- expression 12 operands: lhs = Expression(15, Sub), rhs = Counter(7) +- expression 13 operands: lhs = Expression(0, Add), rhs = Counter(6) +- expression 14 operands: lhs = Expression(15, Sub), rhs = Counter(7) +- expression 15 operands: lhs = Expression(0, Add), rhs = Counter(6) +- expression 16 operands: lhs = Expression(17, Add), rhs = Counter(9) +- expression 17 operands: lhs = Expression(18, Add), rhs = Counter(8) +- expression 18 operands: lhs = Counter(6), rhs = Counter(7) +Number of file 0 mappings: 19 +- Code(Counter(0)) at (prev + 36, 1) to (start + 1, 16) +- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10) + = (c4 + ((c5 + (c2 - c3)) + (c1 - c2))) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 18) +- Branch { true: Counter(2), false: Expression(5, Sub) } at (prev + 0, 13) to (start + 0, 18) + true = c2 + false = (c1 - c2) +- Code(Counter(2)) at (prev + 0, 22) to (start + 0, 27) +- Branch { true: Counter(3), false: Expression(6, Sub) } at (prev + 0, 22) to (start + 0, 27) + true = c3 + false = (c2 - c3) +- Code(Counter(3)) at (prev + 0, 31) to (start + 0, 36) +- Branch { true: Counter(4), false: Counter(5) } at (prev + 0, 31) to (start + 0, 36) + true = c4 + false = c5 +- Code(Counter(4)) at (prev + 0, 40) to (start + 0, 45) +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 0, 17) + = (c4 + ((c5 + (c2 - c3)) + (c1 - c2))) +- Code(Expression(16, Add)) at (prev + 3, 9) to (start + 0, 10) + = (((c6 + c7) + c8) + c9) +- Code(Expression(0, Add)) at (prev + 0, 13) to (start + 0, 18) + = (c4 + ((c5 + (c2 - c3)) + (c1 - c2))) +- Branch { true: Counter(6), false: Expression(15, Sub) } at (prev + 0, 13) to (start + 0, 18) + true = c6 + false = ((c4 + ((c5 + (c2 - c3)) + (c1 - c2))) - c6) +- Code(Expression(15, Sub)) at (prev + 0, 22) to (start + 0, 27) + = ((c4 + ((c5 + (c2 - c3)) + (c1 - c2))) - c6) +- Branch { true: Counter(7), false: Expression(14, Sub) } at (prev + 0, 22) to (start + 0, 27) + true = c7 + false = (((c4 + ((c5 + (c2 - c3)) + (c1 - c2))) - c6) - c7) +- Code(Expression(14, Sub)) at (prev + 0, 31) to (start + 0, 36) + = (((c4 + ((c5 + (c2 - c3)) + (c1 - c2))) - c6) - c7) +- Branch { true: Counter(8), false: Counter(9) } at (prev + 0, 31) to (start + 0, 36) + true = c8 + false = c9 +- Code(Counter(9)) at (prev + 0, 40) to (start + 0, 45) +- Code(Expression(16, Add)) at (prev + 1, 5) to (start + 1, 2) + = (((c6 + c7) + c8) + c9) + +Function name: lazy_boolean::nested_mixed +Raw bytes (159): 0x[01, 01, 18, 07, 22, 11, 36, 3b, 11, 09, 0d, 26, 0d, 05, 09, 05, 09, 05, 09, 26, 0d, 05, 09, 09, 0d, 3b, 11, 09, 0d, 3b, 11, 09, 0d, 19, 5f, 1d, 21, 03, 15, 15, 19, 52, 56, 15, 19, 03, 15, 19, 5f, 1d, 21, 13, 01, 31, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0e, 00, 13, 20, 09, 26, 00, 0e, 00, 13, 26, 00, 17, 00, 1d, 20, 0d, 22, 00, 17, 00, 1d, 3b, 00, 23, 00, 28, 20, 11, 36, 00, 23, 00, 28, 36, 00, 2c, 00, 33, 03, 01, 05, 00, 11, 5b, 03, 09, 00, 0a, 03, 00, 0e, 00, 13, 20, 15, 56, 00, 0e, 00, 13, 15, 00, 17, 00, 1c, 20, 19, 52, 00, 17, 00, 1c, 4f, 00, 22, 00, 28, 20, 1d, 21, 00, 22, 00, 28, 1d, 00, 2c, 00, 33, 5b, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 24 +- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(8, Sub) +- expression 1 operands: lhs = Counter(4), rhs = Expression(13, Sub) +- expression 2 operands: lhs = Expression(14, Add), rhs = Counter(4) +- expression 3 operands: lhs = Counter(2), rhs = Counter(3) +- expression 4 operands: lhs = Expression(9, Sub), rhs = Counter(3) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Counter(1), rhs = Counter(2) +- expression 7 operands: lhs = Counter(1), rhs = Counter(2) +- expression 8 operands: lhs = Expression(9, Sub), rhs = Counter(3) +- expression 9 operands: lhs = Counter(1), rhs = Counter(2) +- expression 10 operands: lhs = Counter(2), rhs = Counter(3) +- expression 11 operands: lhs = Expression(14, Add), rhs = Counter(4) +- expression 12 operands: lhs = Counter(2), rhs = Counter(3) +- expression 13 operands: lhs = Expression(14, Add), rhs = Counter(4) +- expression 14 operands: lhs = Counter(2), rhs = Counter(3) +- expression 15 operands: lhs = Counter(6), rhs = Expression(23, Add) +- expression 16 operands: lhs = Counter(7), rhs = Counter(8) +- expression 17 operands: lhs = Expression(0, Add), rhs = Counter(5) +- expression 18 operands: lhs = Counter(5), rhs = Counter(6) +- expression 19 operands: lhs = Expression(20, Sub), rhs = Expression(21, Sub) +- expression 20 operands: lhs = Counter(5), rhs = Counter(6) +- expression 21 operands: lhs = Expression(0, Add), rhs = Counter(5) +- expression 22 operands: lhs = Counter(6), rhs = Expression(23, Add) +- expression 23 operands: lhs = Counter(7), rhs = Counter(8) +Number of file 0 mappings: 19 +- Code(Counter(0)) at (prev + 49, 1) to (start + 1, 16) +- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10) + = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) +- Code(Counter(1)) at (prev + 0, 14) to (start + 0, 19) +- Branch { true: Counter(2), false: Expression(9, Sub) } at (prev + 0, 14) to (start + 0, 19) + true = c2 + false = (c1 - c2) +- Code(Expression(9, Sub)) at (prev + 0, 23) to (start + 0, 29) + = (c1 - c2) +- Branch { true: Counter(3), false: Expression(8, Sub) } at (prev + 0, 23) to (start + 0, 29) + true = c3 + false = ((c1 - c2) - c3) +- Code(Expression(14, Add)) at (prev + 0, 35) to (start + 0, 40) + = (c2 + c3) +- Branch { true: Counter(4), false: Expression(13, Sub) } at (prev + 0, 35) to (start + 0, 40) + true = c4 + false = ((c2 + c3) - c4) +- Code(Expression(13, Sub)) at (prev + 0, 44) to (start + 0, 51) + = ((c2 + c3) - c4) +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 0, 17) + = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) +- Code(Expression(22, Add)) at (prev + 3, 9) to (start + 0, 10) + = (c6 + (c7 + c8)) +- Code(Expression(0, Add)) at (prev + 0, 14) to (start + 0, 19) + = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) +- Branch { true: Counter(5), false: Expression(21, Sub) } at (prev + 0, 14) to (start + 0, 19) + true = c5 + false = (((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) - c5) +- Code(Counter(5)) at (prev + 0, 23) to (start + 0, 28) +- Branch { true: Counter(6), false: Expression(20, Sub) } at (prev + 0, 23) to (start + 0, 28) + true = c6 + false = (c5 - c6) +- Code(Expression(19, Add)) at (prev + 0, 34) to (start + 0, 40) + = ((c5 - c6) + (((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) - c5)) +- Branch { true: Counter(7), false: Counter(8) } at (prev + 0, 34) to (start + 0, 40) + true = c7 + false = c8 +- Code(Counter(7)) at (prev + 0, 44) to (start + 0, 51) +- Code(Expression(22, Add)) at (prev + 1, 5) to (start + 1, 2) + = (c6 + (c7 + c8)) + diff --git a/tests/coverage/branch/lazy-boolean.coverage b/tests/coverage/branch/lazy-boolean.coverage new file mode 100644 index 0000000000000..f6aba1da46e49 --- /dev/null +++ b/tests/coverage/branch/lazy-boolean.coverage @@ -0,0 +1,113 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |// Tests for branch coverage of the lazy boolean operators `&&` and `||`, + LL| |// as ordinary expressions that aren't part of an `if` condition or similar. + LL| | + LL| |use core::hint::black_box; + LL| | + LL| |// Helper macro to prevent start-of-function spans from being merged into + LL| |// spans on the lines we care about. + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 15|fn branch_and(a: bool, b: bool) { + LL| 15| no_merge!(); + LL| | + LL| | // |13 |18 (no branch) + LL| 15| let c = a && b; + ^12 + ------------------ + | Branch (LL:13): [True: 12, False: 3] + ------------------ + LL| 15| black_box(c); + LL| 15|} + LL| | + LL| 15|fn branch_or(a: bool, b: bool) { + LL| 15| no_merge!(); + LL| | + LL| | // |13 |18 (no branch) + LL| 15| let c = a || b; + ^3 + ------------------ + | Branch (LL:13): [True: 12, False: 3] + ------------------ + LL| 15| black_box(c); + LL| 15|} + LL| | + LL| |// Test for chaining one operator several times. + LL| 16|fn chain(x: u32) { + LL| 16| no_merge!(); + LL| | + LL| | // |13 |22 |31 |40 (no branch) + LL| 16| let c = x > 1 && x > 2 && x > 4 && x > 8; + ^14 ^13 ^11 + ------------------ + | Branch (LL:13): [True: 14, False: 2] + | Branch (LL:22): [True: 13, False: 1] + | Branch (LL:31): [True: 11, False: 2] + ------------------ + LL| 16| black_box(c); + LL| | + LL| | // |13 |22 |31 |40 (no branch) + LL| 16| let d = x < 1 || x < 2 || x < 4 || x < 8; + ^15 ^14 ^12 + ------------------ + | Branch (LL:13): [True: 1, False: 15] + | Branch (LL:22): [True: 1, False: 14] + | Branch (LL:31): [True: 2, False: 12] + ------------------ + LL| 16| black_box(d); + LL| 16|} + LL| | + LL| |// Test for nested combinations of different operators. + LL| 16|fn nested_mixed(x: u32) { + LL| 16| no_merge!(); + LL| | + LL| | // |14 |23 |35 |44 (no branch) + LL| 16| let c = (x < 4 || x >= 9) && (x < 2 || x >= 10); + ^12 ^11 ^9 + ------------------ + | Branch (LL:14): [True: 4, False: 12] + | Branch (LL:23): [True: 7, False: 5] + | Branch (LL:35): [True: 2, False: 9] + ------------------ + LL| 16| black_box(c); + LL| | + LL| | // |14 |23 |34 |44 (no branch) + LL| 16| let d = (x < 4 && x < 1) || (x >= 8 && x >= 10); + ^4 ^15 ^8 + ------------------ + | Branch (LL:14): [True: 4, False: 12] + | Branch (LL:23): [True: 1, False: 3] + | Branch (LL:34): [True: 8, False: 7] + ------------------ + LL| 16| black_box(d); + LL| 16|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | // Use each set of arguments (2^n) times, so that each combination has a + LL| | // unique sum, and we can use those sums to verify expected control flow. + LL| | // 1x (false, false) + LL| | // 2x (false, true) + LL| | // 4x (true, false) + LL| | // 8x (true, true) + LL| | for a in [false, true, true, true, true] { + LL| | for b in [false, true, true] { + LL| | branch_and(a, b); + LL| | branch_or(a, b); + LL| | } + LL| | } + LL| | + LL| | for x in 0..16 { + LL| | chain(x); + LL| | nested_mixed(x); + LL| | } + LL| |} + diff --git a/tests/coverage/branch/lazy-boolean.rs b/tests/coverage/branch/lazy-boolean.rs new file mode 100644 index 0000000000000..3c73fc1a87d71 --- /dev/null +++ b/tests/coverage/branch/lazy-boolean.rs @@ -0,0 +1,80 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +// Tests for branch coverage of the lazy boolean operators `&&` and `||`, +// as ordinary expressions that aren't part of an `if` condition or similar. + +use core::hint::black_box; + +// Helper macro to prevent start-of-function spans from being merged into +// spans on the lines we care about. +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn branch_and(a: bool, b: bool) { + no_merge!(); + + // |13 |18 (no branch) + let c = a && b; + black_box(c); +} + +fn branch_or(a: bool, b: bool) { + no_merge!(); + + // |13 |18 (no branch) + let c = a || b; + black_box(c); +} + +// Test for chaining one operator several times. +fn chain(x: u32) { + no_merge!(); + + // |13 |22 |31 |40 (no branch) + let c = x > 1 && x > 2 && x > 4 && x > 8; + black_box(c); + + // |13 |22 |31 |40 (no branch) + let d = x < 1 || x < 2 || x < 4 || x < 8; + black_box(d); +} + +// Test for nested combinations of different operators. +fn nested_mixed(x: u32) { + no_merge!(); + + // |14 |23 |35 |44 (no branch) + let c = (x < 4 || x >= 9) && (x < 2 || x >= 10); + black_box(c); + + // |14 |23 |34 |44 (no branch) + let d = (x < 4 && x < 1) || (x >= 8 && x >= 10); + black_box(d); +} + +#[coverage(off)] +fn main() { + // Use each set of arguments (2^n) times, so that each combination has a + // unique sum, and we can use those sums to verify expected control flow. + // 1x (false, false) + // 2x (false, true) + // 4x (true, false) + // 8x (true, true) + for a in [false, true, true, true, true] { + for b in [false, true, true] { + branch_and(a, b); + branch_or(a, b); + } + } + + for x in 0..16 { + chain(x); + nested_mixed(x); + } +} diff --git a/tests/coverage/branch_while.cov-map b/tests/coverage/branch/while.cov-map similarity index 96% rename from tests/coverage/branch_while.cov-map rename to tests/coverage/branch/while.cov-map index d5f54f1abea20..5a3ef096bedd9 100644 --- a/tests/coverage/branch_while.cov-map +++ b/tests/coverage/branch/while.cov-map @@ -1,4 +1,4 @@ -Function name: branch_while::while_cond +Function name: while::while_cond Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 0c, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 10, 20, 09, 0a, 00, 0b, 00, 10, 09, 00, 11, 02, 06, 0a, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 @@ -18,7 +18,7 @@ Number of file 0 mappings: 6 - Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2) = ((c1 + c2) - c2) -Function name: branch_while::while_cond_not +Function name: while::while_cond_not Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 15, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 14, 20, 09, 0a, 00, 0b, 00, 14, 09, 00, 15, 02, 06, 0a, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 @@ -38,7 +38,7 @@ Number of file 0 mappings: 6 - Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2) = ((c1 + c2) - c2) -Function name: branch_while::while_op_and +Function name: while::while_op_and Raw bytes (56): 0x[01, 01, 04, 05, 09, 03, 0d, 03, 0d, 11, 0d, 08, 01, 1e, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 0a, 0d, 00, 0b, 00, 10, 0a, 00, 14, 00, 19, 20, 09, 11, 00, 14, 00, 19, 09, 00, 1a, 03, 06, 0f, 04, 01, 00, 02] Number of files: 1 - file 0 => global file 1 @@ -64,7 +64,7 @@ Number of file 0 mappings: 8 - Code(Expression(3, Add)) at (prev + 4, 1) to (start + 0, 2) = (c4 + c3) -Function name: branch_while::while_op_or +Function name: while::while_op_or Raw bytes (66): 0x[01, 01, 09, 05, 1b, 09, 0d, 03, 09, 03, 09, 22, 0d, 03, 09, 09, 0d, 22, 0d, 03, 09, 08, 01, 29, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 09, 22, 00, 0b, 00, 10, 22, 00, 14, 00, 19, 20, 0d, 1e, 00, 14, 00, 19, 1b, 00, 1a, 03, 06, 1e, 04, 01, 00, 02] Number of files: 1 - file 0 => global file 1 diff --git a/tests/coverage/branch_while.coverage b/tests/coverage/branch/while.coverage similarity index 100% rename from tests/coverage/branch_while.coverage rename to tests/coverage/branch/while.coverage diff --git a/tests/coverage/branch_while.rs b/tests/coverage/branch/while.rs similarity index 100% rename from tests/coverage/branch_while.rs rename to tests/coverage/branch/while.rs diff --git a/tests/mir-opt/README.md b/tests/mir-opt/README.md index 39a7b5aea123d..e4b252f5565b0 100644 --- a/tests/mir-opt/README.md +++ b/tests/mir-opt/README.md @@ -14,17 +14,18 @@ presence of pointers in constants or other bit width dependent things. In that c to your test, causing separate files to be generated for 32bit and 64bit systems. -## Unit testing +## Testing a particular MIR pass If you are only testing the behavior of a particular mir-opt pass on some specific input (as is usually the case), you should add ``` -// unit-test: PassName +//@ test-mir-pass: PassName ``` to the top of the file. This makes sure that other passes don't run which means you'll get the input -you expected and your test won't break when other code changes. +you expected and your test won't break when other code changes. This also lets you test passes +that are disabled by default. ## Emit a diff of the mir for a specific optimization diff --git a/tests/mir-opt/array_index_is_temporary.rs b/tests/mir-opt/array_index_is_temporary.rs index 500b8b7f7c74e..cd44c31d00048 100644 --- a/tests/mir-opt/array_index_is_temporary.rs +++ b/tests/mir-opt/array_index_is_temporary.rs @@ -1,4 +1,4 @@ -//@ unit-test: SimplifyCfg-pre-optimizations +//@ test-mir-pass: SimplifyCfg-pre-optimizations // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Retagging (from Stacked Borrows) relies on the array index being a fresh // temporary, so that side-effects cannot change it. diff --git a/tests/mir-opt/basic_assignment.rs b/tests/mir-opt/basic_assignment.rs index 0a23cddf620b4..a9342c13e1556 100644 --- a/tests/mir-opt/basic_assignment.rs +++ b/tests/mir-opt/basic_assignment.rs @@ -1,4 +1,4 @@ -//@ unit-test: ElaborateDrops +//@ test-mir-pass: ElaborateDrops //@ needs-unwind // this tests move up progration, which is not yet implemented diff --git a/tests/mir-opt/box_expr.rs b/tests/mir-opt/box_expr.rs index 8c016629770bc..a2d3ab94db65e 100644 --- a/tests/mir-opt/box_expr.rs +++ b/tests/mir-opt/box_expr.rs @@ -1,4 +1,4 @@ -//@ unit-test: ElaborateDrops +//@ test-mir-pass: ElaborateDrops //@ needs-unwind #![feature(rustc_attrs, stmt_expr_attributes)] diff --git a/tests/mir-opt/const_allocation.rs b/tests/mir-opt/const_allocation.rs index 038caaa0ae730..e65b2ed9a879c 100644 --- a/tests/mir-opt/const_allocation.rs +++ b/tests/mir-opt/const_allocation.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH static FOO: &[(Option, &[&str])] = diff --git a/tests/mir-opt/const_allocation2.rs b/tests/mir-opt/const_allocation2.rs index ba987c1c26b95..0681d4356dd8e 100644 --- a/tests/mir-opt/const_allocation2.rs +++ b/tests/mir-opt/const_allocation2.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR const_allocation2.main.GVN.after.mir diff --git a/tests/mir-opt/const_allocation3.rs b/tests/mir-opt/const_allocation3.rs index 86ffdef24abab..46be3e1e36e87 100644 --- a/tests/mir-opt/const_allocation3.rs +++ b/tests/mir-opt/const_allocation3.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR const_allocation3.main.GVN.after.mir diff --git a/tests/mir-opt/const_debuginfo.rs b/tests/mir-opt/const_debuginfo.rs index 319bcf484111e..b880d7e07a6e4 100644 --- a/tests/mir-opt/const_debuginfo.rs +++ b/tests/mir-opt/const_debuginfo.rs @@ -1,4 +1,4 @@ -//@ unit-test: ConstDebugInfo +//@ test-mir-pass: ConstDebugInfo //@ compile-flags: -C overflow-checks=no -Zmir-enable-passes=+GVN struct Point { diff --git a/tests/mir-opt/const_prop/address_of_pair.rs b/tests/mir-opt/const_prop/address_of_pair.rs index c6bd576699035..6d0c0f8ad52a7 100644 --- a/tests/mir-opt/const_prop/address_of_pair.rs +++ b/tests/mir-opt/const_prop/address_of_pair.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR address_of_pair.fn0.GVN.diff pub fn fn0() -> bool { diff --git a/tests/mir-opt/const_prop/aggregate.rs b/tests/mir-opt/const_prop/aggregate.rs index 2db477077728c..8f8f92bbba19b 100644 --- a/tests/mir-opt/const_prop/aggregate.rs +++ b/tests/mir-opt/const_prop/aggregate.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O // EMIT_MIR aggregate.main.GVN.diff diff --git a/tests/mir-opt/const_prop/array_index.rs b/tests/mir-opt/const_prop/array_index.rs index 2a9ca5f95e65d..aff72268223b2 100644 --- a/tests/mir-opt/const_prop/array_index.rs +++ b/tests/mir-opt/const_prop/array_index.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/bad_op_div_by_zero.rs b/tests/mir-opt/const_prop/bad_op_div_by_zero.rs index c411d3b59ab26..6c576718ac878 100644 --- a/tests/mir-opt/const_prop/bad_op_div_by_zero.rs +++ b/tests/mir-opt/const_prop/bad_op_div_by_zero.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR bad_op_div_by_zero.main.GVN.diff diff --git a/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs b/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs index aa09c1639b35a..b7623dc23da1b 100644 --- a/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs +++ b/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR bad_op_mod_by_zero.main.GVN.diff diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs index 25d513e213273..0f8d278535dc7 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/boolean_identities.rs b/tests/mir-opt/const_prop/boolean_identities.rs index cbc106aa41cac..f23749312f232 100644 --- a/tests/mir-opt/const_prop/boolean_identities.rs +++ b/tests/mir-opt/const_prop/boolean_identities.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR boolean_identities.test.GVN.diff pub fn test(x: bool, y: bool) -> bool { diff --git a/tests/mir-opt/const_prop/boxes.rs b/tests/mir-opt/const_prop/boxes.rs index 859491cf3614f..7813352261e53 100644 --- a/tests/mir-opt/const_prop/boxes.rs +++ b/tests/mir-opt/const_prop/boxes.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/const_prop/cast.rs b/tests/mir-opt/const_prop/cast.rs index ad95515b41b59..dce2e38fa9173 100644 --- a/tests/mir-opt/const_prop/cast.rs +++ b/tests/mir-opt/const_prop/cast.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR cast.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/checked_add.rs b/tests/mir-opt/const_prop/checked_add.rs index 6f43e6abdc1eb..0560b04957311 100644 --- a/tests/mir-opt/const_prop/checked_add.rs +++ b/tests/mir-opt/const_prop/checked_add.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -C overflow-checks=on // EMIT_MIR checked_add.main.GVN.diff diff --git a/tests/mir-opt/const_prop/control_flow_simplification.rs b/tests/mir-opt/const_prop/control_flow_simplification.rs index eb336827dc296..39b5f28983064 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.rs +++ b/tests/mir-opt/const_prop/control_flow_simplification.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-opt-level=1 trait NeedsDrop: Sized { diff --git a/tests/mir-opt/const_prop/discriminant.rs b/tests/mir-opt/const_prop/discriminant.rs index 51542afa4bc3d..aa169eb4e3699 100644 --- a/tests/mir-opt/const_prop/discriminant.rs +++ b/tests/mir-opt/const_prop/discriminant.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // FIXME(wesleywiser): Ideally, we could const-prop away all of this and just be left with // `let x = 42` but that doesn't work because const-prop doesn't support `Operand::Indirect` diff --git a/tests/mir-opt/const_prop/indirect.rs b/tests/mir-opt/const_prop/indirect.rs index 5c469c5d844a5..ca53e2b2f2b00 100644 --- a/tests/mir-opt/const_prop/indirect.rs +++ b/tests/mir-opt/const_prop/indirect.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -C overflow-checks=on // EMIT_MIR indirect.main.GVN.diff diff --git a/tests/mir-opt/const_prop/indirect_mutation.rs b/tests/mir-opt/const_prop/indirect_mutation.rs index b2a9d5db367a6..32ff8f142b1e7 100644 --- a/tests/mir-opt/const_prop/indirect_mutation.rs +++ b/tests/mir-opt/const_prop/indirect_mutation.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // Check that we do not propagate past an indirect mutation. #![feature(raw_ref_op)] diff --git a/tests/mir-opt/const_prop/inherit_overflow.rs b/tests/mir-opt/const_prop/inherit_overflow.rs index e71a05ce760ba..c37f3fdb86475 100644 --- a/tests/mir-opt/const_prop/inherit_overflow.rs +++ b/tests/mir-opt/const_prop/inherit_overflow.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+Inline // After inlining, this will contain a `CheckedBinaryOp`. diff --git a/tests/mir-opt/const_prop/invalid_constant.rs b/tests/mir-opt/const_prop/invalid_constant.rs index 1df82f2ee79de..afd8746af5fb4 100644 --- a/tests/mir-opt/const_prop/invalid_constant.rs +++ b/tests/mir-opt/const_prop/invalid_constant.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+RemoveZsts // Verify that we can pretty print invalid constants. diff --git a/tests/mir-opt/const_prop/issue_66971.rs b/tests/mir-opt/const_prop/issue_66971.rs index 30a9d62d499e4..03f34969bf3e8 100644 --- a/tests/mir-opt/const_prop/issue_66971.rs +++ b/tests/mir-opt/const_prop/issue_66971.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN // Due to a bug in propagating scalar pairs the assertion below used to fail. In the expected // outputs below, after GVN this is how _2 would look like with the bug: diff --git a/tests/mir-opt/const_prop/issue_67019.rs b/tests/mir-opt/const_prop/issue_67019.rs index bf788b924ce19..e13c74429164b 100644 --- a/tests/mir-opt/const_prop/issue_67019.rs +++ b/tests/mir-opt/const_prop/issue_67019.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN // This used to ICE in const-prop diff --git a/tests/mir-opt/const_prop/large_array_index.rs b/tests/mir-opt/const_prop/large_array_index.rs index 1cefc85676ff6..afba73f6a17f7 100644 --- a/tests/mir-opt/const_prop/large_array_index.rs +++ b/tests/mir-opt/const_prop/large_array_index.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/mult_by_zero.rs b/tests/mir-opt/const_prop/mult_by_zero.rs index d79f3e851615b..7943c74c9ee7a 100644 --- a/tests/mir-opt/const_prop/mult_by_zero.rs +++ b/tests/mir-opt/const_prop/mult_by_zero.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mult_by_zero.test.GVN.diff fn test(x: i32) -> i32 { diff --git a/tests/mir-opt/const_prop/mutable_variable.rs b/tests/mir-opt/const_prop/mutable_variable.rs index 4445bd224802e..9698fba6a11ac 100644 --- a/tests/mir-opt/const_prop/mutable_variable.rs +++ b/tests/mir-opt/const_prop/mutable_variable.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate.rs index c2b2731b2a6c9..7de647ed9c37f 100644 --- a/tests/mir-opt/const_prop/mutable_variable_aggregate.rs +++ b/tests/mir-opt/const_prop/mutable_variable_aggregate.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable_aggregate.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs index c9f09f878fe5c..5656c0e7a6863 100644 --- a/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs +++ b/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable_aggregate_mut_ref.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs index 5b7804b11647e..6f99e6be246fa 100644 --- a/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs +++ b/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable_aggregate_partial_read.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/mutable_variable_no_prop.rs b/tests/mir-opt/const_prop/mutable_variable_no_prop.rs index 9ea2e78d8b20e..8289832f81ea3 100644 --- a/tests/mir-opt/const_prop/mutable_variable_no_prop.rs +++ b/tests/mir-opt/const_prop/mutable_variable_no_prop.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // Verify that we do not propagate the contents of this mutable static. static mut STATIC: u32 = 0x42424242; diff --git a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs index 39ac1fa3c946a..cc92949128f72 100644 --- a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs +++ b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable_unprop_assign.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/offset_of.rs b/tests/mir-opt/const_prop/offset_of.rs index 7d258f2e36280..105cbfb53dd0b 100644 --- a/tests/mir-opt/const_prop/offset_of.rs +++ b/tests/mir-opt/const_prop/offset_of.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![feature(offset_of_enum, offset_of_nested)] diff --git a/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs b/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs index 535870fdf88ea..a43558223fe1a 100644 --- a/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs +++ b/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O // Regression test for https://github.com/rust-lang/rust/issues/118328 diff --git a/tests/mir-opt/const_prop/pointer_expose_provenance.rs b/tests/mir-opt/const_prop/pointer_expose_provenance.rs index 840a4d65f3d79..f148a5b6542e0 100644 --- a/tests/mir-opt/const_prop/pointer_expose_provenance.rs +++ b/tests/mir-opt/const_prop/pointer_expose_provenance.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN #[inline(never)] fn read(_: usize) { } diff --git a/tests/mir-opt/const_prop/read_immutable_static.rs b/tests/mir-opt/const_prop/read_immutable_static.rs index ec2dbf6485a86..05fec2f3303b0 100644 --- a/tests/mir-opt/const_prop/read_immutable_static.rs +++ b/tests/mir-opt/const_prop/read_immutable_static.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN static FOO: u8 = 2; diff --git a/tests/mir-opt/const_prop/ref_deref.rs b/tests/mir-opt/const_prop/ref_deref.rs index 20c1fba52099d..aef36323cc033 100644 --- a/tests/mir-opt/const_prop/ref_deref.rs +++ b/tests/mir-opt/const_prop/ref_deref.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR ref_deref.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/ref_deref_project.rs b/tests/mir-opt/const_prop/ref_deref_project.rs index 59e7f1a50b80d..5a48a887f93d7 100644 --- a/tests/mir-opt/const_prop/ref_deref_project.rs +++ b/tests/mir-opt/const_prop/ref_deref_project.rs @@ -1,5 +1,5 @@ // This does not currently propagate (#67862) -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR ref_deref_project.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.rs b/tests/mir-opt/const_prop/reify_fn_ptr.rs index 55dca24f3d2e9..ffce4e97f5de1 100644 --- a/tests/mir-opt/const_prop/reify_fn_ptr.rs +++ b/tests/mir-opt/const_prop/reify_fn_ptr.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR reify_fn_ptr.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/repeat.rs b/tests/mir-opt/const_prop/repeat.rs index d881462b877e7..d778191389518 100644 --- a/tests/mir-opt/const_prop/repeat.rs +++ b/tests/mir-opt/const_prop/repeat.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/return_place.rs b/tests/mir-opt/const_prop/return_place.rs index fea28c93dc3dc..e7eea11ae492f 100644 --- a/tests/mir-opt/const_prop/return_place.rs +++ b/tests/mir-opt/const_prop/return_place.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ compile-flags: -C overflow-checks=on diff --git a/tests/mir-opt/const_prop/scalar_literal_propagation.rs b/tests/mir-opt/const_prop/scalar_literal_propagation.rs index e077746835068..9d02f24e76bd6 100644 --- a/tests/mir-opt/const_prop/scalar_literal_propagation.rs +++ b/tests/mir-opt/const_prop/scalar_literal_propagation.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR scalar_literal_propagation.main.GVN.diff diff --git a/tests/mir-opt/const_prop/slice_len.rs b/tests/mir-opt/const_prop/slice_len.rs index 4a48f92ec2b43..63cdbf01b3e82 100644 --- a/tests/mir-opt/const_prop/slice_len.rs +++ b/tests/mir-opt/const_prop/slice_len.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+InstSimplify // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/switch_int.rs b/tests/mir-opt/const_prop/switch_int.rs index a176bf1443898..114380e316ded 100644 --- a/tests/mir-opt/const_prop/switch_int.rs +++ b/tests/mir-opt/const_prop/switch_int.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+SimplifyConstCondition-after-const-prop // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs index 5f2d76711599b..9cbf8928753b7 100644 --- a/tests/mir-opt/const_prop/transmute.rs +++ b/tests/mir-opt/const_prop/transmute.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O --crate-type=lib //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.rs b/tests/mir-opt/const_prop/tuple_literal_propagation.rs index 5992bb151d3e1..582411c7b59e1 100644 --- a/tests/mir-opt/const_prop/tuple_literal_propagation.rs +++ b/tests/mir-opt/const_prop/tuple_literal_propagation.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR tuple_literal_propagation.main.GVN.diff diff --git a/tests/mir-opt/const_prop/while_let_loops.rs b/tests/mir-opt/const_prop/while_let_loops.rs index 6a421da0807da..cb2c8362e1485 100644 --- a/tests/mir-opt/const_prop/while_let_loops.rs +++ b/tests/mir-opt/const_prop/while_let_loops.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR while_let_loops.change_loop_body.GVN.diff pub fn change_loop_body() { diff --git a/tests/mir-opt/copy-prop/borrowed_local.rs b/tests/mir-opt/copy-prop/borrowed_local.rs index 24b8e45532cfd..74ac6281a8953 100644 --- a/tests/mir-opt/copy-prop/borrowed_local.rs +++ b/tests/mir-opt/copy-prop/borrowed_local.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] #![allow(unused_assignments)] diff --git a/tests/mir-opt/copy-prop/branch.rs b/tests/mir-opt/copy-prop/branch.rs index 0944bb3d59aa5..fc9b8dc41b166 100644 --- a/tests/mir-opt/copy-prop/branch.rs +++ b/tests/mir-opt/copy-prop/branch.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that we bail out when there are multiple assignments to the same local. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp fn val() -> i32 { 1 } diff --git a/tests/mir-opt/copy-prop/calls.rs b/tests/mir-opt/copy-prop/calls.rs index 7d123e64950cf..8937c0d2ecb22 100644 --- a/tests/mir-opt/copy-prop/calls.rs +++ b/tests/mir-opt/copy-prop/calls.rs @@ -1,6 +1,6 @@ // skip-filecheck // Check that CopyProp does propagate return values of call terminators. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp //@ needs-unwind #![feature(custom_mir, core_intrinsics)] diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.rs b/tests/mir-opt/copy-prop/copy_propagation_arg.rs index 3516d8f7f62fb..e062e1e972887 100644 --- a/tests/mir-opt/copy-prop/copy_propagation_arg.rs +++ b/tests/mir-opt/copy-prop/copy_propagation_arg.rs @@ -2,7 +2,7 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Check that CopyProp does not propagate an assignment to a function argument // (doing so can break usages of the original argument value) -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp fn dummy(x: u8) -> u8 { x } diff --git a/tests/mir-opt/copy-prop/custom_move_arg.rs b/tests/mir-opt/copy-prop/custom_move_arg.rs index 3577ed2a4a188..a82d4618e68c5 100644 --- a/tests/mir-opt/copy-prop/custom_move_arg.rs +++ b/tests/mir-opt/copy-prop/custom_move_arg.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] #![allow(unused_assignments)] diff --git a/tests/mir-opt/copy-prop/cycle.rs b/tests/mir-opt/copy-prop/cycle.rs index ed97e86f83ac0..1c0c9eae7fead 100644 --- a/tests/mir-opt/copy-prop/cycle.rs +++ b/tests/mir-opt/copy-prop/cycle.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp fn val() -> i32 { 1 } diff --git a/tests/mir-opt/copy-prop/dead_stores_79191.rs b/tests/mir-opt/copy-prop/dead_stores_79191.rs index f6e0eac6c2c9a..24420e19fa80d 100644 --- a/tests/mir-opt/copy-prop/dead_stores_79191.rs +++ b/tests/mir-opt/copy-prop/dead_stores_79191.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp fn id(x: T) -> T { x diff --git a/tests/mir-opt/copy-prop/dead_stores_better.rs b/tests/mir-opt/copy-prop/dead_stores_better.rs index fdf42876909a9..4b18742940160 100644 --- a/tests/mir-opt/copy-prop/dead_stores_better.rs +++ b/tests/mir-opt/copy-prop/dead_stores_better.rs @@ -3,7 +3,7 @@ // This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates // that that pass enables this one to do more optimizations. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp //@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination fn id(x: T) -> T { diff --git a/tests/mir-opt/copy-prop/issue_107511.rs b/tests/mir-opt/copy-prop/issue_107511.rs index d9bd08b1bcf1c..5e8fc8df42e23 100644 --- a/tests/mir-opt/copy-prop/issue_107511.rs +++ b/tests/mir-opt/copy-prop/issue_107511.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp // EMIT_MIR issue_107511.main.CopyProp.diff fn main() { diff --git a/tests/mir-opt/copy-prop/move_arg.rs b/tests/mir-opt/copy-prop/move_arg.rs index 85ced0f6c0d53..498340534324b 100644 --- a/tests/mir-opt/copy-prop/move_arg.rs +++ b/tests/mir-opt/copy-prop/move_arg.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Test that we do not move multiple times from the same local. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp // EMIT_MIR move_arg.f.CopyProp.diff pub fn f(a: T) { diff --git a/tests/mir-opt/copy-prop/move_projection.rs b/tests/mir-opt/copy-prop/move_projection.rs index d68ffad78bcbc..231e4082e33ba 100644 --- a/tests/mir-opt/copy-prop/move_projection.rs +++ b/tests/mir-opt/copy-prop/move_projection.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] #![allow(unused_assignments)] diff --git a/tests/mir-opt/copy-prop/mutate_through_pointer.rs b/tests/mir-opt/copy-prop/mutate_through_pointer.rs index 610f5401084a1..14ca513c69275 100644 --- a/tests/mir-opt/copy-prop/mutate_through_pointer.rs +++ b/tests/mir-opt/copy-prop/mutate_through_pointer.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp // // This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB // according to Miri. However, the decision to make this UB - and to allow diff --git a/tests/mir-opt/copy-prop/non_dominate.rs b/tests/mir-opt/copy-prop/non_dominate.rs index d8b42b7f96e54..34e7eabc81a2c 100644 --- a/tests/mir-opt/copy-prop/non_dominate.rs +++ b/tests/mir-opt/copy-prop/non_dominate.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] #![allow(unused_assignments)] diff --git a/tests/mir-opt/copy-prop/partial_init.rs b/tests/mir-opt/copy-prop/partial_init.rs index 46390556418df..88e94988181d2 100644 --- a/tests/mir-opt/copy-prop/partial_init.rs +++ b/tests/mir-opt/copy-prop/partial_init.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp // Verify that we do not ICE on partial initializations. #![feature(custom_mir, core_intrinsics)] diff --git a/tests/mir-opt/copy-prop/reborrow.rs b/tests/mir-opt/copy-prop/reborrow.rs index 7d02fb328eefc..2f1720556cdee 100644 --- a/tests/mir-opt/copy-prop/reborrow.rs +++ b/tests/mir-opt/copy-prop/reborrow.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Check that CopyProp considers reborrows as not mutating the pointer. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(raw_ref_op)] diff --git a/tests/mir-opt/dataflow-const-prop/array_index.rs b/tests/mir-opt/dataflow-const-prop/array_index.rs index df8baf77add42..daf9c7729c604 100644 --- a/tests/mir-opt/dataflow-const-prop/array_index.rs +++ b/tests/mir-opt/dataflow-const-prop/array_index.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR array_index.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/boolean_identities.rs b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs index e2b7dbc096b2f..11faecec6b122 100644 --- a/tests/mir-opt/dataflow-const-prop/boolean_identities.rs +++ b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR boolean_identities.test.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/cast.rs b/tests/mir-opt/dataflow-const-prop/cast.rs index bd6141eedd165..a70cc8ee6a22d 100644 --- a/tests/mir-opt/dataflow-const-prop/cast.rs +++ b/tests/mir-opt/dataflow-const-prop/cast.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR cast.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/checked.rs b/tests/mir-opt/dataflow-const-prop/checked.rs index d3d0938168b6e..a73693464f95b 100644 --- a/tests/mir-opt/dataflow-const-prop/checked.rs +++ b/tests/mir-opt/dataflow-const-prop/checked.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -Coverflow-checks=on // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs index 617501217cf47..3a0cbac328cb5 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -Zmir-enable-passes=+GVN,+Inline // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/dataflow-const-prop/enum.rs b/tests/mir-opt/dataflow-const-prop/enum.rs index 82752750eb17f..5c52f92cd8f1f 100644 --- a/tests/mir-opt/dataflow-const-prop/enum.rs +++ b/tests/mir-opt/dataflow-const-prop/enum.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_BIT_WIDTH #![feature(custom_mir, core_intrinsics, rustc_attrs)] diff --git a/tests/mir-opt/dataflow-const-prop/if.rs b/tests/mir-opt/dataflow-const-prop/if.rs index 7df3bb9c42ebc..8cd8b2c2bec14 100644 --- a/tests/mir-opt/dataflow-const-prop/if.rs +++ b/tests/mir-opt/dataflow-const-prop/if.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR if.main.DataflowConstProp.diff // CHECK-LABEL: fn main( diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs b/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs index d0063a8e7e499..7ac59befc8ae4 100644 --- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs +++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -Zmir-enable-passes=+Inline // EMIT_MIR inherit_overflow.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/issue_81605.rs b/tests/mir-opt/dataflow-const-prop/issue_81605.rs index 9231bb22c4ce9..e7960e3fba117 100644 --- a/tests/mir-opt/dataflow-const-prop/issue_81605.rs +++ b/tests/mir-opt/dataflow-const-prop/issue_81605.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR issue_81605.f.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.rs b/tests/mir-opt/dataflow-const-prop/large_array_index.rs index 3a4159ab105f1..e74fd88d002cc 100644 --- a/tests/mir-opt/dataflow-const-prop/large_array_index.rs +++ b/tests/mir-opt/dataflow-const-prop/large_array_index.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs index b15fba29bdf87..3cd0b715a5214 100644 --- a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs +++ b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR mult_by_zero.test.DataflowConstProp.diff // CHECK-LABEL: fn test( diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.rs b/tests/mir-opt/dataflow-const-prop/offset_of.rs index 867890dcf253a..cd4e1f6990dbb 100644 --- a/tests/mir-opt/dataflow-const-prop/offset_of.rs +++ b/tests/mir-opt/dataflow-const-prop/offset_of.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![feature(offset_of_nested)] diff --git a/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs b/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs index aa669fffd4467..399de921a590f 100644 --- a/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs +++ b/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp #[inline(never)] fn escape(x: &T) {} diff --git a/tests/mir-opt/dataflow-const-prop/repeat.rs b/tests/mir-opt/dataflow-const-prop/repeat.rs index bebedbb94642f..e32c0d0877dec 100644 --- a/tests/mir-opt/dataflow-const-prop/repeat.rs +++ b/tests/mir-opt/dataflow-const-prop/repeat.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs index ace38364ee3f6..6152724c98f66 100644 --- a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs +++ b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // The struct has scalar ABI, but is not a scalar type. // Make sure that we handle this correctly. diff --git a/tests/mir-opt/dataflow-const-prop/self_assign.rs b/tests/mir-opt/dataflow-const-prop/self_assign.rs index 4171d2991aeaf..f5897bfe37b66 100644 --- a/tests/mir-opt/dataflow-const-prop/self_assign.rs +++ b/tests/mir-opt/dataflow-const-prop/self_assign.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR self_assign.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/self_assign_add.rs b/tests/mir-opt/dataflow-const-prop/self_assign_add.rs index d958025c7077e..11fe3849a1ec9 100644 --- a/tests/mir-opt/dataflow-const-prop/self_assign_add.rs +++ b/tests/mir-opt/dataflow-const-prop/self_assign_add.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR self_assign_add.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs b/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs index ad24c5855b8b0..be7f311cdc10d 100644 --- a/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs +++ b/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs @@ -6,7 +6,7 @@ // used to modify `x.1` - if it did not, then it might incorrectly assume that it // can infer the value of `x.1` at the end of this function. -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR sibling_ptr.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.rs b/tests/mir-opt/dataflow-const-prop/slice_len.rs index 08707779e2c80..5d9733f498c91 100644 --- a/tests/mir-opt/dataflow-const-prop/slice_len.rs +++ b/tests/mir-opt/dataflow-const-prop/slice_len.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -Zmir-enable-passes=+InstSimplify // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/dataflow-const-prop/struct.rs b/tests/mir-opt/dataflow-const-prop/struct.rs index 0180e978a09fb..eed782c9036b2 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.rs +++ b/tests/mir-opt/dataflow-const-prop/struct.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_BIT_WIDTH #[derive(Copy, Clone)] diff --git a/tests/mir-opt/dataflow-const-prop/terminator.rs b/tests/mir-opt/dataflow-const-prop/terminator.rs index d33f3216933ac..aac5d11d3d482 100644 --- a/tests/mir-opt/dataflow-const-prop/terminator.rs +++ b/tests/mir-opt/dataflow-const-prop/terminator.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp fn foo(n: i32) {} diff --git a/tests/mir-opt/dataflow-const-prop/transmute.rs b/tests/mir-opt/dataflow-const-prop/transmute.rs index 7cf0dad5e483d..e7f93f421cf8d 100644 --- a/tests/mir-opt/dataflow-const-prop/transmute.rs +++ b/tests/mir-opt/dataflow-const-prop/transmute.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -O --crate-type=lib //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/dataflow-const-prop/tuple.rs b/tests/mir-opt/dataflow-const-prop/tuple.rs index 5d7c38970f6c7..d624e21f21ac4 100644 --- a/tests/mir-opt/dataflow-const-prop/tuple.rs +++ b/tests/mir-opt/dataflow-const-prop/tuple.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR tuple.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs index 2ce1e9023a764..edb35061cc652 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DeadStoreElimination-final +//@ test-mir-pass: DeadStoreElimination-final //@ compile-flags: -Zmir-enable-passes=+CopyProp #![feature(core_intrinsics)] diff --git a/tests/mir-opt/dead-store-elimination/cycle.rs b/tests/mir-opt/dead-store-elimination/cycle.rs index ddbc89a7ae2c7..795d57d36f523 100644 --- a/tests/mir-opt/dead-store-elimination/cycle.rs +++ b/tests/mir-opt/dead-store-elimination/cycle.rs @@ -2,7 +2,7 @@ // report that *all* of these stores are live. // //@ needs-unwind -//@ unit-test: DeadStoreElimination-initial +//@ test-mir-pass: DeadStoreElimination-initial #![feature(core_intrinsics, custom_mir)] use std::intrinsics::mir::*; diff --git a/tests/mir-opt/dead-store-elimination/place_mention.rs b/tests/mir-opt/dead-store-elimination/place_mention.rs index d276f6fa0259a..5e4a286a20874 100644 --- a/tests/mir-opt/dead-store-elimination/place_mention.rs +++ b/tests/mir-opt/dead-store-elimination/place_mention.rs @@ -1,7 +1,7 @@ // Verify that we account for the `PlaceMention` statement as a use of the tuple, // and don't remove it as a dead store. // -//@ unit-test: DeadStoreElimination-initial +//@ test-mir-pass: DeadStoreElimination-initial //@ compile-flags: -Zmir-keep-place-mention // EMIT_MIR place_mention.main.DeadStoreElimination-initial.diff diff --git a/tests/mir-opt/dead-store-elimination/provenance_soundness.rs b/tests/mir-opt/dead-store-elimination/provenance_soundness.rs index 20517a00489a5..b2523684a09e6 100644 --- a/tests/mir-opt/dead-store-elimination/provenance_soundness.rs +++ b/tests/mir-opt/dead-store-elimination/provenance_soundness.rs @@ -1,5 +1,5 @@ // Test that we don't remove pointer to int casts or retags -//@ unit-test: DeadStoreElimination-initial +//@ test-mir-pass: DeadStoreElimination-initial //@ compile-flags: -Zmir-emit-retag // EMIT_MIR provenance_soundness.pointer_to_int.DeadStoreElimination-initial.diff diff --git a/tests/mir-opt/deduplicate_blocks.rs b/tests/mir-opt/deduplicate_blocks.rs index 7979fdfe768c7..3a164cb09a094 100644 --- a/tests/mir-opt/deduplicate_blocks.rs +++ b/tests/mir-opt/deduplicate_blocks.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DeduplicateBlocks +//@ test-mir-pass: DeduplicateBlocks // EMIT_MIR deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff pub const fn is_line_doc_comment_2(s: &str) -> bool { diff --git a/tests/mir-opt/derefer_complex_case.rs b/tests/mir-opt/derefer_complex_case.rs index bdaf83fcb5bf8..b1fa2c8733ef7 100644 --- a/tests/mir-opt/derefer_complex_case.rs +++ b/tests/mir-opt/derefer_complex_case.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_complex_case.main.Derefer.diff // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/derefer_inline_test.rs b/tests/mir-opt/derefer_inline_test.rs index 89de514a28080..7f9272bdec852 100644 --- a/tests/mir-opt/derefer_inline_test.rs +++ b/tests/mir-opt/derefer_inline_test.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_inline_test.main.Derefer.diff // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/derefer_terminator_test.rs b/tests/mir-opt/derefer_terminator_test.rs index e225db5bbe8a4..5de6a61eaf2c6 100644 --- a/tests/mir-opt/derefer_terminator_test.rs +++ b/tests/mir-opt/derefer_terminator_test.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_terminator_test.main.Derefer.diff // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/derefer_test.rs b/tests/mir-opt/derefer_test.rs index e30a286805fee..3ca2144e4fc43 100644 --- a/tests/mir-opt/derefer_test.rs +++ b/tests/mir-opt/derefer_test.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_test.main.Derefer.diff fn main() { let mut a = (42,43); diff --git a/tests/mir-opt/derefer_test_multiple.rs b/tests/mir-opt/derefer_test_multiple.rs index 4efc735b22ebc..145a19ee6a3cd 100644 --- a/tests/mir-opt/derefer_test_multiple.rs +++ b/tests/mir-opt/derefer_test_multiple.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_test_multiple.main.Derefer.diff fn main () { let mut a = (42, 43); diff --git a/tests/mir-opt/dest-prop/branch.rs b/tests/mir-opt/dest-prop/branch.rs index cd55130728570..481d4130c7b59 100644 --- a/tests/mir-opt/dest-prop/branch.rs +++ b/tests/mir-opt/dest-prop/branch.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that assignment in both branches of an `if` are eliminated. -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation fn val() -> i32 { 1 } diff --git a/tests/mir-opt/dest-prop/copy_propagation_arg.rs b/tests/mir-opt/dest-prop/copy_propagation_arg.rs index f84b5fde8d88b..db4969924ffb9 100644 --- a/tests/mir-opt/dest-prop/copy_propagation_arg.rs +++ b/tests/mir-opt/dest-prop/copy_propagation_arg.rs @@ -2,7 +2,7 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Check that DestinationPropagation does not propagate an assignment to a function argument // (doing so can break usages of the original argument value) -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation fn dummy(x: u8) -> u8 { x } diff --git a/tests/mir-opt/dest-prop/cycle.rs b/tests/mir-opt/dest-prop/cycle.rs index e6663956d7822..e414daf20f2dd 100644 --- a/tests/mir-opt/dest-prop/cycle.rs +++ b/tests/mir-opt/dest-prop/cycle.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that cyclic assignments don't hang DestinationPropagation, and result in reasonable code. -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation fn val() -> i32 { 1 } diff --git a/tests/mir-opt/dest-prop/dead_stores_79191.rs b/tests/mir-opt/dest-prop/dead_stores_79191.rs index b3e370966d05d..5c218a328f584 100644 --- a/tests/mir-opt/dest-prop/dead_stores_79191.rs +++ b/tests/mir-opt/dest-prop/dead_stores_79191.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation fn id(x: T) -> T { x diff --git a/tests/mir-opt/dest-prop/dead_stores_better.rs b/tests/mir-opt/dest-prop/dead_stores_better.rs index c241d71594bab..06445dc87031e 100644 --- a/tests/mir-opt/dest-prop/dead_stores_better.rs +++ b/tests/mir-opt/dest-prop/dead_stores_better.rs @@ -3,7 +3,7 @@ // This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates // that that pass enables this one to do more optimizations. -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation //@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination fn id(x: T) -> T { diff --git a/tests/mir-opt/dest-prop/simple.rs b/tests/mir-opt/dest-prop/simple.rs index 4aa6b6a4876e4..8e5d6340e566f 100644 --- a/tests/mir-opt/dest-prop/simple.rs +++ b/tests/mir-opt/dest-prop/simple.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Copy of `nrvo-simple.rs`, to ensure that full dest-prop handles it too. -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation // EMIT_MIR simple.nrvo.DestinationPropagation.diff fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { let mut buf = [0; 1024]; diff --git a/tests/mir-opt/dont_inline_type_id.rs b/tests/mir-opt/dont_inline_type_id.rs index ae72eb11735e9..ab748940ac43a 100644 --- a/tests/mir-opt/dont_inline_type_id.rs +++ b/tests/mir-opt/dont_inline_type_id.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ compile-flags: --crate-type=lib -C panic=abort use std::any::Any; diff --git a/tests/mir-opt/early_otherwise_branch.rs b/tests/mir-opt/early_otherwise_branch.rs index bfeb1f7bbc61b..b047c50df97fa 100644 --- a/tests/mir-opt/early_otherwise_branch.rs +++ b/tests/mir-opt/early_otherwise_branch.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching enum Option2 { diff --git a/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs b/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs index 2d215621bbd98..d2a3e1f59ff6e 100644 --- a/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs +++ b/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching enum Option2 { diff --git a/tests/mir-opt/early_otherwise_branch_68867.rs b/tests/mir-opt/early_otherwise_branch_68867.rs index 59bc19ceeccc3..789b5ebab807d 100644 --- a/tests/mir-opt/early_otherwise_branch_68867.rs +++ b/tests/mir-opt/early_otherwise_branch_68867.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // FIXME: This test was broken by the derefer change. diff --git a/tests/mir-opt/early_otherwise_branch_noopt.rs b/tests/mir-opt/early_otherwise_branch_noopt.rs index 6b48393e6b947..307c6e579c99e 100644 --- a/tests/mir-opt/early_otherwise_branch_noopt.rs +++ b/tests/mir-opt/early_otherwise_branch_noopt.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // must not optimize as it does not follow the pattern of diff --git a/tests/mir-opt/early_otherwise_branch_soundness.rs b/tests/mir-opt/early_otherwise_branch_soundness.rs index 74a2af884c0b6..a22be312a9b92 100644 --- a/tests/mir-opt/early_otherwise_branch_soundness.rs +++ b/tests/mir-opt/early_otherwise_branch_soundness.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // Tests various cases that the `early_otherwise_branch` opt should *not* optimize diff --git a/tests/mir-opt/enum_opt.rs b/tests/mir-opt/enum_opt.rs index c5b3e61a4cb25..cacc7301f1220 100644 --- a/tests/mir-opt/enum_opt.rs +++ b/tests/mir-opt/enum_opt.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: EnumSizeOpt +//@ test-mir-pass: EnumSizeOpt // EMIT_MIR_FOR_EACH_BIT_WIDTH //@ compile-flags: -Zunsound-mir-opts diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 6f4d1e35585bc..0484710f00e7e 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ only-64bit diff --git a/tests/mir-opt/gvn_copy_moves.rs b/tests/mir-opt/gvn_copy_moves.rs index 9d83a19e4a583..1812de16d69fd 100644 --- a/tests/mir-opt/gvn_copy_moves.rs +++ b/tests/mir-opt/gvn_copy_moves.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN #![feature(custom_mir, core_intrinsics)] extern crate core; diff --git a/tests/mir-opt/gvn_uninhabited.rs b/tests/mir-opt/gvn_uninhabited.rs index 5f9df7953c8e5..015949c5d20b9 100644 --- a/tests/mir-opt/gvn_uninhabited.rs +++ b/tests/mir-opt/gvn_uninhabited.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // skip-filecheck diff --git a/tests/mir-opt/if_condition_int.rs b/tests/mir-opt/if_condition_int.rs index 2f3f643304569..4cc2c2b902102 100644 --- a/tests/mir-opt/if_condition_int.rs +++ b/tests/mir-opt/if_condition_int.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: SimplifyComparisonIntegral +//@ test-mir-pass: SimplifyComparisonIntegral // EMIT_MIR if_condition_int.opt_u32.SimplifyComparisonIntegral.diff // EMIT_MIR if_condition_int.opt_negative.SimplifyComparisonIntegral.diff // EMIT_MIR if_condition_int.opt_char.SimplifyComparisonIntegral.diff diff --git a/tests/mir-opt/inline/indirect_destination.rs b/tests/mir-opt/inline/indirect_destination.rs index 337f617e70364..4246eef08f704 100644 --- a/tests/mir-opt/inline/indirect_destination.rs +++ b/tests/mir-opt/inline/indirect_destination.rs @@ -1,6 +1,6 @@ // Test for inlining with an indirect destination place. // -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ edition: 2021 //@ needs-unwind #![crate_type = "lib"] diff --git a/tests/mir-opt/inline/inline_box_fn.rs b/tests/mir-opt/inline/inline_box_fn.rs index 3e006016f8c30..bb2da3ac51576 100644 --- a/tests/mir-opt/inline/inline_box_fn.rs +++ b/tests/mir-opt/inline/inline_box_fn.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ compile-flags: --crate-type=lib // EMIT_MIR inline_box_fn.call.Inline.diff diff --git a/tests/mir-opt/inline/unit_test.rs b/tests/mir-opt/inline/unit_test.rs index f6c3d6a58de00..bebe69384612e 100644 --- a/tests/mir-opt/inline/unit_test.rs +++ b/tests/mir-opt/inline/unit_test.rs @@ -1,5 +1,5 @@ // Check that `-Zmir-enable-passes=+Inline` does not ICE because of stolen MIR. -//@ unit-test: Inline +//@ test-mir-pass: Inline // skip-filecheck #![crate_type = "lib"] diff --git a/tests/mir-opt/inline_coroutine_body.rs b/tests/mir-opt/inline_coroutine_body.rs index be73bc49de59e..4326ff8a11b39 100644 --- a/tests/mir-opt/inline_coroutine_body.rs +++ b/tests/mir-opt/inline_coroutine_body.rs @@ -1,6 +1,6 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // skip-filecheck -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ edition: 2021 //@ compile-flags: -Zinline-mir-hint-threshold=10000 -Zinline-mir-threshold=10000 --crate-type=lib diff --git a/tests/mir-opt/inline_generically_if_sized.rs b/tests/mir-opt/inline_generically_if_sized.rs index 794ce3dabbca3..e4fc94ec43626 100644 --- a/tests/mir-opt/inline_generically_if_sized.rs +++ b/tests/mir-opt/inline_generically_if_sized.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ compile-flags: --crate-type=lib -C panic=abort trait Foo { diff --git a/tests/mir-opt/instrument_coverage.rs b/tests/mir-opt/instrument_coverage.rs index ae63990f253df..beb88b607f974 100644 --- a/tests/mir-opt/instrument_coverage.rs +++ b/tests/mir-opt/instrument_coverage.rs @@ -2,7 +2,7 @@ // The Coverage::CounterIncrement statements are later converted into LLVM // instrprof.increment intrinsics, during codegen. -//@ unit-test: InstrumentCoverage +//@ test-mir-pass: InstrumentCoverage //@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff diff --git a/tests/mir-opt/instrument_coverage_cleanup.rs b/tests/mir-opt/instrument_coverage_cleanup.rs index 7db76339e551d..acc544a28af65 100644 --- a/tests/mir-opt/instrument_coverage_cleanup.rs +++ b/tests/mir-opt/instrument_coverage_cleanup.rs @@ -5,7 +5,7 @@ // Removed statement kinds: BlockMarker, SpanMarker // Retained statement kinds: CounterIncrement, ExpressionUsed -//@ unit-test: InstrumentCoverage +//@ test-mir-pass: InstrumentCoverage //@ compile-flags: -Cinstrument-coverage -Zcoverage-options=branch -Zno-profiler-runtime // EMIT_MIR instrument_coverage_cleanup.main.InstrumentCoverage.diff diff --git a/tests/mir-opt/instsimplify/bool_compare.rs b/tests/mir-opt/instsimplify/bool_compare.rs index 47984edd669f0..d1d903f9ef2cf 100644 --- a/tests/mir-opt/instsimplify/bool_compare.rs +++ b/tests/mir-opt/instsimplify/bool_compare.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify // EMIT_MIR bool_compare.eq_true.InstSimplify.diff fn eq_true(x: bool) -> u32 { diff --git a/tests/mir-opt/instsimplify/casts.rs b/tests/mir-opt/instsimplify/casts.rs index adcf325e3f5c2..b3bc34af5b707 100644 --- a/tests/mir-opt/instsimplify/casts.rs +++ b/tests/mir-opt/instsimplify/casts.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify //@ compile-flags: -Zinline-mir #![crate_type = "lib"] diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs index 4b4054a7a2d6b..86455e8b52d55 100644 --- a/tests/mir-opt/instsimplify/combine_array_len.rs +++ b/tests/mir-opt/instsimplify/combine_array_len.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify // EMIT_MIR combine_array_len.norm2.InstSimplify.diff fn norm2(x: [f32; 2]) -> f32 { diff --git a/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs b/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs index d0c85595dbcf8..7b1f3d14f4fd9 100644 --- a/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs +++ b/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR combine_clone_of_primitives.{impl#0}-clone.InstSimplify.diff diff --git a/tests/mir-opt/instsimplify/combine_transmutes.rs b/tests/mir-opt/instsimplify/combine_transmutes.rs index 3707ee17690da..a1274dd1b4086 100644 --- a/tests/mir-opt/instsimplify/combine_transmutes.rs +++ b/tests/mir-opt/instsimplify/combine_transmutes.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify //@ compile-flags: -C panic=abort #![crate_type = "lib"] #![feature(core_intrinsics)] diff --git a/tests/mir-opt/instsimplify/duplicate_switch_targets.rs b/tests/mir-opt/instsimplify/duplicate_switch_targets.rs index fd09d632a4f04..454728249b1a4 100644 --- a/tests/mir-opt/instsimplify/duplicate_switch_targets.rs +++ b/tests/mir-opt/instsimplify/duplicate_switch_targets.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify #![feature(custom_mir, core_intrinsics)] #![crate_type = "lib"] diff --git a/tests/mir-opt/instsimplify/intrinsic_asserts.rs b/tests/mir-opt/instsimplify/intrinsic_asserts.rs index c14b1ac5a218e..c031c97816261 100644 --- a/tests/mir-opt/instsimplify/intrinsic_asserts.rs +++ b/tests/mir-opt/instsimplify/intrinsic_asserts.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify #![crate_type = "lib"] #![feature(core_intrinsics)] diff --git a/tests/mir-opt/instsimplify/ub_check.rs b/tests/mir-opt/instsimplify/ub_check.rs index fc568abcd601c..5f13f5ba059b5 100644 --- a/tests/mir-opt/instsimplify/ub_check.rs +++ b/tests/mir-opt/instsimplify/ub_check.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify //@ compile-flags: -Cdebug-assertions=no -Zinline-mir // EMIT_MIR ub_check.unwrap_unchecked.InstSimplify.diff diff --git a/tests/mir-opt/issue_101973.rs b/tests/mir-opt/issue_101973.rs index c40eaa1f2a9b8..84a36f1374e8e 100644 --- a/tests/mir-opt/issue_101973.rs +++ b/tests/mir-opt/issue_101973.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ compile-flags: -O -C debug-assertions=on -// This needs inlining followed by GVN to reproduce, so we cannot use "unit-test". +// This needs inlining followed by GVN to reproduce, so we cannot use "test-mir-pass". #[inline] pub fn imm8(x: u32) -> u32 { diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index eedb26ad41a53..57f4e4a2654f7 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -1,4 +1,4 @@ -//@ unit-test: JumpThreading +//@ test-mir-pass: JumpThreading //@ compile-flags: -Zmir-enable-passes=+Inline // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/lower_array_len.rs b/tests/mir-opt/lower_array_len.rs index 7fcea75aaaf43..1c30c4c89b966 100644 --- a/tests/mir-opt/lower_array_len.rs +++ b/tests/mir-opt/lower_array_len.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: NormalizeArrayLen +//@ test-mir-pass: NormalizeArrayLen //@ compile-flags: -Zmir-enable-passes=+LowerSliceLenCalls // EMIT_MIR lower_array_len.array_bound.NormalizeArrayLen.diff diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 08366417d7cb4..693425c004152 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -1,4 +1,4 @@ -//@ unit-test: LowerIntrinsics +//@ test-mir-pass: LowerIntrinsics // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![feature(core_intrinsics, intrinsics, rustc_attrs)] diff --git a/tests/mir-opt/lower_slice_len.rs b/tests/mir-opt/lower_slice_len.rs index 38d5e984cee7e..b82094dc18f9c 100644 --- a/tests/mir-opt/lower_slice_len.rs +++ b/tests/mir-opt/lower_slice_len.rs @@ -1,4 +1,4 @@ -//@ unit-test: LowerSliceLenCalls +//@ test-mir-pass: LowerSliceLenCalls // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR lower_slice_len.bound.LowerSliceLenCalls.diff diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs index 2e7b7d4e6001a..fa466220b6537 100644 --- a/tests/mir-opt/matches_reduce_branches.rs +++ b/tests/mir-opt/matches_reduce_branches.rs @@ -1,4 +1,4 @@ -//@ unit-test: MatchBranchSimplification +//@ test-mir-pass: MatchBranchSimplification #![feature(repr128)] #![feature(core_intrinsics)] diff --git a/tests/mir-opt/matches_u8.rs b/tests/mir-opt/matches_u8.rs index e855c913226a4..f0be82d025713 100644 --- a/tests/mir-opt/matches_u8.rs +++ b/tests/mir-opt/matches_u8.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: MatchBranchSimplification +//@ test-mir-pass: MatchBranchSimplification // EMIT_MIR matches_u8.exhaustive_match.MatchBranchSimplification.diff diff --git a/tests/mir-opt/nrvo_miscompile_111005.rs b/tests/mir-opt/nrvo_miscompile_111005.rs index 3087c98d0528d..18814b0678fe4 100644 --- a/tests/mir-opt/nrvo_miscompile_111005.rs +++ b/tests/mir-opt/nrvo_miscompile_111005.rs @@ -1,7 +1,7 @@ // skip-filecheck // This is a miscompilation, #111005 to track -//@ unit-test: RenameReturnPlace +//@ test-mir-pass: RenameReturnPlace #![feature(custom_mir, core_intrinsics)] extern crate core; diff --git a/tests/mir-opt/nrvo_simple.rs b/tests/mir-opt/nrvo_simple.rs index adb787a09fb66..5d2894a704a54 100644 --- a/tests/mir-opt/nrvo_simple.rs +++ b/tests/mir-opt/nrvo_simple.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: RenameReturnPlace +//@ test-mir-pass: RenameReturnPlace // EMIT_MIR nrvo_simple.nrvo.RenameReturnPlace.diff fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { diff --git a/tests/mir-opt/reference_prop.rs b/tests/mir-opt/reference_prop.rs index 70587dff0b540..2dda771ba7d74 100644 --- a/tests/mir-opt/reference_prop.rs +++ b/tests/mir-opt/reference_prop.rs @@ -1,5 +1,5 @@ //@ compile-flags: -Zlint-mir=no -//@ unit-test: ReferencePropagation +//@ test-mir-pass: ReferencePropagation //@ needs-unwind #![feature(raw_ref_op)] diff --git a/tests/mir-opt/remove_storage_markers.rs b/tests/mir-opt/remove_storage_markers.rs index c53c387504527..4a928b7745272 100644 --- a/tests/mir-opt/remove_storage_markers.rs +++ b/tests/mir-opt/remove_storage_markers.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: RemoveStorageMarkers +//@ test-mir-pass: RemoveStorageMarkers // Checks that storage markers are removed at opt-level=0. // diff --git a/tests/mir-opt/retag.rs b/tests/mir-opt/retag.rs index 17b3c10abeb18..43d74aa5726fa 100644 --- a/tests/mir-opt/retag.rs +++ b/tests/mir-opt/retag.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: AddRetag +//@ test-mir-pass: AddRetag // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // ignore-tidy-linelength //@ compile-flags: -Z mir-emit-retag -Z mir-opt-level=0 -Z span_free_formats diff --git a/tests/mir-opt/set_no_discriminant.rs b/tests/mir-opt/set_no_discriminant.rs index 995bd11a1a927..0c29d1faf02a7 100644 --- a/tests/mir-opt/set_no_discriminant.rs +++ b/tests/mir-opt/set_no_discriminant.rs @@ -1,6 +1,6 @@ // `SetDiscriminant` does not actually write anything if the chosen variant is the untagged variant // of a niche encoding. Verify that we do not thread over this case. -//@ unit-test: JumpThreading +//@ test-mir-pass: JumpThreading #![feature(custom_mir)] #![feature(core_intrinsics)] diff --git a/tests/mir-opt/simplify_dead_blocks.rs b/tests/mir-opt/simplify_dead_blocks.rs index d4de85622d41e..686eac5823640 100644 --- a/tests/mir-opt/simplify_dead_blocks.rs +++ b/tests/mir-opt/simplify_dead_blocks.rs @@ -1,4 +1,4 @@ -//@ unit-test: SimplifyCfg-after-unreachable-enum-branching +//@ test-mir-pass: SimplifyCfg-after-unreachable-enum-branching #![feature(custom_mir, core_intrinsics)] #![crate_type = "lib"] diff --git a/tests/mir-opt/simplify_locals.rs b/tests/mir-opt/simplify_locals.rs index 756679e77e396..f57611111cfcb 100644 --- a/tests/mir-opt/simplify_locals.rs +++ b/tests/mir-opt/simplify_locals.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: SimplifyLocals-before-const-prop +//@ test-mir-pass: SimplifyLocals-before-const-prop #![feature(thread_local)] diff --git a/tests/mir-opt/simplify_locals_removes_unused_consts.rs b/tests/mir-opt/simplify_locals_removes_unused_consts.rs index 3a461647e3624..70d1555e786bc 100644 --- a/tests/mir-opt/simplify_locals_removes_unused_consts.rs +++ b/tests/mir-opt/simplify_locals_removes_unused_consts.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: SimplifyLocals-before-const-prop +//@ test-mir-pass: SimplifyLocals-before-const-prop //@ compile-flags: -C overflow-checks=no fn use_zst(_: ((), ())) {} diff --git a/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs b/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs index 52afb4f2c5276..6257f5ee795ac 100644 --- a/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs +++ b/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: SimplifyLocals-before-const-prop +//@ test-mir-pass: SimplifyLocals-before-const-prop fn map(x: Option>) -> Option> { match x { diff --git a/tests/mir-opt/sroa/lifetimes.rs b/tests/mir-opt/sroa/lifetimes.rs index 3f5c99404d86e..6c18dbaf5a23b 100644 --- a/tests/mir-opt/sroa/lifetimes.rs +++ b/tests/mir-opt/sroa/lifetimes.rs @@ -1,4 +1,4 @@ -//@ unit-test: ScalarReplacementOfAggregates +//@ test-mir-pass: ScalarReplacementOfAggregates //@ compile-flags: -Cpanic=abort //@ no-prefer-dynamic diff --git a/tests/mir-opt/sroa/structs.rs b/tests/mir-opt/sroa/structs.rs index cbe4b98953039..a177dbf71cf1c 100644 --- a/tests/mir-opt/sroa/structs.rs +++ b/tests/mir-opt/sroa/structs.rs @@ -1,4 +1,4 @@ -//@ unit-test: ScalarReplacementOfAggregates +//@ test-mir-pass: ScalarReplacementOfAggregates //@ compile-flags: -Cpanic=abort //@ no-prefer-dynamic diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs index b07b8230faf3e..5838b35a553a4 100644 --- a/tests/mir-opt/unreachable.rs +++ b/tests/mir-opt/unreachable.rs @@ -1,4 +1,4 @@ -//@ unit-test: UnreachablePropagation +//@ test-mir-pass: UnreachablePropagation // EMIT_MIR_FOR_EACH_PANIC_STRATEGY enum Empty {} diff --git a/tests/mir-opt/unreachable_diverging.rs b/tests/mir-opt/unreachable_diverging.rs index b7e0f6eff9bf7..695e3c4e8fc86 100644 --- a/tests/mir-opt/unreachable_diverging.rs +++ b/tests/mir-opt/unreachable_diverging.rs @@ -1,4 +1,4 @@ -//@ unit-test: UnreachablePropagation +//@ test-mir-pass: UnreachablePropagation // EMIT_MIR_FOR_EACH_PANIC_STRATEGY pub enum Empty {} diff --git a/tests/mir-opt/unreachable_enum_branching.rs b/tests/mir-opt/unreachable_enum_branching.rs index 156b23657b7a6..6005dc546dc8b 100644 --- a/tests/mir-opt/unreachable_enum_branching.rs +++ b/tests/mir-opt/unreachable_enum_branching.rs @@ -1,4 +1,4 @@ -//@ unit-test: UnreachableEnumBranching +//@ test-mir-pass: UnreachableEnumBranching // EMIT_MIR_FOR_EACH_PANIC_STRATEGY enum Empty {} diff --git a/tests/ui/inference/hint-closure-signature-119266.rs b/tests/ui/inference/hint-closure-signature-119266.rs new file mode 100644 index 0000000000000..35be600fd6ab8 --- /dev/null +++ b/tests/ui/inference/hint-closure-signature-119266.rs @@ -0,0 +1,11 @@ +fn main() { + let x = |a: u8, b: (usize, u32), c: fn() -> char| -> String { "I love beans.".to_string() }; + //~^ NOTE: the found closure + + let x: fn(i32) = x; + //~^ ERROR: 5:22: 5:23: mismatched types [E0308] + //~| NOTE: incorrect number of function parameters + //~| NOTE: expected due to this + //~| NOTE: expected fn pointer `fn(i32)` + //~| NOTE: closure has signature: `fn(u8, (usize, u32), fn() -> char) -> String` +} diff --git a/tests/ui/inference/hint-closure-signature-119266.stderr b/tests/ui/inference/hint-closure-signature-119266.stderr new file mode 100644 index 0000000000000..f0b957906afbc --- /dev/null +++ b/tests/ui/inference/hint-closure-signature-119266.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/hint-closure-signature-119266.rs:5:22 + | +LL | let x = |a: u8, b: (usize, u32), c: fn() -> char| -> String { "I love beans.".to_string() }; + | --------------------------------------------------- the found closure +... +LL | let x: fn(i32) = x; + | ------- ^ incorrect number of function parameters + | | + | expected due to this + | + = note: expected fn pointer `fn(i32)` + found closure `{closure@$DIR/hint-closure-signature-119266.rs:2:13: 2:64}` + = note: closure has signature: `fn(u8, (usize, u32), fn() -> char) -> String` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`.