diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 0221bc6c36383..02333f4575344 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -310,6 +310,14 @@ impl<'tcx> CoroutineInfo<'tcx> { } } +/// Some item that needs to monomorphize successfully for a MIR body to be considered well-formed. +#[derive(Copy, Clone, PartialEq, Debug, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeFoldable, TypeVisitable)] +pub enum RequiredItem<'tcx> { + Fn(DefId, GenericArgsRef<'tcx>), + Drop(Ty<'tcx>), +} + /// The lowered representation of a single function. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct Body<'tcx> { @@ -375,6 +383,9 @@ pub struct Body<'tcx> { /// We hold in this field all the constants we are not able to evaluate yet. pub required_consts: Vec>, + /// Further items that need to monomorphize successfully for this MIR to be well-formed. + pub required_items: Vec>, + /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. /// /// Note that this does not actually mean that this body is not computable right now. @@ -445,6 +456,7 @@ impl<'tcx> Body<'tcx> { var_debug_info, span, required_consts: Vec::new(), + required_items: Vec::new(), is_polymorphic: false, injection_phase: None, tainted_by_errors, @@ -473,6 +485,7 @@ impl<'tcx> Body<'tcx> { spread_arg: None, span: DUMMY_SP, required_consts: Vec::new(), + required_items: Vec::new(), var_debug_info: Vec::new(), is_polymorphic: false, injection_phase: None, diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index c2bff9084c67e..0ad6b22ee5405 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -56,6 +56,7 @@ pub(super) fn build_custom_mir<'tcx>( var_debug_info: Vec::new(), span, required_consts: Vec::new(), + required_items: Vec::new(), is_polymorphic: false, tainted_by_errors: None, injection_phase: None, diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f6a0945c22297..22911d1ac82c1 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -565,7 +565,8 @@ impl<'tcx> Inliner<'tcx> { mut callee_body: Body<'tcx>, ) { let terminator = caller_body[callsite.block].terminator.take().unwrap(); - let TerminatorKind::Call { args, destination, unwind, target, .. } = terminator.kind else { + let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind + else { bug!("unexpected terminator kind {:?}", terminator.kind); }; @@ -717,6 +718,18 @@ impl<'tcx> Inliner<'tcx> { Const::Val(..) | Const::Unevaluated(..) => true, }, )); + // Now that we incorporated the callee's `required_consts`, we can remove the callee from + // `required_items` -- but we have to take their `required_consts` in return. + let callee_item = { + // We need to reconstruct the `required_item` for the callee. + let func_ty = func.ty(caller_body, self.tcx); + match func_ty.kind() { + ty::FnDef(def_id, args) => RequiredItem::Fn(*def_id, args), + _ => bug!(), + } + }; + caller_body.required_items.retain(|item| *item != callee_item); + caller_body.required_items.extend(callee_body.required_items); } fn make_call_args( diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index cd9b98e4f32cd..b76ee39b1ba97 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -344,11 +344,14 @@ fn mir_promoted( } let mut required_consts = Vec::new(); - let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); + let mut required_items = Vec::new(); + let mut required_consts_visitor = + RequiredConstsVisitor::new(tcx, &body, &mut required_consts, &mut required_items); for (bb, bb_data) in traversal::reverse_postorder(&body) { required_consts_visitor.visit_basic_block_data(bb, bb_data); } body.required_consts = required_consts; + body.required_items = required_items; // What we need to run borrowck etc. let promote_pass = promote_consts::PromoteTemps::default(); diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index abde6a47e83aa..a888956b39a90 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -1,14 +1,23 @@ use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{Const, ConstOperand, Location}; -use rustc_middle::ty::ConstKind; +use rustc_middle::mir::{self, Const, ConstOperand, Location, RequiredItem}; +use rustc_middle::ty::{self, ConstKind, TyCtxt}; +#[allow(unused)] pub struct RequiredConstsVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, required_consts: &'a mut Vec>, + required_items: &'a mut Vec>, } impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { - pub fn new(required_consts: &'a mut Vec>) -> Self { - RequiredConstsVisitor { required_consts } + pub fn new( + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + required_consts: &'a mut Vec>, + required_items: &'a mut Vec>, + ) -> Self { + RequiredConstsVisitor { tcx, body, required_consts, required_items } } } @@ -21,7 +30,27 @@ impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { _ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c), }, Const::Unevaluated(..) => self.required_consts.push(*constant), - Const::Val(..) => {} + Const::Val(_val, ty) => { + // This is how function items get referenced: via zero-sized constants of `FnDef` type + if let ty::FnDef(def_id, args) = ty.kind() { + debug!("adding to required_items: {def_id:?}"); + self.required_items.push(RequiredItem::Fn(*def_id, args)); + } + } } } + + /*fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + self.super_terminator(terminator, location); + + match terminator.kind { + // We don't need to handle `Call` as we already handled all function type operands in + // `visit_constant`. But we do need to handle `Drop`. + mir::TerminatorKind::Drop { place, .. } => { + let ty = place.ty(self.body, self.tcx).ty; + self.required_items.push(RequiredItem::Drop(ty)); + } + _ => {} + } + }*/ } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 32f823a5ac68f..e62861d42edbf 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -345,7 +345,7 @@ fn collect_items_rec<'tcx>( return; } - let mut used_items = Vec::new(); + let mut used_items = MonoItems::new(); let recursion_depth_reset; // Post-monomorphization errors MVP @@ -734,6 +734,19 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { } impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { + fn visit_body(&mut self, body: &mir::Body<'tcx>) { + self.super_body(body); + + // Go over `required_items` *after* body, so if an item appears in both we reach it the + // "normal" way, via the body. + for item in &body.required_items { + // All these also need monomorphization to ensure that if that leads to error, we find + // those errors. + let item = self.monomorphize(*item); + visit_required_item(self.tcx, item, self.output); + } + } + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { debug!("visiting rvalue {:?}", *rvalue); @@ -915,6 +928,25 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { } } +fn visit_required_item<'tcx>( + tcx: TyCtxt<'tcx>, + item: mir::RequiredItem<'tcx>, + output: &mut MonoItems<'tcx>, +) { + let instance = match item { + mir::RequiredItem::Fn(def_id, args) => { + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args) + } + mir::RequiredItem::Drop(ty) => Instance::resolve_drop_in_place(tcx, ty), + }; + // We pretend this is a direct call, just to make sure this is visited at all. + // "indirect" would mean we also generate some shims, but we don't care about the + // generated code, just about the side-effect of code generation causing errors, so we + // can skip the shims. + // FIXME: track the span so that we can show it here. + visit_instance_use(tcx, instance, /*is_direct_call*/ true, DUMMY_SP, output); +} + fn visit_drop_use<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, @@ -953,7 +985,7 @@ fn visit_instance_use<'tcx>( source: Span, output: &mut MonoItems<'tcx>, ) { - debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); + debug!("visit_instance_use({:?}, is_direct_call={:?})", instance, is_direct_call); if !should_codegen_locally(tcx, &instance) { return; } diff --git a/tests/coverage/async.cov-map b/tests/coverage/async.cov-map index e06d1676358fb..67d149076837a 100644 --- a/tests/coverage/async.cov-map +++ b/tests/coverage/async.cov-map @@ -37,21 +37,21 @@ Number of expressions: 0 Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 17, 20) to (start + 0, 25) -Function name: async::e (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 13, 01, 00, 14] +Function name: async::e +Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 01, 00, 14] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Zero) at (prev + 19, 1) to (start + 0, 20) +- Code(Counter(0)) at (prev + 19, 1) to (start + 0, 20) -Function name: async::e::{closure#0} (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 13, 14, 00, 19] +Function name: async::e::{closure#0} +Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 14, 00, 19] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Zero) at (prev + 19, 20) to (start + 0, 25) +- Code(Counter(0)) at (prev + 19, 20) to (start + 0, 25) Function name: async::f Raw bytes (9): 0x[01, 01, 00, 01, 01, 15, 01, 00, 14] @@ -69,21 +69,21 @@ Number of expressions: 0 Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 21, 20) to (start + 0, 25) -Function name: async::foo (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 17, 01, 00, 1e] +Function name: async::foo +Raw bytes (9): 0x[01, 01, 00, 01, 01, 17, 01, 00, 1e] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Zero) at (prev + 23, 1) to (start + 0, 30) +- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 30) -Function name: async::foo::{closure#0} (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 17, 1e, 00, 2d] +Function name: async::foo::{closure#0} +Raw bytes (9): 0x[01, 01, 00, 01, 01, 17, 1e, 00, 2d] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Zero) at (prev + 23, 30) to (start + 0, 45) +- Code(Counter(0)) at (prev + 23, 30) to (start + 0, 45) Function name: async::g Raw bytes (9): 0x[01, 01, 00, 01, 01, 19, 01, 00, 17] diff --git a/tests/coverage/closure.cov-map b/tests/coverage/closure.cov-map index 9f0d33745bcb5..2f2b75b8f3dd6 100644 --- a/tests/coverage/closure.cov-map +++ b/tests/coverage/closure.cov-map @@ -33,16 +33,20 @@ Number of file 0 mappings: 24 - Code(Expression(1, Add)) at (prev + 1, 5) to (start + 3, 2) = (c1 + (c0 - c1)) -Function name: closure::main::{closure#0} (unused) -Raw bytes (24): 0x[01, 01, 00, 04, 00, 28, 05, 02, 14, 00, 02, 15, 02, 0a, 00, 02, 0a, 00, 0b, 00, 01, 09, 01, 06] +Function name: closure::main::{closure#0} +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 28, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 07, 01, 09, 01, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 0 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 -- Code(Zero) at (prev + 40, 5) to (start + 2, 20) -- Code(Zero) at (prev + 2, 21) to (start + 2, 10) -- Code(Zero) at (prev + 2, 10) to (start + 0, 11) -- Code(Zero) at (prev + 1, 9) to (start + 1, 6) +- Code(Counter(0)) at (prev + 40, 5) to (start + 2, 20) +- Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10) +- Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 1, 9) to (start + 1, 6) + = (c1 + (c0 - c1)) Function name: closure::main::{closure#10} (unused) Raw bytes (10): 0x[01, 01, 00, 01, 00, 9b, 01, 07, 00, 21] diff --git a/tests/ui/consts/const-eval/erroneous-const.stderr b/tests/ui/consts/const-eval/erroneous-const.stderr deleted file mode 100644 index bd25e96c2cf13..0000000000000 --- a/tests/ui/consts/const-eval/erroneous-const.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/erroneous-const.rs:6:22 - | -LL | const VOID: () = [()][2]; - | ^^^^^^^ index out of bounds: the length is 1 but the index is 2 - -note: erroneous constant encountered - --> $DIR/erroneous-const.rs:13:13 - | -LL | PrintName::::VOID; - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/erroneous-const2.stderr b/tests/ui/consts/const-eval/erroneous-const2.stderr deleted file mode 100644 index 6a5839e3dfb41..0000000000000 --- a/tests/ui/consts/const-eval/erroneous-const2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/erroneous-const2.rs:6:22 - | -LL | const VOID: () = [()][2]; - | ^^^^^^^ index out of bounds: the length is 1 but the index is 2 - -note: erroneous constant encountered - --> $DIR/erroneous-const2.rs:13:9 - | -LL | PrintName::::VOID; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/unused-broken-const-late.stderr b/tests/ui/consts/const-eval/unused-broken-const-late.stderr deleted file mode 100644 index c2cf2f3813c5e..0000000000000 --- a/tests/ui/consts/const-eval/unused-broken-const-late.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/unused-broken-const-late.rs:8:22 - | -LL | const VOID: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/unused-broken-const-late.rs:8:22 - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/dead-code-in-called-fn.no-opt.stderr b/tests/ui/consts/required-consts/dead-code-in-called-fn.no-opt.stderr new file mode 100644 index 0000000000000..9a072ac1df697 --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-called-fn.no-opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-called-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-called-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn called::` + --> $DIR/dead-code-in-called-fn.rs:23:5 + | +LL | called::(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/dead-code-in-called-fn.opt.stderr b/tests/ui/consts/required-consts/dead-code-in-called-fn.opt.stderr new file mode 100644 index 0000000000000..9a072ac1df697 --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-called-fn.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-called-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-called-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn called::` + --> $DIR/dead-code-in-called-fn.rs:23:5 + | +LL | called::(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/unused-broken-const-late.rs b/tests/ui/consts/required-consts/dead-code-in-called-fn.rs similarity index 54% rename from tests/ui/consts/const-eval/unused-broken-const-late.rs rename to tests/ui/consts/required-consts/dead-code-in-called-fn.rs index c4916061f9e5a..2dd58b9e71d75 100644 --- a/tests/ui/consts/const-eval/unused-broken-const-late.rs +++ b/tests/ui/consts/required-consts/dead-code-in-called-fn.rs @@ -1,20 +1,24 @@ +//@revisions: opt no-opt //@ build-fail -//@ compile-flags: -O +//@[opt] compile-flags: -O //! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is //! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) -struct PrintName(T); -impl PrintName { - const VOID: () = panic!(); //~ERROR evaluation of `PrintName::::VOID` failed +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } -fn no_codegen() { +#[inline(never)] +fn called() { // Any function that is called is guaranteed to have all consts that syntactically // appear in its body evaluated, even if they only appear in dead code. + // This relies on mono-item collection checking `required_consts` in collected functions. if false { - let _ = PrintName::::VOID; + let _ = Fail::::C; } } + pub fn main() { - no_codegen::(); + called::(); } diff --git a/tests/ui/consts/required-consts/dead-code-in-const-called-fn.no-opt.stderr b/tests/ui/consts/required-consts/dead-code-in-const-called-fn.no-opt.stderr new file mode 100644 index 0000000000000..feacf51cfca0e --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-const-called-fn.no-opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-const-called-fn.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-const-called-fn.rs:8:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/dead-code-in-const-called-fn.rs:17:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/dead-code-in-const-called-fn.opt.stderr b/tests/ui/consts/required-consts/dead-code-in-const-called-fn.opt.stderr new file mode 100644 index 0000000000000..feacf51cfca0e --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-const-called-fn.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-const-called-fn.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-const-called-fn.rs:8:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/dead-code-in-const-called-fn.rs:17:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/erroneous-const.rs b/tests/ui/consts/required-consts/dead-code-in-const-called-fn.rs similarity index 54% rename from tests/ui/consts/const-eval/erroneous-const.rs rename to tests/ui/consts/required-consts/dead-code-in-const-called-fn.rs index 74d44c5259a3e..03af0da78a3e3 100644 --- a/tests/ui/consts/const-eval/erroneous-const.rs +++ b/tests/ui/consts/required-consts/dead-code-in-const-called-fn.rs @@ -1,16 +1,20 @@ +//@revisions: opt no-opt +//@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. #![allow(unconditional_panic)] -struct PrintName(T); -impl PrintName { - const VOID: () = [()][2]; //~ERROR evaluation of `PrintName::::VOID` failed +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } +#[inline(never)] const fn no_codegen() { if false { // This bad constant is only used in dead code in a no-codegen function... and yet we still // must make sure that the build fails. - PrintName::::VOID; //~ constant + // This relies on const-eval evaluating all `required_consts` of `const fn`. + Fail::::C; //~ constant } } diff --git a/tests/ui/consts/required-consts/dead-code-in-dead-drop.noopt.stderr b/tests/ui/consts/required-consts/dead-code-in-dead-drop.noopt.stderr new file mode 100644 index 0000000000000..14fe323272f0d --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-dead-drop.noopt.stderr @@ -0,0 +1,14 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-dead-drop.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-dead-drop.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn as std::ops::Drop>::drop` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/dead-code-in-dead-drop.rs b/tests/ui/consts/required-consts/dead-code-in-dead-drop.rs new file mode 100644 index 0000000000000..d6ec873e99df1 --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-dead-drop.rs @@ -0,0 +1,33 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but is mentioned implicitly as destructor in dead code in a +// function that is called. Make sure we still find this error. +impl Drop for Fail { + fn drop(&mut self) { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called(x: T) { + if false { + let v = Fail(x); + drop(v); + } +} + +pub fn main() { + called::(0); +} diff --git a/tests/ui/consts/required-consts/dead-code-in-dead-fn.no-opt.stderr b/tests/ui/consts/required-consts/dead-code-in-dead-fn.no-opt.stderr new file mode 100644 index 0000000000000..5c410fc7c4570 --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-dead-fn.no-opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-dead-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-dead-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/dead-code-in-dead-fn.rs:26:9 + | +LL | not_called::(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/dead-code-in-dead-fn.opt.stderr b/tests/ui/consts/required-consts/dead-code-in-dead-fn.opt.stderr new file mode 100644 index 0000000000000..4c4c5f85f697e --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-dead-fn.opt.stderr @@ -0,0 +1,13 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-dead-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-dead-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn not_called::` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/dead-code-in-dead-fn.rs b/tests/ui/consts/required-consts/dead-code-in-dead-fn.rs new file mode 100644 index 0000000000000..826149ca2a81f --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-dead-fn.rs @@ -0,0 +1,32 @@ +//@revisions: opt no-opt +//@ build-fail +//@[opt] compile-flags: -O +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but it is mentioned in dead code in a function that is +// called. Make sure we still find this error. +// This relies on mono-item collection checking `required_consts` in functions that syntactically +// are called in collected functions (even inside dead code). +#[inline(never)] +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called() { + if false { + not_called::(); + } +} + +pub fn main() { + called::(); +} diff --git a/tests/ui/consts/required-consts/dead-code-in-dead-vtable.noopt.stderr b/tests/ui/consts/required-consts/dead-code-in-dead-vtable.noopt.stderr new file mode 100644 index 0000000000000..ab60450049546 --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-dead-vtable.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-dead-vtable.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-dead-vtable.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn as MyTrait>::not_called` + --> $DIR/dead-code-in-dead-vtable.rs:35:40 + | +LL | let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here + | ^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/dead-code-in-dead-vtable.rs b/tests/ui/consts/required-consts/dead-code-in-dead-vtable.rs new file mode 100644 index 0000000000000..f21a1cc1fc2fd --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-dead-vtable.rs @@ -0,0 +1,41 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +trait MyTrait { + fn not_called(&self); +} + +// This function is not actually called, but it is mentioned in a vtable in a function that is +// called. Make sure we still find this error. +// This relies on mono-item collection checking `required_consts` in functions that are referenced +// in vtables that syntactically appear in collected functions (even inside dead code). +impl MyTrait for Vec { + fn not_called(&self) { + if false { + let _ = Fail::::C; + } + } +} + +#[inline(never)] +fn called() { + if false { + let v: Vec = Vec::new(); + let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here + } +} + +pub fn main() { + called::(); +} diff --git a/tests/ui/consts/required-consts/dead-code-in-static.no-opt.stderr b/tests/ui/consts/required-consts/dead-code-in-static.no-opt.stderr new file mode 100644 index 0000000000000..b64e2fc45f430 --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-static.no-opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-static.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-static.rs:8:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/dead-code-in-static.rs:16:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/dead-code-in-static.opt.stderr b/tests/ui/consts/required-consts/dead-code-in-static.opt.stderr new file mode 100644 index 0000000000000..b64e2fc45f430 --- /dev/null +++ b/tests/ui/consts/required-consts/dead-code-in-static.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/dead-code-in-static.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-static.rs:8:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/dead-code-in-static.rs:16:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/erroneous-const2.rs b/tests/ui/consts/required-consts/dead-code-in-static.rs similarity index 51% rename from tests/ui/consts/const-eval/erroneous-const2.rs rename to tests/ui/consts/required-consts/dead-code-in-static.rs index 61f2955f2d822..b1da8153e944b 100644 --- a/tests/ui/consts/const-eval/erroneous-const2.rs +++ b/tests/ui/consts/required-consts/dead-code-in-static.rs @@ -1,16 +1,19 @@ +//@revisions: opt no-opt +//@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. #![allow(unconditional_panic)] -struct PrintName(T); -impl PrintName { - const VOID: () = [()][2]; //~ERROR evaluation of `PrintName::::VOID` failed +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } pub static FOO: () = { if false { // This bad constant is only used in dead code in a static initializer... and yet we still // must make sure that the build fails. - PrintName::::VOID; //~ constant + // This relies on const-eval evaluating all `required_consts` of the `static` MIR body. + Fail::::C; //~ constant } };