diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 1a18a84b3585d..8d9a5f5a36b6d 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1513,16 +1513,26 @@ fn collect_const_value<'tcx>( // Find all non-generic items by walking the HIR. These items serve as roots to // start monomorphizing from. #[instrument(skip(tcx, mode), level = "debug")] -fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec> { +fn collect_roots( + tcx: TyCtxt<'_>, + mode: MonoItemCollectionStrategy, +) -> Vec<(MonoItem<'_>, CollectionMode)> { debug!("collecting roots"); - let mut roots = Vec::new(); + let mut used_roots = MonoItems::new(); + let mut mentioned_roots = MonoItems::new(); { let entry_fn = tcx.entry_fn(()); debug!("collect_roots: entry_fn = {:?}", entry_fn); - let mut collector = RootCollector { tcx, strategy: mode, entry_fn, output: &mut roots }; + let mut collector = RootCollector { + tcx, + strategy: mode, + entry_fn, + used_roots: &mut used_roots, + mentioned_roots: &mut mentioned_roots, + }; let crate_items = tcx.hir_crate_items(()); @@ -1537,21 +1547,30 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec { tcx: TyCtxt<'tcx>, strategy: MonoItemCollectionStrategy, - output: &'a mut MonoItems<'tcx>, + // `MonoItems` includes spans we don't actually want... but this lets us reuse some of the + // collector's functions. + used_roots: &'a mut MonoItems<'tcx>, + mentioned_roots: &'a mut MonoItems<'tcx>, entry_fn: Option<(DefId, EntryFnType)>, } @@ -1565,7 +1584,7 @@ impl<'v> RootCollector<'_, 'v> { debug!("RootCollector: ADT drop-glue for `{id:?}`",); let ty = self.tcx.type_of(id.owner_id.to_def_id()).no_bound_vars().unwrap(); - visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output); + visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.used_roots); } } DefKind::GlobalAsm => { @@ -1573,12 +1592,12 @@ impl<'v> RootCollector<'_, 'v> { "RootCollector: ItemKind::GlobalAsm({})", self.tcx.def_path_str(id.owner_id) ); - self.output.push(dummy_spanned(MonoItem::GlobalAsm(id))); + self.used_roots.push(dummy_spanned(MonoItem::GlobalAsm(id))); } DefKind::Static { .. } => { let def_id = id.owner_id.to_def_id(); debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id)); - self.output.push(dummy_spanned(MonoItem::Static(def_id))); + self.used_roots.push(dummy_spanned(MonoItem::Static(def_id))); } DefKind::Const => { // const items only generate mono items if they are @@ -1586,12 +1605,12 @@ impl<'v> RootCollector<'_, 'v> { // but even just declaring them must collect the items they refer to if let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) { - collect_const_value(self.tcx, val, self.output); + collect_const_value(self.tcx, val, self.used_roots); } } DefKind::Impl { .. } => { if self.strategy == MonoItemCollectionStrategy::Eager { - create_mono_items_for_default_impls(self.tcx, id, self.output); + create_mono_items_for_default_impls(self.tcx, id, self.used_roots); } } DefKind::Fn => { @@ -1607,31 +1626,54 @@ impl<'v> RootCollector<'_, 'v> { } } - fn is_root(&self, def_id: LocalDefId) -> bool { - !self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) - && match self.strategy { - MonoItemCollectionStrategy::Eager => true, - MonoItemCollectionStrategy::Lazy => { - self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id) - || self.tcx.is_reachable_non_generic(def_id) - || self - .tcx - .codegen_fn_attrs(def_id) - .flags - .contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) - } + /// Determines whether this is an item we start walking, and in which mode. The "real" roots are + /// walked as "used" items, but that set is optimization-dependent. We add all other non-generic + /// items as "mentioned" roots. This makes the set of items where `is_root` return `Some` + /// optimization-independent, which is crucial to ensure that optimized and unoptimized builds + /// evaluate the same constants. + fn is_root(&self, def_id: LocalDefId) -> Option { + // Generic things are never roots. + if self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) { + return None; + } + // We have to skip `must_be_overridden` bodies; asking for their MIR ICEs. + if self.tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) { + return None; + } + // The rest is definitely a root, but is it used or merely mentioned? + // Determine whether this item is reachable, which makes it "used". + let is_used_root = match self.strategy { + MonoItemCollectionStrategy::Eager => true, + MonoItemCollectionStrategy::Lazy => { + self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id) + || self.tcx.is_reachable_non_generic(def_id) + || self + .tcx + .codegen_fn_attrs(def_id) + .flags + .contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) } + }; + if is_used_root { + Some(CollectionMode::UsedItems) + } else { + Some(CollectionMode::MentionedItems) + } } /// If `def_id` represents a root, pushes it onto the list of /// outputs. (Note that all roots must be monomorphic.) #[instrument(skip(self), level = "debug")] fn push_if_root(&mut self, def_id: LocalDefId) { - if self.is_root(def_id) { + if let Some(mode) = self.is_root(def_id) { debug!("found root"); let instance = Instance::mono(self.tcx, def_id.to_def_id()); - self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP)); + let mono_item = create_fn_mono_item(self.tcx, instance, DUMMY_SP); + match mode { + CollectionMode::UsedItems => self.used_roots.push(mono_item), + CollectionMode::MentionedItems => self.mentioned_roots.push(mono_item), + } } } @@ -1667,7 +1709,7 @@ impl<'v> RootCollector<'_, 'v> { self.tcx.mk_args(&[main_ret_ty.into()]), ); - self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP)); + self.used_roots.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP)); } } @@ -1772,7 +1814,7 @@ pub fn collect_crate_mono_items( let state: LRef<'_, _> = &mut state; tcx.sess.time("monomorphization_collector_graph_walk", || { - par_for_each_in(roots, |root| { + par_for_each_in(roots, |(root, mode)| { let mut recursion_depths = DefIdMap::default(); collect_items_rec( tcx, @@ -1780,7 +1822,7 @@ pub fn collect_crate_mono_items( state, &mut recursion_depths, recursion_limit, - CollectionMode::UsedItems, + mode, ); }); }); diff --git a/tests/incremental/callee_caller_cross_crate/b.rs b/tests/incremental/callee_caller_cross_crate/b.rs index b9012ecf7f77d..1645be156c776 100644 --- a/tests/incremental/callee_caller_cross_crate/b.rs +++ b/tests/incremental/callee_caller_cross_crate/b.rs @@ -6,7 +6,7 @@ extern crate a; -#[rustc_clean(except="typeck", cfg="rpass2")] +#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] pub fn call_function0() { a::function0(77); } diff --git a/tests/incremental/dirty_clean.rs b/tests/incremental/dirty_clean.rs index 24aedb011c6b9..072ecdcf225ac 100644 --- a/tests/incremental/dirty_clean.rs +++ b/tests/incremental/dirty_clean.rs @@ -36,6 +36,7 @@ mod y { //[cfail2]~| ERROR `type_of(y)` should be dirty but is not //[cfail2]~| ERROR `fn_sig(y)` should be dirty but is not //[cfail2]~| ERROR `typeck(y)` should be clean but is not + //[cfail2]~| ERROR `optimized_mir(y)` should be clean but is not x::x(); } } diff --git a/tests/incremental/hashes/call_expressions.rs b/tests/incremental/hashes/call_expressions.rs index 36b6980bcc67f..4fa1d0c28d7c8 100644 --- a/tests/incremental/hashes/call_expressions.rs +++ b/tests/incremental/hashes/call_expressions.rs @@ -62,9 +62,9 @@ mod change_callee_indirectly_function { #[cfg(not(any(cfail1,cfail4)))] use super::callee2 as callee; - #[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")] + #[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] - #[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")] + #[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] pub fn change_callee_indirectly_function() { callee(1, 2) diff --git a/tests/incremental/hashes/indexing_expressions.rs b/tests/incremental/hashes/indexing_expressions.rs index 1b90483c50c3b..d69de505fd175 100644 --- a/tests/incremental/hashes/indexing_expressions.rs +++ b/tests/incremental/hashes/indexing_expressions.rs @@ -23,9 +23,9 @@ fn change_simple_index(slice: &[u32]) -> u32 { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail5")] +#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] fn change_simple_index(slice: &[u32]) -> u32 { slice[4] @@ -40,9 +40,9 @@ fn change_lower_bound(slice: &[u32]) -> &[u32] { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail5")] +#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] fn change_lower_bound(slice: &[u32]) -> &[u32] { &slice[2..5] @@ -57,9 +57,9 @@ fn change_upper_bound(slice: &[u32]) -> &[u32] { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail5")] +#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] fn change_upper_bound(slice: &[u32]) -> &[u32] { &slice[3..7] @@ -74,9 +74,9 @@ fn add_lower_bound(slice: &[u32]) -> &[u32] { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")] +#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")] +#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] fn add_lower_bound(slice: &[u32]) -> &[u32] { &slice[3..4] @@ -91,9 +91,9 @@ fn add_upper_bound(slice: &[u32]) -> &[u32] { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")] +#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")] +#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] fn add_upper_bound(slice: &[u32]) -> &[u32] { &slice[3..7] @@ -108,9 +108,9 @@ fn change_mutability(slice: &mut [u32]) -> u32 { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")] +#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")] +#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] fn change_mutability(slice: &mut [u32]) -> u32 { (& slice[3..5])[0] @@ -125,9 +125,9 @@ fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")] +#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")] +#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { &slice[3..=7] diff --git a/tests/incremental/ich_method_call_trait_scope.rs b/tests/incremental/ich_method_call_trait_scope.rs index a4c6558602f7b..bf848322e7510 100644 --- a/tests/incremental/ich_method_call_trait_scope.rs +++ b/tests/incremental/ich_method_call_trait_scope.rs @@ -26,7 +26,7 @@ mod mod3 { #[cfg(rpass2)] use Trait2; - #[rustc_clean(except="typeck", cfg="rpass2")] + #[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] fn bar() { ().method(); } diff --git a/tests/incremental/ich_resolve_results.rs b/tests/incremental/ich_resolve_results.rs index c5b2b66fef9f8..82ec759ebdb3e 100644 --- a/tests/incremental/ich_resolve_results.rs +++ b/tests/incremental/ich_resolve_results.rs @@ -36,7 +36,7 @@ mod mod3 { } #[rustc_clean(cfg="rpass2")] - #[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="rpass3")] + #[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="rpass3")] fn in_type() { test::(); } diff --git a/tests/incremental/struct_add_field.rs b/tests/incremental/struct_add_field.rs index ad1b6fcfe2828..6e9512fc48a93 100644 --- a/tests/incremental/struct_add_field.rs +++ b/tests/incremental/struct_add_field.rs @@ -21,12 +21,12 @@ pub struct Y { pub y: char } -#[rustc_clean(except="fn_sig,typeck", cfg="rpass2")] +#[rustc_clean(except="fn_sig,typeck,optimized_mir", cfg="rpass2")] pub fn use_X(x: X) -> u32 { x.x as u32 } -#[rustc_clean(except="typeck", cfg="rpass2")] +#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] pub fn use_EmbedX(embed: EmbedX) -> u32 { embed.x.x as u32 } diff --git a/tests/incremental/struct_change_field_type.rs b/tests/incremental/struct_change_field_type.rs index aed6c8884ac47..1c0d591a3ad8e 100644 --- a/tests/incremental/struct_change_field_type.rs +++ b/tests/incremental/struct_change_field_type.rs @@ -24,13 +24,13 @@ pub struct Y { pub y: char } -#[rustc_clean(except="typeck", cfg="rpass2")] +#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] pub fn use_X() -> u32 { let x: X = X { x: 22 }; x.x as u32 } -#[rustc_clean(except="typeck", cfg="rpass2")] +#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] pub fn use_EmbedX(x: EmbedX) -> u32 { let x: X = X { x: 22 }; x.x as u32 diff --git a/tests/incremental/struct_change_field_type_cross_crate/b.rs b/tests/incremental/struct_change_field_type_cross_crate/b.rs index 00bc3db0de86b..1bc94585b87ca 100644 --- a/tests/incremental/struct_change_field_type_cross_crate/b.rs +++ b/tests/incremental/struct_change_field_type_cross_crate/b.rs @@ -8,13 +8,13 @@ extern crate a; use a::*; -#[rustc_clean(except="typeck", cfg="rpass2")] +#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] pub fn use_X() -> u32 { let x: X = X { x: 22 }; x.x as u32 } -#[rustc_clean(except="typeck", cfg="rpass2")] +#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] pub fn use_EmbedX(embed: EmbedX) -> u32 { embed.x.x as u32 } diff --git a/tests/incremental/struct_remove_field.rs b/tests/incremental/struct_remove_field.rs index 488d7996b339b..059c8b7a92b39 100644 --- a/tests/incremental/struct_remove_field.rs +++ b/tests/incremental/struct_remove_field.rs @@ -1,4 +1,4 @@ -// Test incremental compilation tracking where we change field names +// Test incremental compilation tracking where we remove a field // in between revisions (hashing should be stable). //@ revisions:rpass1 rpass2 @@ -25,12 +25,12 @@ pub struct Y { pub y: char } -#[rustc_clean(except="typeck,fn_sig", cfg="rpass2")] +#[rustc_clean(except="typeck,fn_sig,optimized_mir", cfg="rpass2")] pub fn use_X(x: X) -> u32 { x.x as u32 } -#[rustc_clean(except="typeck", cfg="rpass2")] +#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] pub fn use_EmbedX(embed: EmbedX) -> u32 { embed.x.x as u32 } diff --git a/tests/incremental/type_alias_cross_crate/b.rs b/tests/incremental/type_alias_cross_crate/b.rs index 7187336ba5ee5..8ab4ebcd8e06e 100644 --- a/tests/incremental/type_alias_cross_crate/b.rs +++ b/tests/incremental/type_alias_cross_crate/b.rs @@ -6,7 +6,7 @@ extern crate a; -#[rustc_clean(except="typeck", cfg="rpass2")] +#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")] #[rustc_clean(cfg="rpass3")] pub fn use_X() -> u32 { let x: a::X = 22; diff --git a/tests/ui/consts/required-consts/collect-roots-dead-fn1.noopt.stderr b/tests/ui/consts/required-consts/collect-roots-dead-fn1.noopt.stderr new file mode 100644 index 0000000000000..69b275ae5e575 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-dead-fn1.noopt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-roots-dead-fn1.rs:15:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-dead-fn1.rs:15: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/collect-roots-dead-fn1.rs:31:5 + | +LL | Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn h::` + --> $DIR/collect-roots-dead-fn1.rs:24:5 + | +LL | h::() + | ^^^^^^^^^^ + +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/collect-roots-dead-fn1.opt.stderr b/tests/ui/consts/required-consts/collect-roots-dead-fn1.opt.stderr new file mode 100644 index 0000000000000..69b275ae5e575 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-dead-fn1.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-roots-dead-fn1.rs:15:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-dead-fn1.rs:15: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/collect-roots-dead-fn1.rs:31:5 + | +LL | Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn h::` + --> $DIR/collect-roots-dead-fn1.rs:24:5 + | +LL | h::() + | ^^^^^^^^^^ + +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/collect-roots-dead-fn1.rs b/tests/ui/consts/required-consts/collect-roots-dead-fn1.rs new file mode 100644 index 0000000000000..62ef421c18db4 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-dead-fn1.rs @@ -0,0 +1,32 @@ +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O + +//! This used to fail in optimized builds but pass in unoptimized builds. The reason is that in +//! optimized builds, `f` gets marked as cross-crate-inlineable, so the functions it calls become +//! reachable, and therefore `g` becomes a collection root. But in unoptimized builds, `g` is no +//! root, and the call to `g` disappears in an early `SimplifyCfg` before "mentioned items" are +//! gathered, so we never reach `g`. +#![crate_type = "lib"] + +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR: evaluation of `Fail::::C` failed +} + +pub fn f() { + loop {}; g() +} + +#[inline(never)] +fn g() { + h::() +} + +// Make sure we only use the faulty const in a generic function, or +// else it gets evaluated by some MIR pass. +#[inline(never)] +fn h() { + Fail::::C; +} diff --git a/tests/ui/consts/required-consts/collect-roots-dead-fn2.rs b/tests/ui/consts/required-consts/collect-roots-dead-fn2.rs new file mode 100644 index 0000000000000..9e2b500e43ce1 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-dead-fn2.rs @@ -0,0 +1,23 @@ +//@revisions: noopt opt +//@ build-pass +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O + +//! A slight variant of `collect-roots-in-dead-fn` where the dead call is itself generic. Now this +//! *passes* in both optimized and unoptimized builds: the call to `h` always disappears in an early +//! `SimplifyCfg`, and `h` is generic so it can never be a root. +#![crate_type = "lib"] + +struct Fail(T); +impl Fail { + const C: () = panic!(); +} + +pub fn f() { + loop {}; h::() +} + +#[inline(never)] +fn h() { + Fail::::C; +} diff --git a/tests/ui/consts/required-consts/collect-roots-dead-fn3.noopt.stderr b/tests/ui/consts/required-consts/collect-roots-dead-fn3.noopt.stderr new file mode 100644 index 0000000000000..f99f1043e39c6 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-dead-fn3.noopt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `fail::<()>::{constant#0}` failed + --> $DIR/collect-roots-dead-fn3.rs:35:13 + | +LL | const { panic!(); } + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-dead-fn3.rs:35:13 + | + = 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/collect-roots-dead-fn3.rs:35:5 + | +LL | const { panic!(); } + | ^^^^^^^^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn fail::<()>` + --> $DIR/collect-roots-dead-fn3.rs:28:5 + | +LL | fail::<()>(); + | ^^^^^^^^^^^^ + +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/collect-roots-dead-fn3.opt.stderr b/tests/ui/consts/required-consts/collect-roots-dead-fn3.opt.stderr new file mode 100644 index 0000000000000..f99f1043e39c6 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-dead-fn3.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `fail::<()>::{constant#0}` failed + --> $DIR/collect-roots-dead-fn3.rs:35:13 + | +LL | const { panic!(); } + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-dead-fn3.rs:35:13 + | + = 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/collect-roots-dead-fn3.rs:35:5 + | +LL | const { panic!(); } + | ^^^^^^^^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn fail::<()>` + --> $DIR/collect-roots-dead-fn3.rs:28:5 + | +LL | fail::<()>(); + | ^^^^^^^^^^^^ + +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/collect-roots-dead-fn3.rs b/tests/ui/consts/required-consts/collect-roots-dead-fn3.rs new file mode 100644 index 0000000000000..b0ad2e636984f --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-dead-fn3.rs @@ -0,0 +1,36 @@ +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O + +#![crate_type = "lib"] +#![feature(inline_const)] + +// Will be `inline` with optimizations, so then `g::<()>` becomes reachable. At the same time `g` is +// not "mentioned" in `f` since it is only called inside an inline `const` and hence never appears +// in its MIR! This fundamentally is an issue caused by reachability walking the HIR (before inline +// `const` are extracted) and collection being based on MIR. +pub fn f() { + loop {}; const { g::<()>() } +} + +// When this comes reachable (for any `T`), so does `hidden_root`. +// And `hidden_root` is monomorphic so it can become a root! +const fn g() { + match std::mem::size_of::() { + 0 => (), + _ => hidden_root(), + } +} + +#[inline(never)] +const fn hidden_root() { + fail::<()>(); +} + +#[inline(never)] +const fn fail() { + // Hiding this in a generic fn so that it doesn't get evaluated by + // MIR passes. + const { panic!(); } //~ERROR: evaluation of `fail::<()>::{constant#0}` failed +} diff --git a/tests/ui/consts/required-consts/collect-roots-inline-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-roots-inline-fn.noopt.stderr new file mode 100644 index 0000000000000..2d28a787c3a72 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-inline-fn.noopt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Zst::::ASSERT` failed + --> $DIR/collect-roots-inline-fn.rs:13:9 + | +LL | panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-inline-fn.rs:13:9 + | + = 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/collect-roots-inline-fn.rs:18:5 + | +LL | Zst::::ASSERT; + | ^^^^^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn f::` + --> $DIR/collect-roots-inline-fn.rs:22:5 + | +LL | f::() + | ^^^^^^^^^^ + +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/collect-roots-inline-fn.opt.stderr b/tests/ui/consts/required-consts/collect-roots-inline-fn.opt.stderr new file mode 100644 index 0000000000000..2f31ff2806dcd --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-inline-fn.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Zst::::ASSERT` failed + --> $DIR/collect-roots-inline-fn.rs:13:9 + | +LL | panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-inline-fn.rs:13:9 + | + = 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/collect-roots-inline-fn.rs:18:5 + | +LL | Zst::::ASSERT; + | ^^^^^^^^^^^^^^^^ + +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/collect-roots-inline-fn.rs b/tests/ui/consts/required-consts/collect-roots-inline-fn.rs new file mode 100644 index 0000000000000..1fc22157bf7d1 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-roots-inline-fn.rs @@ -0,0 +1,23 @@ +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O + +//! In optimized builds, the functions in this crate are all marked "inline" so none of them become +//! collector roots. Ensure that we still evaluate the constants. +#![crate_type = "lib"] + +struct Zst(T); +impl Zst { + const ASSERT: () = if std::mem::size_of::() != 0 { + panic!(); //~ERROR: evaluation of `Zst::::ASSERT` failed + }; +} + +fn f() { + Zst::::ASSERT; +} + +pub fn g() { + f::() +}