From 712fe36611cf18306d3912beb5456cb0a7d0d276 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 15 Mar 2024 21:06:40 +0100 Subject: [PATCH 01/10] collector: recursively traverse 'mentioned' items to evaluate their constants --- compiler/rustc_data_structures/src/sync.rs | 9 +- compiler/rustc_middle/src/mir/mod.rs | 29 ++ .../rustc_mir_build/src/build/custom/mod.rs | 1 + compiler/rustc_mir_transform/src/inline.rs | 27 +- compiler/rustc_mir_transform/src/lib.rs | 5 + .../src/mentioned_items.rs | 57 +++ compiler/rustc_mir_transform/src/shim.rs | 4 +- compiler/rustc_monomorphize/src/collector.rs | 382 +++++++++++++----- .../rustc_monomorphize/src/partitioning.rs | 14 +- .../collect-in-dead-drop.noopt.stderr | 6 +- .../collect-in-dead-drop.opt.stderr | 20 + .../required-consts/collect-in-dead-drop.rs | 7 +- .../collect-in-dead-fn.noopt.stderr | 8 +- .../collect-in-dead-fn.opt.stderr | 23 ++ .../required-consts/collect-in-dead-fn.rs | 7 +- ...ollect-in-dead-fnptr-in-const.noopt.stderr | 20 + .../collect-in-dead-fnptr-in-const.opt.stderr | 20 + .../collect-in-dead-fnptr-in-const.rs | 33 ++ .../collect-in-dead-fnptr.noopt.stderr | 23 ++ .../collect-in-dead-fnptr.opt.stderr | 23 ++ .../required-consts/collect-in-dead-fnptr.rs | 32 ++ .../collect-in-dead-move.noopt.stderr | 6 +- .../collect-in-dead-move.opt.stderr | 20 + .../required-consts/collect-in-dead-move.rs | 7 +- 24 files changed, 641 insertions(+), 142 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/mentioned_items.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fnptr.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 32202ac3ede..eab6d8168ca 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -20,6 +20,7 @@ //! | ----------------------- | ------------------- | ------------------------------- | //! | `Lrc` | `rc::Rc` | `sync::Arc` | //! |` Weak` | `rc::Weak` | `sync::Weak` | +//! | `LRef<'a, T>` [^2] | `&'a mut T` | `&'a T` | //! | | | | //! | `AtomicBool` | `Cell` | `atomic::AtomicBool` | //! | `AtomicU32` | `Cell` | `atomic::AtomicU32` | @@ -38,7 +39,7 @@ //! of a `RefCell`. This is appropriate when interior mutability is not //! required. //! -//! [^2] `MTLockRef` is a typedef. +//! [^2] `MTRef`, `MTLockRef` are type aliases. pub use crate::marker::*; use std::collections::HashMap; @@ -208,7 +209,7 @@ cfg_match! { use std::cell::RefCell as InnerRwLock; - pub type MTLockRef<'a, T> = &'a mut MTLock; + pub type LRef<'a, T> = &'a mut T; #[derive(Debug, Default)] pub struct MTLock(T); @@ -274,7 +275,7 @@ cfg_match! { pub use std::sync::Arc as Lrc; pub use std::sync::Weak as Weak; - pub type MTLockRef<'a, T> = &'a MTLock; + pub type LRef<'a, T> = &'a T; #[derive(Debug, Default)] pub struct MTLock(Lock); @@ -314,6 +315,8 @@ cfg_match! { } } +pub type MTLockRef<'a, T> = LRef<'a, MTLock>; + #[derive(Default)] #[cfg_attr(parallel_compiler, repr(align(64)))] pub struct CacheAligned(pub T); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d57ffc0f8b5..05e8360b93f 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -20,6 +20,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind}; use rustc_hir::{self as hir, HirId}; use rustc_session::Session; +use rustc_span::source_map::Spanned; use rustc_target::abi::{FieldIdx, VariantIdx}; use polonius_engine::Atom; @@ -312,6 +313,16 @@ 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 MentionedItem<'tcx> { + Fn(DefId, GenericArgsRef<'tcx>), + Drop(Ty<'tcx>), + // FIXME: add Vtable { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> }, + // FIXME: do we have to add closures? +} + /// The lowered representation of a single function. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct Body<'tcx> { @@ -375,8 +386,24 @@ pub struct Body<'tcx> { /// Constants that are required to evaluate successfully for this MIR to be well-formed. /// We hold in this field all the constants we are not able to evaluate yet. + /// + /// This is soundness-critical, we make a guarantee that all consts syntactically mentioned in a + /// function have successfully evaluated if the function ever gets executed at runtime. pub required_consts: Vec>, + /// Further items that were mentioned in this function and hence *may* become monomorphized, + /// depending on optimizations. We use this to avoid optimization-dependent compile errors: the + /// collector recursively traverses all "mentioned" items and evaluates all their + /// `required_consts`. + /// + /// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee. + /// All that's relevant is that this set is optimization-level-independent, and that it includes + /// everything that the collector would consider "used". (For example, we currently compute this + /// set after drop elaboration, so some drop calls that can never be reached are not considered + /// "mentioned".) See the documentation of `CollectionMode` in + /// `compiler/rustc_monomorphize/src/collector.rs` for more context. + pub mentioned_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. @@ -453,6 +480,7 @@ impl<'tcx> Body<'tcx> { var_debug_info, span, required_consts: Vec::new(), + mentioned_items: Vec::new(), is_polymorphic: false, injection_phase: None, tainted_by_errors, @@ -482,6 +510,7 @@ impl<'tcx> Body<'tcx> { spread_arg: None, span: DUMMY_SP, required_consts: Vec::new(), + mentioned_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 288b787798b..109ffedec55 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(), + mentioned_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 f6a0945c222..0f6a7ae982c 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,30 @@ impl<'tcx> Inliner<'tcx> { Const::Val(..) | Const::Unevaluated(..) => true, }, )); + // Now that we incorporated the callee's `required_consts`, we can remove the callee from + // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does + // some extra work here to save the monomorphization collector work later. It helps a lot, + // since monomorphization can avoid a lot of work when the "mentioned items" are similar to + // the actually used items. By doing this we can entirely avoid visiting the callee! + let callee_item = { + // We need to reconstruct the `required_item` for the callee so that we can find and + // remove it. + let func_ty = func.ty(caller_body, self.tcx); + match func_ty.kind() { + ty::FnDef(def_id, args) => MentionedItem::Fn(*def_id, args), + _ => bug!(), + } + }; + if let Some(idx) = + caller_body.mentioned_items.iter().position(|item| item.node == callee_item) + { + // We found the callee, so remove it and add its items instead. + caller_body.mentioned_items.remove(idx); + caller_body.mentioned_items.extend(callee_body.mentioned_items); + } else { + // If we can't find the callee, there's no point in adding its items. + // Probably it already got removed by being inlined elsewhere in the same function. + } } fn make_call_args( diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index afe228be127..74e7d51ea96 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -89,6 +89,7 @@ mod lint; mod lower_intrinsics; mod lower_slice_len; mod match_branches; +mod mentioned_items; mod multiple_return_terminators; mod normalize_array_len; mod nrvo; @@ -566,6 +567,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { tcx, body, &[ + // Before doing anything, remember which items are being mentioned so that the set of items + // visited does not depend on the optimization level. + &mentioned_items::MentionedItems, + // Add some UB checks before any UB gets optimized away. &check_alignment::CheckAlignment, // Before inlining: trim down MIR with passes to reduce inlining work. diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs new file mode 100644 index 00000000000..ed363b4f252 --- /dev/null +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -0,0 +1,57 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{self, ConstOperand, Location, MentionedItem, MirPass}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::Session; +use rustc_span::source_map::Spanned; + +pub struct MentionedItems; + +struct MentionedItemsVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + mentioned_items: &'a mut Vec>>, +} + +impl<'tcx> MirPass<'tcx> for MentionedItems { + fn is_enabled(&self, _sess: &Session) -> bool { + // If this pass is skipped the collector assume that nothing got mentioned! We could + // potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses + // of anything, but that still seems fragile. Furthermore, even debug builds use level 1, so + // special-casing level 0 is just not worth it. + true + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { + debug_assert!(body.mentioned_items.is_empty()); + let mut mentioned_items = Vec::new(); + MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body); + body.mentioned_items = mentioned_items; + } +} + +impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { + fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) { + let const_ = constant.const_; + // This is how function items get referenced: via constants of `FnDef` type. This handles + // both functions that are called and those that are just turned to function pointers. + if let ty::FnDef(def_id, args) = const_.ty().kind() { + debug!("adding to required_items: {def_id:?}"); + self.mentioned_items + .push(Spanned { node: MentionedItem::Fn(*def_id, args), span: constant.span }); + } + } + + 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; + let span = self.body.source_info(location).span; + self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span }); + } + _ => {} + } + } +} diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 733e2f93b25..dd2b24ad669 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -17,7 +17,7 @@ use std::iter; use crate::{ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, - pass_manager as pm, remove_noop_landing_pads, simplify, + mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, }; use rustc_middle::mir::patch::MirPatch; use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle}; @@ -147,6 +147,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' tcx, &mut body, &[ + &mentioned_items::MentionedItems, &abort_unwinding_calls::AbortUnwindingCalls, &add_call_guards::CriticalCallEdges, ], @@ -178,6 +179,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' tcx, &mut result, &[ + &mentioned_items::MentionedItems, &add_moves_for_packed_drops::AddMovesForPackedDrops, &deref_separator::Derefer, &remove_noop_landing_pads::RemoveNoopLandingPads, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index d8bdbd8c442..1eb11cec6c5 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -137,21 +137,41 @@ //! just linked to and no node is created; which is exactly what we want, since //! no machine code should be generated in the current crate for such an item. //! -//! Eager and Lazy Collection Mode -//! ------------------------------ -//! Mono item collection can be performed in one of two modes: +//! Eager and Lazy Collection Strategy +//! ---------------------------------- +//! Mono item collection can be performed with one of two strategies: //! -//! - Lazy mode means that items will only be instantiated when actually +//! - Lazy strategy means that items will only be instantiated when actually //! used. The goal is to produce the least amount of machine code //! possible. //! -//! - Eager mode is meant to be used in conjunction with incremental compilation +//! - Eager strategy is meant to be used in conjunction with incremental compilation //! where a stable set of mono items is more important than a minimal -//! one. Thus, eager mode will instantiate drop-glue for every drop-able type +//! one. Thus, eager strategy will instantiate drop-glue for every drop-able type //! in the crate, even if no drop call for that type exists (yet). It will //! also instantiate default implementations of trait methods, something that //! otherwise is only done on demand. //! +//! Collection-time const evaluation and "mentioned" items +//! ------------------------------------------------------ +//! +//! One important role of collection is to evaluate all constants that are used by all the items +//! which are being collected. Codegen can then rely on only encountering constants that evaluate +//! successfully, and if a constant fails to evaluate, the collector has much better context to be +//! able to show where this constant comes up. +//! +//! However, the exact set of "used" items (collected as described above), and therefore the exact +//! set of used constants, can depend on optimizations. Optimizing away dead code may optimize away +//! a function call that uses a failing constant, so an unoptimized build may fail where an +//! optimized build succeeds. This is undesirable. +//! +//! To avoid this, the collector has the concept of "mentioned" items. Some time during the MIR +//! pipeline, before any optimization-level-dependent optimizations, we compute a list of all items +//! that syntactically appear in the code. These are considered "mentioned", and even if they are in +//! dead code and get optimized away (which makes them no longer "used"), they are still +//! "mentioned". For every used item, the collector ensures that all mentioned items, recursively, +//! do not use a failing constant. This is reflected via the [`CollectionMode`], which determines +//! whether we are visiting a used item or merely a mentioned item. //! //! Open Issues //! ----------- @@ -165,15 +185,16 @@ //! regardless of whether it is actually needed or not. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::{par_for_each_in, MTLock, MTLockRef}; +use rustc_data_structures::sync::{par_for_each_in, LRef, MTLock}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::lang_items::LangItem; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::mir::visit::Visitor as MirVisitor; -use rustc_middle::mir::{self, Location}; +use rustc_middle::mir::{self, Location, MentionedItem}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; @@ -183,7 +204,6 @@ use rustc_middle::ty::{ TypeVisitableExt, VtblEntry, }; use rustc_middle::ty::{GenericArgKind, GenericArgs}; -use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext}; use rustc_session::config::EntryFnType; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; use rustc_session::Limit; @@ -199,7 +219,7 @@ use crate::errors::{ }; #[derive(PartialEq)] -pub enum MonoItemCollectionMode { +pub enum MonoItemCollectionStrategy { Eager, Lazy, } @@ -214,6 +234,35 @@ pub struct UsageMap<'tcx> { type MonoItems<'tcx> = Vec>>; +/// The state that is shared across the concurrent threads that are doing collection. +struct SharedState<'tcx> { + /// Items that have been or are currently being recursively collected. + visited: MTLock>>, + /// Items that have been or are currently being recursively treated as "mentioned", i.e., their + /// consts are evaluated but nothing is added to the collection. + mentioned: MTLock>>, + /// Which items are being used where, for better errors. + usage_map: MTLock>, +} + +/// See module-level docs on some contect for "mentioned" items. +#[derive(Copy, Clone, Debug, PartialEq)] +enum CollectionMode { + /// Collect items that are used, i.e., actually needed for codegen. + /// + /// Which items are used can depend on optimization levels, as MIR optimizations can remove + /// uses. + UsedItems, + /// Collect items that are mentioned. The goal of this mode is that it is independent of + /// optimizations: the set of "mentioned" items is computed before optimizations are run. + /// + /// The exact contents of this set are *not* a stable guarantee. (For instance, it is currently + /// computed after drop-elaboration. If we ever do some optimizations even in debug builds, we + /// might decide to run them before computing mentioned items.) The key property of this set is + /// that it is optimization-independent. + MentionedItems, +} + impl<'tcx> UsageMap<'tcx> { fn new() -> UsageMap<'tcx> { UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() } @@ -253,25 +302,28 @@ impl<'tcx> UsageMap<'tcx> { } } -#[instrument(skip(tcx, mode), level = "debug")] +#[instrument(skip(tcx, strategy), level = "debug")] pub fn collect_crate_mono_items( tcx: TyCtxt<'_>, - mode: MonoItemCollectionMode, + strategy: MonoItemCollectionStrategy, ) -> (FxHashSet>, UsageMap<'_>) { let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); - let roots = - tcx.sess.time("monomorphization_collector_root_collections", || collect_roots(tcx, mode)); + let roots = tcx + .sess + .time("monomorphization_collector_root_collections", || collect_roots(tcx, strategy)); debug!("building mono item graph, beginning at roots"); - let mut visited = MTLock::new(FxHashSet::default()); - let mut usage_map = MTLock::new(UsageMap::new()); + let mut state = SharedState { + visited: MTLock::new(FxHashSet::default()), + mentioned: MTLock::new(FxHashSet::default()), + usage_map: MTLock::new(UsageMap::new()), + }; let recursion_limit = tcx.recursion_limit(); { - let visited: MTLockRef<'_, _> = &mut visited; - let usage_map: MTLockRef<'_, _> = &mut usage_map; + let state: LRef<'_, _> = &mut state; tcx.sess.time("monomorphization_collector_graph_walk", || { par_for_each_in(roots, |root| { @@ -279,22 +331,22 @@ pub fn collect_crate_mono_items( collect_items_rec( tcx, dummy_spanned(root), - visited, + state, &mut recursion_depths, recursion_limit, - usage_map, + CollectionMode::UsedItems, ); }); }); } - (visited.into_inner(), usage_map.into_inner()) + (state.visited.into_inner(), state.usage_map.into_inner()) } // 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: MonoItemCollectionMode) -> Vec> { +fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec> { debug!("collecting roots"); let mut roots = Vec::new(); @@ -303,7 +355,7 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec, mode: MonoItemCollectionMode) -> Vec( tcx: TyCtxt<'tcx>, starting_item: Spanned>, - visited: MTLockRef<'_, FxHashSet>>, + state: LRef<'_, SharedState<'tcx>>, recursion_depths: &mut DefIdMap, recursion_limit: Limit, - usage_map: MTLockRef<'_, UsageMap<'tcx>>, + mode: CollectionMode, ) { - if !visited.lock_mut().insert(starting_item.node) { - // We've been here already, no need to search again. - return; + if mode == CollectionMode::UsedItems { + if !state.visited.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + } else { + if state.visited.lock().contains(&starting_item.node) { + // We've already done a *full* visit on this one, no need to do the "mention" visit. + return; + } + if !state.mentioned.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + // There's some risk that we first do a 'mention' visit and then a full visit. But there's no + // harm in that, the mention visit will trigger all the queries and the results are cached. } - let mut used_items = Vec::new(); + let mut used_items = MonoItems::new(); + let mut mentioned_items = MonoItems::new(); let recursion_depth_reset; // Post-monomorphization errors MVP @@ -373,37 +442,48 @@ fn collect_items_rec<'tcx>( // FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do. let error_count = tcx.dcx().err_count(); + // In `mentioned_items` we collect items that were mentioned in this MIR but possibly do not + // need to be monomorphized. This is done to ensure that optimizing away function calls does not + // hide const-eval errors that those calls would otherwise have triggered. match starting_item.node { MonoItem::Static(def_id) => { - let instance = Instance::mono(tcx, def_id); - - // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, &instance)); - - let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; - // Nested statics have no type. - if !nested { - let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); - } - recursion_depth_reset = None; - if let Ok(alloc) = tcx.eval_static_initializer(def_id) { - for &prov in alloc.inner().provenance().ptrs().values() { - collect_alloc(tcx, prov.alloc_id(), &mut used_items); + // Statics always get evaluted (which is possible because they can't be generic), so for + // `MentionedItems` collection there's nothing to do here. + if mode == CollectionMode::UsedItems { + let instance = Instance::mono(tcx, def_id); + + // Sanity check whether this ended up being collected accidentally + debug_assert!(should_codegen_locally(tcx, &instance)); + + let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; + // Nested statics have no type. + if !nested { + let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); + } + + if let Ok(alloc) = tcx.eval_static_initializer(def_id) { + for &prov in alloc.inner().provenance().ptrs().values() { + collect_alloc(tcx, prov.alloc_id(), &mut used_items); + } + } + + if tcx.needs_thread_local_shim(def_id) { + used_items.push(respan( + starting_item.span, + MonoItem::Fn(Instance { + def: InstanceDef::ThreadLocalShim(def_id), + args: GenericArgs::empty(), + }), + )); } } - if tcx.needs_thread_local_shim(def_id) { - used_items.push(respan( - starting_item.span, - MonoItem::Fn(Instance { - def: InstanceDef::ThreadLocalShim(def_id), - args: GenericArgs::empty(), - }), - )); - } + // mentioned_items stays empty since there's no codegen for statics. statics don't get + // optimized, and if they did then the const-eval interpreter would have to worry about + // mentioned_items. } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally @@ -420,10 +500,20 @@ fn collect_items_rec<'tcx>( check_type_length_limit(tcx, instance); rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_used_items(tcx, instance, &mut used_items); + collect_items_of_instance( + tcx, + instance, + &mut used_items, + &mut mentioned_items, + mode, + ) }); } MonoItem::GlobalAsm(item_id) => { + assert!( + mode == CollectionMode::UsedItems, + "should never encounter global_asm when collecting mentioned items" + ); recursion_depth_reset = None; let item = tcx.hir().item(item_id); @@ -459,8 +549,10 @@ fn collect_items_rec<'tcx>( } else { span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") } + + // mention_items stays empty as nothing gets optimized here. } - } + }; // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the // mono item graph. @@ -474,10 +566,41 @@ fn collect_items_rec<'tcx>( formatted_item, }); } - usage_map.lock_mut().record_used(starting_item.node, &used_items); + // Only updating `usage_map` for used items as otherwise we may be inserting the same item + // multiple times (if it is first 'mentioned' and then later actuall used), and the usage map + // logic does not like that. + // This is part of the output of collection and hence only relevant for "used" items. + // ("Mentioned" items are only considered internally during collection.) + if mode == CollectionMode::UsedItems { + state.usage_map.lock_mut().record_used(starting_item.node, &used_items); + } - for used_item in used_items { - collect_items_rec(tcx, used_item, visited, recursion_depths, recursion_limit, usage_map); + if mode == CollectionMode::MentionedItems { + assert!(used_items.is_empty(), "'mentioned' collection should never encounter used items"); + } else { + for used_item in used_items { + collect_items_rec( + tcx, + used_item, + state, + recursion_depths, + recursion_limit, + CollectionMode::UsedItems, + ); + } + } + + // Walk over mentioned items *after* used items, so that if an item is both mentioned and used then + // the loop above has fully collected it, so this loop will skip it. + for mentioned_item in mentioned_items { + collect_items_rec( + tcx, + mentioned_item, + state, + recursion_depths, + recursion_limit, + CollectionMode::MentionedItems, + ); } if let Some((def_id, depth)) = recursion_depth_reset { @@ -596,7 +719,7 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { struct MirUsedCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, - output: &'a mut MonoItems<'tcx>, + used_items: &'a mut MonoItems<'tcx>, instance: Instance<'tcx>, /// Spans for move size lints already emitted. Helps avoid duplicate lints. move_size_spans: Vec, @@ -735,6 +858,31 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { ); self.move_size_spans.push(span); } + + /// Evaluates a *not yet monomorphized* constant. + fn eval_constant( + &mut self, + constant: &mir::ConstOperand<'tcx>, + ) -> Option> { + let const_ = self.monomorphize(constant.const_); + let param_env = ty::ParamEnv::reveal_all(); + // Evaluate the constant. This makes const eval failure a collection-time error (rather than + // a codegen-time error). rustc stops after collection if there was an error, so this + // ensures codegen never has to worry about failing consts. + // (codegen relies on this and ICEs will happen if this is violated.) + match const_.eval(self.tcx, param_env, constant.span) { + Ok(v) => Some(v), + Err(ErrorHandled::TooGeneric(..)) => span_bug!( + constant.span, + "collection encountered polymorphic constant: {:?}", + const_ + ), + Err(err @ ErrorHandled::Reported(..)) => { + err.emit_note(self.tcx); + return None; + } + } + } } impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { @@ -769,7 +917,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { target_ty, source_ty, span, - self.output, + self.used_items, ); } } @@ -780,7 +928,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { ) => { let fn_ty = operand.ty(self.body, self.tcx); let fn_ty = self.monomorphize(fn_ty); - visit_fn_use(self.tcx, fn_ty, false, span, self.output); + visit_fn_use(self.tcx, fn_ty, false, span, self.used_items); } mir::Rvalue::Cast( mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), @@ -798,7 +946,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { ty::ClosureKind::FnOnce, ); if should_codegen_locally(self.tcx, &instance) { - self.output.push(create_fn_mono_item(self.tcx, instance, span)); + self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); } } _ => bug!(), @@ -809,7 +957,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let instance = Instance::mono(self.tcx, def_id); if should_codegen_locally(self.tcx, &instance) { trace!("collecting thread-local static {:?}", def_id); - self.output.push(respan(span, MonoItem::Static(def_id))); + self.used_items.push(respan(span, MonoItem::Static(def_id))); } } _ => { /* not interesting */ } @@ -822,26 +970,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { /// to ensure that the constant evaluates successfully and walk the result. #[instrument(skip(self), level = "debug")] fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) { - let const_ = self.monomorphize(constant.const_); - let param_env = ty::ParamEnv::reveal_all(); - // Evaluate the constant. This makes const eval failure a collection-time error (rather than - // a codegen-time error). rustc stops after collection if there was an error, so this - // ensures codegen never has to worry about failing consts. - // (codegen relies on this and ICEs will happen if this is violated.) - let val = match const_.eval(self.tcx, param_env, constant.span) { - Ok(v) => v, - Err(ErrorHandled::TooGeneric(..)) => span_bug!( - self.body.source_info(location).span, - "collection encountered polymorphic constant: {:?}", - const_ - ), - Err(err @ ErrorHandled::Reported(..)) => { - err.emit_note(self.tcx); - return; - } - }; - collect_const_value(self.tcx, val, self.output); - MirVisitor::visit_ty(self, const_.ty(), TyContext::Location(location)); + // No `super_constant` as we don't care about `visit_ty`/`visit_ty_const`. + let Some(val) = self.eval_constant(constant) else { return }; + collect_const_value(self.tcx, val, self.used_items); } fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { @@ -852,7 +983,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let push_mono_lang_item = |this: &mut Self, lang_item: LangItem| { let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source))); if should_codegen_locally(tcx, &instance) { - this.output.push(create_fn_mono_item(tcx, instance, source)); + this.used_items.push(create_fn_mono_item(tcx, instance, source)); } }; @@ -861,25 +992,25 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let callee_ty = func.ty(self.body, tcx); let callee_ty = self.monomorphize(callee_ty); self.check_fn_args_move_size(callee_ty, args, *fn_span, location); - visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output) + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) } mir::TerminatorKind::Drop { ref place, .. } => { let ty = place.ty(self.body, self.tcx).ty; let ty = self.monomorphize(ty); - visit_drop_use(self.tcx, ty, true, source, self.output); + visit_drop_use(self.tcx, ty, true, source, self.used_items); } mir::TerminatorKind::InlineAsm { ref operands, .. } => { for op in operands { match *op { mir::InlineAsmOperand::SymFn { ref value } => { let fn_ty = self.monomorphize(value.const_.ty()); - visit_fn_use(self.tcx, fn_ty, false, source, self.output); + visit_fn_use(self.tcx, fn_ty, false, source, self.used_items); } mir::InlineAsmOperand::SymStatic { def_id } => { let instance = Instance::mono(self.tcx, def_id); if should_codegen_locally(self.tcx, &instance) { trace!("collecting asm sym static {:?}", def_id); - self.output.push(respan(source, MonoItem::Static(def_id))); + self.used_items.push(respan(source, MonoItem::Static(def_id))); } } _ => {} @@ -1239,7 +1370,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( struct RootCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, - mode: MonoItemCollectionMode, + strategy: MonoItemCollectionStrategy, output: &'a mut MonoItems<'tcx>, entry_fn: Option<(DefId, EntryFnType)>, } @@ -1248,7 +1379,7 @@ impl<'v> RootCollector<'_, 'v> { fn process_item(&mut self, id: hir::ItemId) { match self.tcx.def_kind(id.owner_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { - if self.mode == MonoItemCollectionMode::Eager + if self.strategy == MonoItemCollectionStrategy::Eager && self.tcx.generics_of(id.owner_id).count() == 0 { debug!("RootCollector: ADT drop-glue for `{id:?}`",); @@ -1279,7 +1410,7 @@ impl<'v> RootCollector<'_, 'v> { } } DefKind::Impl { .. } => { - if self.mode == MonoItemCollectionMode::Eager { + if self.strategy == MonoItemCollectionStrategy::Eager { create_mono_items_for_default_impls(self.tcx, id, self.output); } } @@ -1298,9 +1429,9 @@ impl<'v> RootCollector<'_, 'v> { fn is_root(&self, def_id: LocalDefId) -> bool { !self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) - && match self.mode { - MonoItemCollectionMode::Eager => true, - MonoItemCollectionMode::Lazy => { + && 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 @@ -1497,26 +1628,67 @@ fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec { } /// Scans the MIR in order to find function calls, closures, and drop-glue. -#[instrument(skip(tcx, output), level = "debug")] -fn collect_used_items<'tcx>( +/// +/// Anything that's found is added to `output`. Furthermore the "mentioned items" of the MIR are returned. +#[instrument(skip(tcx, used_items, mentioned_items), level = "debug")] +fn collect_items_of_instance<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, - output: &mut MonoItems<'tcx>, + used_items: &mut MonoItems<'tcx>, + mentioned_items: &mut MonoItems<'tcx>, + mode: CollectionMode, ) { let body = tcx.instance_mir(instance.def); - - // Here we rely on the visitor also visiting `required_consts`, so that we evaluate them - // and abort compilation if any of them errors. - MirUsedCollector { + let mut collector = MirUsedCollector { tcx, - body: body, - output, + body, + used_items, instance, move_size_spans: vec![], visiting_call_terminator: false, skip_move_check_fns: None, + }; + + if mode == CollectionMode::UsedItems { + // Visit everything. Here we rely on the visitor also visiting `required_consts`, so that we + // evaluate them and abort compilation if any of them errors. + collector.visit_body(body); + } else { + // We only need to evaluate all constants, but can ignore the rest of the MIR. + for const_op in &body.required_consts { + if let Some(val) = collector.eval_constant(const_op) { + collect_const_value(tcx, val, mentioned_items); + } + } + } + + // Always gather mentioned items. + for item in &body.mentioned_items { + let item_mono = collector.monomorphize(item.node); + visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); + } +} + +/// `item` must be already monomorphized +fn visit_mentioned_item<'tcx>( + tcx: TyCtxt<'tcx>, + item: &MentionedItem<'tcx>, + span: Span, + output: &mut MonoItems<'tcx>, +) { + match *item { + MentionedItem::Fn(def_id, args) => { + let instance = Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); + // `visit_instance_use` was written for "used" item collection but works just as well + // for "mentioned" item collection. + // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway + // can't have their own failing constants. + visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); + } + MentionedItem::Drop(ty) => { + visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output); + } } - .visit_body(body); } #[instrument(skip(tcx, output), level = "debug")] diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 15041b9cd41..5a92657cb40 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -117,7 +117,7 @@ use rustc_session::CodegenUnits; use rustc_span::symbol::Symbol; use crate::collector::UsageMap; -use crate::collector::{self, MonoItemCollectionMode}; +use crate::collector::{self, MonoItemCollectionStrategy}; use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode}; struct PartitioningCx<'a, 'tcx> { @@ -1087,30 +1087,30 @@ where } fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) { - let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items { + let collection_strategy = match tcx.sess.opts.unstable_opts.print_mono_items { Some(ref s) => { let mode = s.to_lowercase(); let mode = mode.trim(); if mode == "eager" { - MonoItemCollectionMode::Eager + MonoItemCollectionStrategy::Eager } else { if mode != "lazy" { tcx.dcx().emit_warn(UnknownCguCollectionMode { mode }); } - MonoItemCollectionMode::Lazy + MonoItemCollectionStrategy::Lazy } } None => { if tcx.sess.link_dead_code() { - MonoItemCollectionMode::Eager + MonoItemCollectionStrategy::Eager } else { - MonoItemCollectionMode::Lazy + MonoItemCollectionStrategy::Lazy } } }; - let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_mode); + let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy); // If there was an error during collection (e.g. from one of the constants we evaluated), // then we stop here. This way codegen does not have to worry about failing constants. diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr index 0bf231d09f1..73790f7517d 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-drop.rs:12:19 + --> $DIR/collect-in-dead-drop.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:12:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.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: erroneous constant encountered - --> $DIR/collect-in-dead-drop.rs:19:17 + --> $DIR/collect-in-dead-drop.rs:16:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr new file mode 100644 index 00000000000..73790f7517d --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-drop.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.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: erroneous constant encountered + --> $DIR/collect-in-dead-drop.rs:16:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +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/collect-in-dead-drop.rs b/tests/ui/consts/required-consts/collect-in-dead-drop.rs index c9ffcec6903..98e9d191895 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.rs @@ -1,15 +1,12 @@ //@revisions: noopt opt -//@[noopt] build-fail +//@ 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 + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } // This function is not actually called, but is mentioned implicitly as destructor in dead code in a diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr index 8bb99efe8e4..52462076ff9 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn.rs:12:19 + --> $DIR/collect-in-dead-fn.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:12:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-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: erroneous constant encountered - --> $DIR/collect-in-dead-fn.rs:22:17 + --> $DIR/collect-in-dead-fn.rs:19:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-fn.rs:29:9 + --> $DIR/collect-in-dead-fn.rs:26:9 | LL | not_called::(); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr new file mode 100644 index 00000000000..30c5c2d5c5c --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-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: erroneous constant encountered + --> $DIR/collect-in-dead-fn.rs:19:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-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/collect-in-dead-fn.rs b/tests/ui/consts/required-consts/collect-in-dead-fn.rs index 9e6b1519153..754fcf7b987 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.rs @@ -1,15 +1,12 @@ //@revisions: noopt opt -//@[noopt] build-fail +//@ 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 + 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 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr new file mode 100644 index 00000000000..7303b8d608a --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Late::::FAIL` failed + --> $DIR/collect-in-dead-fnptr-in-const.rs:8:22 + | +LL | const FAIL: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr-in-const.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) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fnptr-in-const.rs:9:28 + | +LL | const FNPTR: fn() = || Self::FAIL; + | ^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn Late::::FNPTR::{closure#0}` + --> $SRC_DIR/core/src/ops/function.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/collect-in-dead-fnptr-in-const.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr new file mode 100644 index 00000000000..7303b8d608a --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Late::::FAIL` failed + --> $DIR/collect-in-dead-fnptr-in-const.rs:8:22 + | +LL | const FAIL: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr-in-const.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) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fnptr-in-const.rs:9:28 + | +LL | const FNPTR: fn() = || Self::FAIL; + | ^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn Late::::FNPTR::{closure#0}` + --> $SRC_DIR/core/src/ops/function.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/collect-in-dead-fnptr-in-const.rs b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs new file mode 100644 index 00000000000..ed073e486c8 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs @@ -0,0 +1,33 @@ +//@revisions: noopt opt +//@ build-fail +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Late(T); +impl Late { + const FAIL: () = panic!(); //~ERROR evaluation of `Late::::FAIL` failed + const FNPTR: fn() = || Self::FAIL; +} + +// This function is not actually called, but it is mentioned in dead code in a function that is +// called. The function then mentions a const that evaluates to a fnptr that points to a function +// that used a const that fails to evaluate. +// This tests that when processing mentioned items, we also check the fnptrs in the final values +// of consts that we encounter. +#[inline(never)] +fn not_called() { + if false { + let _ = Late::::FNPTR; + } +} + +#[inline(never)] +fn called() { + if false { + not_called::(); + } +} + +pub fn main() { + called::(); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr new file mode 100644 index 00000000000..bf2f3cf9cc7 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fnptr.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr.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/collect-in-dead-fnptr.rs:17:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-in-dead-fnptr.rs:26:28 + | +LL | let _fnptr: 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/collect-in-dead-fnptr.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr new file mode 100644 index 00000000000..bf2f3cf9cc7 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fnptr.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr.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/collect-in-dead-fnptr.rs:17:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-in-dead-fnptr.rs:26:28 + | +LL | let _fnptr: 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/collect-in-dead-fnptr.rs b/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs new file mode 100644 index 00000000000..061addfe102 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs @@ -0,0 +1,32 @@ +//@revisions: noopt opt +//@ build-fail +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +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 ensures that we consider ReifyFnPointer coercions when gathering "mentioned" items. +#[inline(never)] +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called() { + if false { + // We don't call the function, but turn it to a function pointer. + // Make sure it still gest added to `mentioned_items`. + let _fnptr: fn() = not_called::; + } +} + +pub fn main() { + called::(); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr index 5b1df78b232..2ab1f80e2d3 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-move.rs:12:19 + --> $DIR/collect-in-dead-move.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:12:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.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: erroneous constant encountered - --> $DIR/collect-in-dead-move.rs:19:17 + --> $DIR/collect-in-dead-move.rs:16:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr new file mode 100644 index 00000000000..2ab1f80e2d3 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-move.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.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: erroneous constant encountered + --> $DIR/collect-in-dead-move.rs:16:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +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/collect-in-dead-move.rs b/tests/ui/consts/required-consts/collect-in-dead-move.rs index f3a6ba8a657..2fd6aea58de 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-move.rs @@ -1,15 +1,12 @@ //@revisions: noopt opt -//@[noopt] build-fail +//@ 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 + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } // This function is not actually called, but is mentioned implicitly as destructor in dead code in a From 91b35a1b40a0cc7a7f3d9e3824bd0960c26ad21a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Mar 2024 09:02:18 +0100 Subject: [PATCH 02/10] fix comments in required-consts tests --- .../required-consts/collect-in-dead-drop.noopt.stderr | 6 +++--- .../required-consts/collect-in-dead-drop.opt.stderr | 6 +++--- tests/ui/consts/required-consts/collect-in-dead-drop.rs | 3 +-- .../required-consts/collect-in-dead-fn.noopt.stderr | 8 ++++---- .../consts/required-consts/collect-in-dead-fn.opt.stderr | 8 ++++---- tests/ui/consts/required-consts/collect-in-dead-fn.rs | 3 +-- tests/ui/consts/required-consts/collect-in-dead-forget.rs | 3 +-- .../required-consts/collect-in-dead-move.noopt.stderr | 6 +++--- .../required-consts/collect-in-dead-move.opt.stderr | 6 +++--- tests/ui/consts/required-consts/collect-in-dead-move.rs | 3 +-- .../required-consts/collect-in-dead-vtable.noopt.stderr | 8 ++++---- tests/ui/consts/required-consts/collect-in-dead-vtable.rs | 3 +-- 12 files changed, 29 insertions(+), 34 deletions(-) diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr index 73790f7517d..796df067e0b 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-drop.rs:9:19 + --> $DIR/collect-in-dead-drop.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.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/collect-in-dead-drop.rs:16:17 + --> $DIR/collect-in-dead-drop.rs:15:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr index 73790f7517d..796df067e0b 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-drop.rs:9:19 + --> $DIR/collect-in-dead-drop.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.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/collect-in-dead-drop.rs:16:17 + --> $DIR/collect-in-dead-drop.rs:15:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.rs b/tests/ui/consts/required-consts/collect-in-dead-drop.rs index 98e9d191895..ca140f7e95e 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.rs @@ -1,8 +1,7 @@ //@revisions: noopt 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) +//! This fails without optimizations, so it should also fail with optimizations. struct Fail(T); impl Fail { diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr index 52462076ff9..f0b7984d2fa 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn.rs:9:19 + --> $DIR/collect-in-dead-fn.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-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/collect-in-dead-fn.rs:19:17 + --> $DIR/collect-in-dead-fn.rs:18:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-fn.rs:26:9 + --> $DIR/collect-in-dead-fn.rs:25:9 | LL | not_called::(); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr index 30c5c2d5c5c..25c9bb0f8c6 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn.rs:9:19 + --> $DIR/collect-in-dead-fn.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-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/collect-in-dead-fn.rs:19:17 + --> $DIR/collect-in-dead-fn.rs:18:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-fn.rs:26:9 + --> $DIR/collect-in-dead-fn.rs:25:9 | LL | not_called::(); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.rs b/tests/ui/consts/required-consts/collect-in-dead-fn.rs index 754fcf7b987..f1d8c479a45 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.rs @@ -1,8 +1,7 @@ //@revisions: noopt 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) +//! This fails without optimizations, so it should also fail with optimizations. struct Fail(T); impl Fail { diff --git a/tests/ui/consts/required-consts/collect-in-dead-forget.rs b/tests/ui/consts/required-consts/collect-in-dead-forget.rs index 720b7a499f7..17479ef8b37 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-forget.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-forget.rs @@ -1,8 +1,7 @@ //@revisions: noopt opt //@build-pass //@[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) +//! This passes without optimizations, so it can (and should) also pass with optimizations. struct Fail(T); impl Fail { diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr index 2ab1f80e2d3..915949bcddf 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-move.rs:9:19 + --> $DIR/collect-in-dead-move.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.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/collect-in-dead-move.rs:16:17 + --> $DIR/collect-in-dead-move.rs:15:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr index 2ab1f80e2d3..915949bcddf 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-move.rs:9:19 + --> $DIR/collect-in-dead-move.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.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/collect-in-dead-move.rs:16:17 + --> $DIR/collect-in-dead-move.rs:15:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.rs b/tests/ui/consts/required-consts/collect-in-dead-move.rs index 2fd6aea58de..d78c13b988c 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-move.rs @@ -1,8 +1,7 @@ //@revisions: noopt 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) +//! This fails without optimizations, so it should also fail with optimizations. struct Fail(T); impl Fail { diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr index 56b6989b441..2a835a4237e 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-vtable.rs:12:19 + --> $DIR/collect-in-dead-vtable.rs:11:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:12:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:11: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-in-dead-vtable.rs:26:21 + --> $DIR/collect-in-dead-vtable.rs:25:21 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn as MyTrait>::not_called` - --> $DIR/collect-in-dead-vtable.rs:35:40 + --> $DIR/collect-in-dead-vtable.rs:34:40 | LL | let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here | ^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs index f21a1cc1fc2..3098661fbd9 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs @@ -4,8 +4,7 @@ //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) +//! This fails without optimizations, so it should also fail with optimizations. struct Fail(T); impl Fail { From ee4b7581616962d721bceaab36a43135c82aa158 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Mar 2024 09:37:52 +0100 Subject: [PATCH 03/10] avoid processing mentioned items that are also still used --- compiler/rustc_middle/src/mir/mod.rs | 2 +- compiler/rustc_monomorphize/src/collector.rs | 30 ++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 05e8360b93f..c4e3a12acef 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -314,7 +314,7 @@ 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(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable, TyEncodable, TyDecodable)] #[derive(TypeFoldable, TypeVisitable)] pub enum MentionedItem<'tcx> { Fn(DefId, GenericArgsRef<'tcx>), diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 1eb11cec6c5..84353a7c054 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -720,6 +720,9 @@ struct MirUsedCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, used_items: &'a mut MonoItems<'tcx>, + /// See the comment in `collect_items_of_instance` for the purpose of this set. + /// Note that this contains *not-monomorphized* items! + used_mentioned_items: &'a mut FxHashSet>, instance: Instance<'tcx>, /// Spans for move size lints already emitted. Helps avoid duplicate lints. move_size_spans: Vec, @@ -990,12 +993,18 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { match terminator.kind { mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { let callee_ty = func.ty(self.body, tcx); + // *Before* monomorphizing, record that we already handled this mention. + if let ty::FnDef(def_id, args) = callee_ty.kind() { + self.used_mentioned_items.insert(MentionedItem::Fn(*def_id, args)); + } let callee_ty = self.monomorphize(callee_ty); self.check_fn_args_move_size(callee_ty, args, *fn_span, location); visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) } mir::TerminatorKind::Drop { ref place, .. } => { let ty = place.ty(self.body, self.tcx).ty; + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Drop(ty)); let ty = self.monomorphize(ty); visit_drop_use(self.tcx, ty, true, source, self.used_items); } @@ -1639,10 +1648,22 @@ fn collect_items_of_instance<'tcx>( mode: CollectionMode, ) { let body = tcx.instance_mir(instance.def); + // Naively, in "used" collection mode, all functions get added to *both* `used_items` and + // `mentioned_items`. Mentioned items processing will then notice that they have already been + // visited, but at that point each mentioned item has been monomorphized, added to the + // `mentioned_items` worklist, and checked in the global set of visited items. To removes that + // overhead, we have a special optimization that avoids adding items to `mentioned_items` when + // they are already added in `used_items`. We could just scan `used_items`, but that's a linear + // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the + // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already + // added to `used_items` in a hash set, which can efficiently query in the + // `body.mentioned_items` loop below. + let mut used_mentioned_items = FxHashSet::>::default(); let mut collector = MirUsedCollector { tcx, body, used_items, + used_mentioned_items: &mut used_mentioned_items, instance, move_size_spans: vec![], visiting_call_terminator: false, @@ -1662,10 +1683,13 @@ fn collect_items_of_instance<'tcx>( } } - // Always gather mentioned items. + // Always gather mentioned items. We try to avoid processing items that we have already added to + // `used_items` above. for item in &body.mentioned_items { - let item_mono = collector.monomorphize(item.node); - visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); + if !collector.used_mentioned_items.contains(&item.node) { + let item_mono = collector.monomorphize(item.node); + visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); + } } } From 347ca50bc82734b45ed1834fd3a15ed978aba258 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Mar 2024 13:28:31 +0100 Subject: [PATCH 04/10] mentioned items: also handle vtables --- compiler/rustc_middle/src/mir/mod.rs | 6 +- .../src/mentioned_items.rs | 26 ++++++- compiler/rustc_monomorphize/src/collector.rs | 73 +++++++++++-------- .../collect-in-dead-vtable.noopt.stderr | 10 +-- .../collect-in-dead-vtable.opt.stderr | 23 ++++++ .../required-consts/collect-in-dead-vtable.rs | 12 +-- 6 files changed, 106 insertions(+), 44 deletions(-) create mode 100644 tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c4e3a12acef..f7451e4c916 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -319,7 +319,11 @@ impl<'tcx> CoroutineInfo<'tcx> { pub enum MentionedItem<'tcx> { Fn(DefId, GenericArgsRef<'tcx>), Drop(Ty<'tcx>), - // FIXME: add Vtable { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> }, + /// Unsizing casts might require vtables, so we have to record them. + UnsizeCast { + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>, + }, // FIXME: do we have to add closures? } diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index ed363b4f252..0e0114c9d2c 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, ConstOperand, Location, MentionedItem, MirPass}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::Spanned; @@ -54,4 +54,28 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { _ => {} } } + + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + match *rvalue { + // We need to detect unsizing casts that required vtables. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::Unsize), + ref operand, + target_ty, + ) + | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { + let span = self.body.source_info(location).span; + self.mentioned_items.push(Spanned { + node: MentionedItem::UnsizeCast { + source_ty: operand.ty(self.body, self.tcx), + target_ty, + }, + span, + }); + } + // Function pointer casts are already handled by `visit_constant` above. + _ => {} + } + } } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 84353a7c054..005a2233507 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -736,7 +736,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { where T: TypeFoldable>, { - debug!("monomorphize: self.instance={:?}", self.instance); + trace!("monomorphize: self.instance={:?}", self.instance); self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::ParamEnv::reveal_all(), @@ -1342,35 +1342,37 @@ fn create_mono_items_for_vtable_methods<'tcx>( ) { assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars()); - if let ty::Dynamic(trait_ty, ..) = trait_ty.kind() { - if let Some(principal) = trait_ty.principal() { - let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); - assert!(!poly_trait_ref.has_escaping_bound_vars()); + let ty::Dynamic(trait_ty, ..) = trait_ty.kind() else { + bug!("create_mono_items_for_vtable_methods: {trait_ty:?} not a trait type"); + }; + if let Some(principal) = trait_ty.principal() { + let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); + assert!(!poly_trait_ref.has_escaping_bound_vars()); - // Walk all methods of the trait, including those of its supertraits - let entries = tcx.vtable_entries(poly_trait_ref); - let methods = entries - .iter() - .filter_map(|entry| match entry { - VtblEntry::MetadataDropInPlace - | VtblEntry::MetadataSize - | VtblEntry::MetadataAlign - | VtblEntry::Vacant => None, - VtblEntry::TraitVPtr(_) => { - // all super trait items already covered, so skip them. - None - } - VtblEntry::Method(instance) => { - Some(*instance).filter(|instance| should_codegen_locally(tcx, instance)) - } - }) - .map(|item| create_fn_mono_item(tcx, item, source)); - output.extend(methods); - } - - // Also add the destructor. - visit_drop_use(tcx, impl_ty, false, source, output); + // Walk all methods of the trait, including those of its supertraits + let entries = tcx.vtable_entries(poly_trait_ref); + debug!(?entries); + let methods = entries + .iter() + .filter_map(|entry| match entry { + VtblEntry::MetadataDropInPlace + | VtblEntry::MetadataSize + | VtblEntry::MetadataAlign + | VtblEntry::Vacant => None, + VtblEntry::TraitVPtr(_) => { + // all super trait items already covered, so skip them. + None + } + VtblEntry::Method(instance) => { + Some(*instance).filter(|instance| should_codegen_locally(tcx, instance)) + } + }) + .map(|item| create_fn_mono_item(tcx, item, source)); + output.extend(methods); } + + // Also add the destructor. + visit_drop_use(tcx, impl_ty, false, source, output); } //=----------------------------------------------------------------------------- @@ -1693,7 +1695,8 @@ fn collect_items_of_instance<'tcx>( } } -/// `item` must be already monomorphized +/// `item` must be already monomorphized. +#[instrument(skip(tcx, span, output), level = "debug")] fn visit_mentioned_item<'tcx>( tcx: TyCtxt<'tcx>, item: &MentionedItem<'tcx>, @@ -1712,6 +1715,18 @@ fn visit_mentioned_item<'tcx>( MentionedItem::Drop(ty) => { visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output); } + MentionedItem::UnsizeCast { source_ty, target_ty } => { + let (source_ty, target_ty) = + find_vtable_types_for_unsizing(tcx.at(span), source_ty, target_ty); + // This could also be a different Unsize instruction, like + // from a fixed sized array to a slice. But we are only + // interested in things that produce a vtable. + if (target_ty.is_trait() && !source_ty.is_trait()) + || (target_ty.is_dyn_star() && !source_ty.is_dyn_star()) + { + create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, span, output); + } + } } } diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr index 2a835a4237e..53d8e0cb996 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr @@ -1,21 +1,21 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-vtable.rs:11:19 + --> $DIR/collect-in-dead-vtable.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:11:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.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/collect-in-dead-vtable.rs:25:21 + --> $DIR/collect-in-dead-vtable.rs:21:21 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn as MyTrait>::not_called` - --> $DIR/collect-in-dead-vtable.rs:34:40 + --> $DIR/collect-in-dead-vtable.rs:30:40 | -LL | let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here +LL | let gen_vtable: &dyn MyTrait = &v; // vtable is "mentioned" here | ^^ error: aborting due to 1 previous error diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr new file mode 100644 index 00000000000..53d8e0cb996 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-vtable.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.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/collect-in-dead-vtable.rs:21:21 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn as MyTrait>::not_called` + --> $DIR/collect-in-dead-vtable.rs:30:40 + | +LL | let gen_vtable: &dyn MyTrait = &v; // vtable is "mentioned" 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/collect-in-dead-vtable.rs b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs index 3098661fbd9..39d51801c04 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs @@ -1,14 +1,11 @@ //@revisions: noopt opt -//@[noopt] build-fail +//@ build-fail //@[opt] compile-flags: -O -//FIXME: `opt` revision currently does not stop with an error due to -//. -//@[opt] build-pass //! This fails without optimizations, so it should also fail with optimizations. struct Fail(T); impl Fail { - const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } trait MyTrait { @@ -17,8 +14,7 @@ trait MyTrait { // 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). +// This ensures that we are properly considering vtables when gathering "mentioned" items. impl MyTrait for Vec { fn not_called(&self) { if false { @@ -31,7 +27,7 @@ impl MyTrait for Vec { fn called() { if false { let v: Vec = Vec::new(); - let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here + let gen_vtable: &dyn MyTrait = &v; // vtable is "mentioned" here } } From f1ec494c322a2eabbba0bf93867170f702e7a05a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Mar 2024 13:42:54 +0100 Subject: [PATCH 05/10] mentioned items: also handle closure-to-fn-ptr coercions --- compiler/rustc_middle/src/mir/mod.rs | 3 +- .../src/mentioned_items.rs | 16 ++++++++++ compiler/rustc_monomorphize/src/collector.rs | 6 ++++ .../collect-in-dead-closure.noopt.stderr | 23 +++++++++++++++ .../collect-in-dead-closure.opt.stderr | 23 +++++++++++++++ .../collect-in-dead-closure.rs | 29 +++++++++++++++++++ 6 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-closure.rs diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index f7451e4c916..8e9b095028c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -324,7 +324,8 @@ pub enum MentionedItem<'tcx> { source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, }, - // FIXME: do we have to add closures? + /// A closure that is coerced to a function pointer. + Closure(DefId, GenericArgsRef<'tcx>), } /// The lowered representation of a single function. diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index 0e0114c9d2c..63f898630ab 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -74,6 +74,22 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { span, }); } + // Similarly, record closures that are turned into function pointers. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), + ref operand, + _, + ) => { + let span = self.body.source_info(location).span; + let source_ty = operand.ty(self.body, self.tcx); + match *source_ty.kind() { + ty::Closure(def_id, args) => { + self.mentioned_items + .push(Spanned { node: MentionedItem::Closure(def_id, args), span }); + } + _ => bug!(), + } + } // Function pointer casts are already handled by `visit_constant` above. _ => {} } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 005a2233507..a5ab4ab3d18 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1727,6 +1727,12 @@ fn visit_mentioned_item<'tcx>( create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, span, output); } } + MentionedItem::Closure(def_id, args) => { + let instance = Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(tcx, &instance) { + output.push(create_fn_mono_item(tcx, instance, span)); + } + } } } diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr new file mode 100644 index 00000000000..40cecaab241 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-closure.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.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/collect-in-dead-closure.rs:16:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-in-dead-closure.rs:23:33 + | +LL | let _closure: 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/collect-in-dead-closure.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr new file mode 100644 index 00000000000..d6298132e1b --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-closure.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.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/collect-in-dead-closure.rs:16:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-in-dead-closure.rs:23:33 + | +LL | let _closure: 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/collect-in-dead-closure.rs b/tests/ui/consts/required-consts/collect-in-dead-closure.rs new file mode 100644 index 00000000000..91f52827af4 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.rs @@ -0,0 +1,29 @@ +//@revisions: noopt opt +//@ build-fail +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +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 a closure that is coerced to a +// function pointer in dead code in a function that is called. Make sure we still find this error. +#[inline(never)] +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called() { + if false { + let _closure: fn() = || not_called::(); + } +} + +pub fn main() { + called::(); +} From 0d6a16ac4b0905df17d93ed015ecb0a9a89c3484 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 18 Mar 2024 11:36:53 +0100 Subject: [PATCH 06/10] mentioned_items: record all callee and coerced closure types, whether they are FnDef/Closure or not They may become FnDef during monomorphization! --- compiler/rustc_middle/src/mir/mod.rs | 24 +++-- compiler/rustc_mir_transform/src/inline.rs | 12 +-- .../src/mentioned_items.rs | 71 ++++++++------ compiler/rustc_monomorphize/src/collector.rs | 93 +++++++++++++------ .../collect-in-dead-closure.opt.stderr | 2 +- ...-in-dead-fn-behind-assoc-type.noopt.stderr | 20 ++++ ...ct-in-dead-fn-behind-assoc-type.opt.stderr | 20 ++++ .../collect-in-dead-fn-behind-assoc-type.rs | 45 +++++++++ ...ect-in-dead-fn-behind-generic.noopt.stderr | 20 ++++ ...llect-in-dead-fn-behind-generic.opt.stderr | 20 ++++ .../collect-in-dead-fn-behind-generic.rs | 27 ++++++ ...in-dead-fn-behind-opaque-type.noopt.stderr | 20 ++++ ...t-in-dead-fn-behind-opaque-type.opt.stderr | 20 ++++ .../collect-in-dead-fn-behind-opaque-type.rs | 35 +++++++ .../collect-in-dead-fn.opt.stderr | 2 +- 15 files changed, 355 insertions(+), 76 deletions(-) create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 8e9b095028c..db40cb29082 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -45,6 +45,7 @@ use std::ops::{Index, IndexMut}; use std::{iter, mem}; pub use self::query::*; +use self::visit::TyContext; pub use basic_blocks::BasicBlocks; mod basic_blocks; @@ -317,15 +318,15 @@ impl<'tcx> CoroutineInfo<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable, TyEncodable, TyDecodable)] #[derive(TypeFoldable, TypeVisitable)] pub enum MentionedItem<'tcx> { - Fn(DefId, GenericArgsRef<'tcx>), + /// A function that gets called. We don't necessarily know its precise type yet, since it can be + /// hidden behind a generic. + Fn(Ty<'tcx>), + /// A type that has its drop shim called. Drop(Ty<'tcx>), /// Unsizing casts might require vtables, so we have to record them. - UnsizeCast { - source_ty: Ty<'tcx>, - target_ty: Ty<'tcx>, - }, + UnsizeCast { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> }, /// A closure that is coerced to a function pointer. - Closure(DefId, GenericArgsRef<'tcx>), + Closure(Ty<'tcx>), } /// The lowered representation of a single function. @@ -610,6 +611,17 @@ impl<'tcx> Body<'tcx> { } } + pub fn span_for_ty_context(&self, ty_context: TyContext) -> Span { + match ty_context { + TyContext::UserTy(span) => span, + TyContext::ReturnTy(source_info) + | TyContext::LocalDecl { source_info, .. } + | TyContext::YieldTy(source_info) + | TyContext::ResumeTy(source_info) => source_info.span, + TyContext::Location(loc) => self.source_info(loc).span, + } + } + /// Returns the return type; it always return first element from `local_decls` array. #[inline] pub fn return_ty(&self) -> Ty<'tcx> { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 0f6a7ae982c..4ec76eec3a9 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -723,15 +723,9 @@ impl<'tcx> Inliner<'tcx> { // some extra work here to save the monomorphization collector work later. It helps a lot, // since monomorphization can avoid a lot of work when the "mentioned items" are similar to // the actually used items. By doing this we can entirely avoid visiting the callee! - let callee_item = { - // We need to reconstruct the `required_item` for the callee so that we can find and - // remove it. - let func_ty = func.ty(caller_body, self.tcx); - match func_ty.kind() { - ty::FnDef(def_id, args) => MentionedItem::Fn(*def_id, args), - _ => bug!(), - } - }; + // We need to reconstruct the `required_item` for the callee so that we can find and + // remove it. + let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx)); if let Some(idx) = caller_body.mentioned_items.iter().position(|item| item.node == callee_item) { diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index 63f898630ab..765269a7c80 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, ConstOperand, Location, MentionedItem, MirPass}; -use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt}; +use rustc_middle::mir::{self, Location, MentionedItem, MirPass}; +use rustc_middle::ty::{adjustment::PointerCoercion, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::Spanned; @@ -29,27 +29,36 @@ impl<'tcx> MirPass<'tcx> for MentionedItems { } } +// This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are +// visiting the exact same places but then instead of monomorphizing and creating `MonoItems`, we +// have to remain generic and just recording the relevant information in `mentioned_items`, where it +// will then be monomorphized later during "mentioned items" collection. impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { - fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) { - let const_ = constant.const_; - // This is how function items get referenced: via constants of `FnDef` type. This handles - // both functions that are called and those that are just turned to function pointers. - if let ty::FnDef(def_id, args) = const_.ty().kind() { - debug!("adding to required_items: {def_id:?}"); - self.mentioned_items - .push(Spanned { node: MentionedItem::Fn(*def_id, args), span: constant.span }); - } - } - 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`. + let span = || self.body.source_info(location).span; + match &terminator.kind { + mir::TerminatorKind::Call { func, .. } => { + let callee_ty = func.ty(self.body, self.tcx); + self.mentioned_items + .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() }); + } mir::TerminatorKind::Drop { place, .. } => { let ty = place.ty(self.body, self.tcx).ty; - let span = self.body.source_info(location).span; - self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span }); + self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span: span() }); + } + mir::TerminatorKind::InlineAsm { ref operands, .. } => { + for op in operands { + match *op { + mir::InlineAsmOperand::SymFn { ref value } => { + self.mentioned_items.push(Spanned { + node: MentionedItem::Fn(value.const_.ty()), + span: span(), + }); + } + _ => {} + } + } } _ => {} } @@ -57,6 +66,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { self.super_rvalue(rvalue, location); + let span = || self.body.source_info(location).span; match *rvalue { // We need to detect unsizing casts that required vtables. mir::Rvalue::Cast( @@ -65,13 +75,14 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { target_ty, ) | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { - let span = self.body.source_info(location).span; + // This isn't monomorphized yet so we can't tell what the actual types are -- just + // add everything. self.mentioned_items.push(Spanned { node: MentionedItem::UnsizeCast { source_ty: operand.ty(self.body, self.tcx), target_ty, }, - span, + span: span(), }); } // Similarly, record closures that are turned into function pointers. @@ -80,17 +91,19 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { ref operand, _, ) => { - let span = self.body.source_info(location).span; let source_ty = operand.ty(self.body, self.tcx); - match *source_ty.kind() { - ty::Closure(def_id, args) => { - self.mentioned_items - .push(Spanned { node: MentionedItem::Closure(def_id, args), span }); - } - _ => bug!(), - } + self.mentioned_items + .push(Spanned { node: MentionedItem::Closure(source_ty), span: span() }); + } + // And finally, function pointer reification casts. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), + ref operand, + _, + ) => { + let fn_ty = operand.ty(self.body, self.tcx); + self.mentioned_items.push(Spanned { node: MentionedItem::Fn(fn_ty), span: span() }); } - // Function pointer casts are already handled by `visit_constant` above. _ => {} } } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index a5ab4ab3d18..44950151203 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -173,6 +173,26 @@ //! do not use a failing constant. This is reflected via the [`CollectionMode`], which determines //! whether we are visiting a used item or merely a mentioned item. //! +//! The collector and "mentioned items" gathering (which lives in `rustc_mir_transform::mentioned_items`) +//! need to stay in sync in the following sense: +//! +//! - For every item that the collector gather that could eventually lead to build failure (most +//! likely due to containing a constant that fails to evaluate), a corresponding mentioned item +//! must be added. This should use the exact same strategy as the ecollector to make sure they are +//! in sync. However, while the collector works on monomorphized types, mentioned items are +//! collected on generic MIR -- so any time the collector checks for a particular type (such as +//! `ty::FnDef`), we have to just onconditionally add this as a mentioned item. +//! - In `visit_mentioned_item`, we then do with that mentioned item exactly what the collector +//! would have done during regular MIR visiting. Basically you can think of the collector having +//! two stages, a pre-monomorphization stage and a post-monomorphization stage (usually quite +//! literally separated by a call to `self.monomorphize`); the pre-monomorphizationn stage is +//! duplicated in mentioned items gathering and the post-monomorphization stage is duplicated in +//! `visit_mentioned_item`. +//! - Finally, as a performance optimization, the collector should fill `used_mentioned_item` during +//! its MIR traversal with exactly what mentioned item gathering would have added in the same +//! situation. This detects mentioned items that have *not* been optimized away and hence don't +//! need a dedicated traversal. +//! //! Open Issues //! ----------- //! Some things are not yet fully implemented in the current version of this @@ -904,8 +924,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { target_ty, ) | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { - let target_ty = self.monomorphize(target_ty); let source_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items + .insert(MentionedItem::UnsizeCast { source_ty, target_ty }); + let target_ty = self.monomorphize(target_ty); let source_ty = self.monomorphize(source_ty); let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.tcx.at(span), source_ty, target_ty); @@ -930,6 +953,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { _, ) => { let fn_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(fn_ty)); let fn_ty = self.monomorphize(fn_ty); visit_fn_use(self.tcx, fn_ty, false, span, self.used_items); } @@ -939,20 +964,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { _, ) => { let source_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Closure(source_ty)); let source_ty = self.monomorphize(source_ty); - match *source_ty.kind() { - ty::Closure(def_id, args) => { - let instance = Instance::resolve_closure( - self.tcx, - def_id, - args, - ty::ClosureKind::FnOnce, - ); - if should_codegen_locally(self.tcx, &instance) { - self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); - } + if let ty::Closure(def_id, args) = *source_ty.kind() { + let instance = + Instance::resolve_closure(self.tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(self.tcx, &instance) { + self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); } - _ => bug!(), + } else { + bug!() } } mir::Rvalue::ThreadLocalRef(def_id) => { @@ -994,9 +1016,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { let callee_ty = func.ty(self.body, tcx); // *Before* monomorphizing, record that we already handled this mention. - if let ty::FnDef(def_id, args) = callee_ty.kind() { - self.used_mentioned_items.insert(MentionedItem::Fn(*def_id, args)); - } + self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); let callee_ty = self.monomorphize(callee_ty); self.check_fn_args_move_size(callee_ty, args, *fn_span, location); visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) @@ -1012,7 +1032,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { for op in operands { match *op { mir::InlineAsmOperand::SymFn { ref value } => { - let fn_ty = self.monomorphize(value.const_.ty()); + let fn_ty = value.const_.ty(); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(fn_ty)); + let fn_ty = self.monomorphize(fn_ty); visit_fn_use(self.tcx, fn_ty, false, source, self.used_items); } mir::InlineAsmOperand::SymStatic { def_id } => { @@ -1076,6 +1099,8 @@ fn visit_drop_use<'tcx>( visit_instance_use(tcx, instance, is_direct_call, source, output); } +/// For every call of this function in the visitor, make sure there is a matching call in the +/// `mentioned_items` pass! fn visit_fn_use<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, @@ -1653,13 +1678,13 @@ fn collect_items_of_instance<'tcx>( // Naively, in "used" collection mode, all functions get added to *both* `used_items` and // `mentioned_items`. Mentioned items processing will then notice that they have already been // visited, but at that point each mentioned item has been monomorphized, added to the - // `mentioned_items` worklist, and checked in the global set of visited items. To removes that + // `mentioned_items` worklist, and checked in the global set of visited items. To remove that // overhead, we have a special optimization that avoids adding items to `mentioned_items` when // they are already added in `used_items`. We could just scan `used_items`, but that's a linear // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already // added to `used_items` in a hash set, which can efficiently query in the - // `body.mentioned_items` loop below. + // `body.mentioned_items` loop below without even having to monomorphize the item. let mut used_mentioned_items = FxHashSet::>::default(); let mut collector = MirUsedCollector { tcx, @@ -1704,13 +1729,16 @@ fn visit_mentioned_item<'tcx>( output: &mut MonoItems<'tcx>, ) { match *item { - MentionedItem::Fn(def_id, args) => { - let instance = Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); - // `visit_instance_use` was written for "used" item collection but works just as well - // for "mentioned" item collection. - // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway - // can't have their own failing constants. - visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); + MentionedItem::Fn(ty) => { + if let ty::FnDef(def_id, args) = *ty.kind() { + let instance = + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); + // `visit_instance_use` was written for "used" item collection but works just as well + // for "mentioned" item collection. + // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway + // can't have their own failing constants. + visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); + } } MentionedItem::Drop(ty) => { visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output); @@ -1727,10 +1755,15 @@ fn visit_mentioned_item<'tcx>( create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, span, output); } } - MentionedItem::Closure(def_id, args) => { - let instance = Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); - if should_codegen_locally(tcx, &instance) { - output.push(create_fn_mono_item(tcx, instance, span)); + MentionedItem::Closure(source_ty) => { + if let ty::Closure(def_id, args) = *source_ty.kind() { + let instance = + Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(tcx, &instance) { + output.push(create_fn_mono_item(tcx, instance, span)); + } + } else { + bug!() } } } diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr index d6298132e1b..40cecaab241 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr @@ -16,7 +16,7 @@ note: the above error was encountered while instantiating `fn not_called::` --> $DIR/collect-in-dead-closure.rs:23:33 | LL | let _closure: fn() = || not_called::(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr new file mode 100644 index 00000000000..ec9031440eb --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.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: erroneous constant encountered + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:14:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $SRC_DIR/core/src/ops/function.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/collect-in-dead-fn-behind-assoc-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr new file mode 100644 index 00000000000..ec9031440eb --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.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: erroneous constant encountered + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:14:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $SRC_DIR/core/src/ops/function.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/collect-in-dead-fn-behind-assoc-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs new file mode 100644 index 00000000000..a738f550f16 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs @@ -0,0 +1,45 @@ +#![feature(impl_trait_in_assoc_type)] +//@revisions: noopt opt +//@ build-fail +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed +} + +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +fn callit_not(f: impl Fn()) { + if false { + f(); + } +} + +// Using `Fn` here is important; with `FnOnce` another shim gets involved which somehow makes this +// easier to collect properly. +trait Hideaway { + type T: Fn(); + const C: Self::T; +} +impl Hideaway for () { + type T = impl Fn(); + const C: Self::T = not_called::; +} + +fn reveal() { + if false { + callit_not(T::C); + } +} + +fn main() { + if false { + reveal::<()>() + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr new file mode 100644 index 00000000000..51d8d6f35b4 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-generic.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.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/collect-in-dead-fn-behind-generic.rs:13:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $SRC_DIR/core/src/ops/function.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/collect-in-dead-fn-behind-generic.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr new file mode 100644 index 00000000000..51d8d6f35b4 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-generic.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.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/collect-in-dead-fn-behind-generic.rs:13:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $SRC_DIR/core/src/ops/function.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/collect-in-dead-fn-behind-generic.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs new file mode 100644 index 00000000000..ac07af38c1d --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs @@ -0,0 +1,27 @@ +//@revisions: noopt opt +//@ build-fail +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed +} + +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +fn callit_not(f: impl Fn()) { + if false { + f(); + } +} + +fn main() { + if false { + callit_not(not_called::) + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr new file mode 100644 index 00000000000..fb0be0905c2 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `m::Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | + = 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-in-dead-fn-behind-opaque-type.rs:17:21 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn m::not_called::` + --> $SRC_DIR/core/src/ops/function.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/collect-in-dead-fn-behind-opaque-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr new file mode 100644 index 00000000000..fb0be0905c2 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `m::Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | + = 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-in-dead-fn-behind-opaque-type.rs:17:21 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn m::not_called::` + --> $SRC_DIR/core/src/ops/function.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/collect-in-dead-fn-behind-opaque-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs new file mode 100644 index 00000000000..245400f16a5 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs @@ -0,0 +1,35 @@ +//@revisions: noopt opt +//@ build-fail +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. +#![feature(type_alias_impl_trait)] + +mod m { + struct Fail(T); + impl Fail { + const C: () = panic!(); //~ERROR evaluation of `m::Fail::::C` failed + } + + pub type NotCalledFn = impl Fn(); + + fn not_called() { + if false { + let _ = Fail::::C; + } + } + + fn mk_not_called() -> NotCalledFn { + not_called:: + } +} + +fn main() { + // This does not involve a constant of `FnDef` type, it generates the value via unsafe + // shenanigans instead. This ensures that we check all `FnDef` types that occur in a function, + // not just those of constants. Furthermore the `FnDef` is behind an opaque type which bust be + // normalized away to reveal the function type. + if false { + let x: m::NotCalledFn = unsafe { std::mem::transmute(()) }; + x(); + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr index 25c9bb0f8c6..f0b7984d2fa 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr @@ -16,7 +16,7 @@ note: the above error was encountered while instantiating `fn not_called::` --> $DIR/collect-in-dead-fn.rs:25:9 | LL | not_called::(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error From 682991d2c748f6e60529603a8efadc3810bf9c6a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 18 Mar 2024 17:54:48 +0100 Subject: [PATCH 07/10] explicitly set opt-level=0 --- .../required-consts/collect-in-called-fn.noopt.stderr | 8 ++++---- .../required-consts/collect-in-called-fn.opt.stderr | 8 ++++---- tests/ui/consts/required-consts/collect-in-called-fn.rs | 1 + .../required-consts/collect-in-dead-closure.noopt.stderr | 8 ++++---- .../required-consts/collect-in-dead-closure.opt.stderr | 8 ++++---- .../ui/consts/required-consts/collect-in-dead-closure.rs | 1 + .../required-consts/collect-in-dead-drop.noopt.stderr | 6 +++--- .../required-consts/collect-in-dead-drop.opt.stderr | 6 +++--- tests/ui/consts/required-consts/collect-in-dead-drop.rs | 1 + .../collect-in-dead-fn-behind-assoc-type.noopt.stderr | 6 +++--- .../collect-in-dead-fn-behind-assoc-type.opt.stderr | 6 +++--- .../collect-in-dead-fn-behind-assoc-type.rs | 1 + .../collect-in-dead-fn-behind-generic.noopt.stderr | 6 +++--- .../collect-in-dead-fn-behind-generic.opt.stderr | 6 +++--- .../required-consts/collect-in-dead-fn-behind-generic.rs | 1 + .../collect-in-dead-fn-behind-opaque-type.noopt.stderr | 6 +++--- .../collect-in-dead-fn-behind-opaque-type.opt.stderr | 6 +++--- .../collect-in-dead-fn-behind-opaque-type.rs | 1 + .../required-consts/collect-in-dead-fn.noopt.stderr | 8 ++++---- .../consts/required-consts/collect-in-dead-fn.opt.stderr | 8 ++++---- tests/ui/consts/required-consts/collect-in-dead-fn.rs | 1 + .../collect-in-dead-fnptr-in-const.noopt.stderr | 6 +++--- .../collect-in-dead-fnptr-in-const.opt.stderr | 6 +++--- .../required-consts/collect-in-dead-fnptr-in-const.rs | 1 + .../required-consts/collect-in-dead-fnptr.noopt.stderr | 8 ++++---- .../required-consts/collect-in-dead-fnptr.opt.stderr | 8 ++++---- tests/ui/consts/required-consts/collect-in-dead-fnptr.rs | 1 + tests/ui/consts/required-consts/collect-in-dead-forget.rs | 1 + .../required-consts/collect-in-dead-move.noopt.stderr | 6 +++--- .../required-consts/collect-in-dead-move.opt.stderr | 6 +++--- tests/ui/consts/required-consts/collect-in-dead-move.rs | 1 + .../required-consts/collect-in-dead-vtable.noopt.stderr | 8 ++++---- .../required-consts/collect-in-dead-vtable.opt.stderr | 8 ++++---- tests/ui/consts/required-consts/collect-in-dead-vtable.rs | 1 + .../interpret-in-const-called-fn.noopt.stderr | 6 +++--- .../interpret-in-const-called-fn.opt.stderr | 6 +++--- .../required-consts/interpret-in-const-called-fn.rs | 1 + .../required-consts/interpret-in-promoted.noopt.stderr | 6 +++--- .../required-consts/interpret-in-promoted.opt.stderr | 6 +++--- tests/ui/consts/required-consts/interpret-in-promoted.rs | 1 + .../required-consts/interpret-in-static.noopt.stderr | 6 +++--- .../consts/required-consts/interpret-in-static.opt.stderr | 6 +++--- tests/ui/consts/required-consts/interpret-in-static.rs | 1 + 43 files changed, 109 insertions(+), 94 deletions(-) diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr index 14a4cb0217f..c3b641a899a 100644 --- a/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-called-fn.rs:9:19 + --> $DIR/collect-in-called-fn.rs:10:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:10: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-in-called-fn.rs:18:17 + --> $DIR/collect-in-called-fn.rs:19:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn called::` - --> $DIR/collect-in-called-fn.rs:23:5 + --> $DIR/collect-in-called-fn.rs:24:5 | LL | called::(); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr index 14a4cb0217f..c3b641a899a 100644 --- a/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-called-fn.rs:9:19 + --> $DIR/collect-in-called-fn.rs:10:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:10: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-in-called-fn.rs:18:17 + --> $DIR/collect-in-called-fn.rs:19:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn called::` - --> $DIR/collect-in-called-fn.rs:23:5 + --> $DIR/collect-in-called-fn.rs:24:5 | LL | called::(); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.rs b/tests/ui/consts/required-consts/collect-in-called-fn.rs index 55133a10cd9..93947950af2 100644 --- a/tests/ui/consts/required-consts/collect-in-called-fn.rs +++ b/tests/ui/consts/required-consts/collect-in-called-fn.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[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) diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr index 40cecaab241..75c3575a110 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-closure.rs:8:19 + --> $DIR/collect-in-dead-closure.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.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: erroneous constant encountered - --> $DIR/collect-in-dead-closure.rs:16:17 + --> $DIR/collect-in-dead-closure.rs:17:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-closure.rs:23:33 + --> $DIR/collect-in-dead-closure.rs:24:33 | LL | let _closure: fn() = || not_called::(); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr index 40cecaab241..75c3575a110 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-closure.rs:8:19 + --> $DIR/collect-in-dead-closure.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.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: erroneous constant encountered - --> $DIR/collect-in-dead-closure.rs:16:17 + --> $DIR/collect-in-dead-closure.rs:17:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-closure.rs:23:33 + --> $DIR/collect-in-dead-closure.rs:24:33 | LL | let _closure: fn() = || not_called::(); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.rs b/tests/ui/consts/required-consts/collect-in-dead-closure.rs index 91f52827af4..a00214c62db 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-closure.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr index 796df067e0b..73790f7517d 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-drop.rs:8:19 + --> $DIR/collect-in-dead-drop.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.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: erroneous constant encountered - --> $DIR/collect-in-dead-drop.rs:15:17 + --> $DIR/collect-in-dead-drop.rs:16:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr index 796df067e0b..73790f7517d 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-drop.rs:8:19 + --> $DIR/collect-in-dead-drop.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.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: erroneous constant encountered - --> $DIR/collect-in-dead-drop.rs:15:17 + --> $DIR/collect-in-dead-drop.rs:16:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.rs b/tests/ui/consts/required-consts/collect-in-dead-drop.rs index ca140f7e95e..389fcf5dfc9 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr index ec9031440eb..38521061535 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19 + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:10:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:10: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-in-dead-fn-behind-assoc-type.rs:14:17 + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:15:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr index ec9031440eb..38521061535 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19 + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:10:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:10: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-in-dead-fn-behind-assoc-type.rs:14:17 + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:15:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs index a738f550f16..77ebf28407c 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs @@ -1,6 +1,7 @@ #![feature(impl_trait_in_assoc_type)] //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr index 51d8d6f35b4..47515117417 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn-behind-generic.rs:8:19 + --> $DIR/collect-in-dead-fn-behind-generic.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.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: erroneous constant encountered - --> $DIR/collect-in-dead-fn-behind-generic.rs:13:17 + --> $DIR/collect-in-dead-fn-behind-generic.rs:14:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr index 51d8d6f35b4..47515117417 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn-behind-generic.rs:8:19 + --> $DIR/collect-in-dead-fn-behind-generic.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.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: erroneous constant encountered - --> $DIR/collect-in-dead-fn-behind-generic.rs:13:17 + --> $DIR/collect-in-dead-fn-behind-generic.rs:14:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs index ac07af38c1d..de8ccfda97a 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr index fb0be0905c2..d5a68a13e9d 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `m::Fail::::C` failed - --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:11:23 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:11:23 | = 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-in-dead-fn-behind-opaque-type.rs:17:21 + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:18:21 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr index fb0be0905c2..d5a68a13e9d 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `m::Fail::::C` failed - --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:11:23 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:11:23 | = 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-in-dead-fn-behind-opaque-type.rs:17:21 + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:18:21 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs index 245400f16a5..ee8c402c0e3 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. #![feature(type_alias_impl_trait)] diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr index f0b7984d2fa..52462076ff9 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn.rs:8:19 + --> $DIR/collect-in-dead-fn.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-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: erroneous constant encountered - --> $DIR/collect-in-dead-fn.rs:18:17 + --> $DIR/collect-in-dead-fn.rs:19:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-fn.rs:25:9 + --> $DIR/collect-in-dead-fn.rs:26:9 | LL | not_called::(); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr index f0b7984d2fa..52462076ff9 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fn.rs:8:19 + --> $DIR/collect-in-dead-fn.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-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: erroneous constant encountered - --> $DIR/collect-in-dead-fn.rs:18:17 + --> $DIR/collect-in-dead-fn.rs:19:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-fn.rs:25:9 + --> $DIR/collect-in-dead-fn.rs:26:9 | LL | not_called::(); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.rs b/tests/ui/consts/required-consts/collect-in-dead-fn.rs index f1d8c479a45..1c95e0c303f 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr index 7303b8d608a..dea2a342383 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Late::::FAIL` failed - --> $DIR/collect-in-dead-fnptr-in-const.rs:8:22 + --> $DIR/collect-in-dead-fnptr-in-const.rs:9:22 | LL | const FAIL: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr-in-const.rs:8:22 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr-in-const.rs:9: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) note: erroneous constant encountered - --> $DIR/collect-in-dead-fnptr-in-const.rs:9:28 + --> $DIR/collect-in-dead-fnptr-in-const.rs:10:28 | LL | const FNPTR: fn() = || Self::FAIL; | ^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr index 7303b8d608a..dea2a342383 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Late::::FAIL` failed - --> $DIR/collect-in-dead-fnptr-in-const.rs:8:22 + --> $DIR/collect-in-dead-fnptr-in-const.rs:9:22 | LL | const FAIL: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr-in-const.rs:8:22 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr-in-const.rs:9: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) note: erroneous constant encountered - --> $DIR/collect-in-dead-fnptr-in-const.rs:9:28 + --> $DIR/collect-in-dead-fnptr-in-const.rs:10:28 | LL | const FNPTR: fn() = || Self::FAIL; | ^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs index ed073e486c8..8b6344c93f3 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr index bf2f3cf9cc7..51c68782687 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fnptr.rs:8:19 + --> $DIR/collect-in-dead-fnptr.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr.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: erroneous constant encountered - --> $DIR/collect-in-dead-fnptr.rs:17:17 + --> $DIR/collect-in-dead-fnptr.rs:18:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-fnptr.rs:26:28 + --> $DIR/collect-in-dead-fnptr.rs:27:28 | LL | let _fnptr: fn() = not_called::; | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr index bf2f3cf9cc7..51c68782687 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-fnptr.rs:8:19 + --> $DIR/collect-in-dead-fnptr.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr.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: erroneous constant encountered - --> $DIR/collect-in-dead-fnptr.rs:17:17 + --> $DIR/collect-in-dead-fnptr.rs:18:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::` - --> $DIR/collect-in-dead-fnptr.rs:26:28 + --> $DIR/collect-in-dead-fnptr.rs:27:28 | LL | let _fnptr: fn() = not_called::; | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs b/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs index 061addfe102..acbe34829e8 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-forget.rs b/tests/ui/consts/required-consts/collect-in-dead-forget.rs index 17479ef8b37..7586004116c 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-forget.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-forget.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@build-pass +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This passes without optimizations, so it can (and should) also pass with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr index 915949bcddf..2ab1f80e2d3 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-move.rs:8:19 + --> $DIR/collect-in-dead-move.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.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: erroneous constant encountered - --> $DIR/collect-in-dead-move.rs:15:17 + --> $DIR/collect-in-dead-move.rs:16:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr index 915949bcddf..2ab1f80e2d3 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-move.rs:8:19 + --> $DIR/collect-in-dead-move.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.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: erroneous constant encountered - --> $DIR/collect-in-dead-move.rs:15:17 + --> $DIR/collect-in-dead-move.rs:16:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.rs b/tests/ui/consts/required-consts/collect-in-dead-move.rs index d78c13b988c..6a224a375cf 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-move.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr index 53d8e0cb996..b4e18706489 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-vtable.rs:8:19 + --> $DIR/collect-in-dead-vtable.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.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: erroneous constant encountered - --> $DIR/collect-in-dead-vtable.rs:21:21 + --> $DIR/collect-in-dead-vtable.rs:22:21 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn as MyTrait>::not_called` - --> $DIR/collect-in-dead-vtable.rs:30:40 + --> $DIR/collect-in-dead-vtable.rs:31:40 | LL | let gen_vtable: &dyn MyTrait = &v; // vtable is "mentioned" here | ^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr index 53d8e0cb996..b4e18706489 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/collect-in-dead-vtable.rs:8:19 + --> $DIR/collect-in-dead-vtable.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:8:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.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: erroneous constant encountered - --> $DIR/collect-in-dead-vtable.rs:21:21 + --> $DIR/collect-in-dead-vtable.rs:22:21 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn as MyTrait>::not_called` - --> $DIR/collect-in-dead-vtable.rs:30:40 + --> $DIR/collect-in-dead-vtable.rs:31:40 | LL | let gen_vtable: &dyn MyTrait = &v; // vtable is "mentioned" here | ^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs index 39d51801c04..f63207eafec 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! This fails without optimizations, so it should also fail with optimizations. diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr index 75304591b9f..0e3bbbcc2ec 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/interpret-in-const-called-fn.rs:7:19 + --> $DIR/interpret-in-const-called-fn.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:7:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-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/interpret-in-const-called-fn.rs:16:9 + --> $DIR/interpret-in-const-called-fn.rs:17:9 | LL | Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr index 75304591b9f..0e3bbbcc2ec 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/interpret-in-const-called-fn.rs:7:19 + --> $DIR/interpret-in-const-called-fn.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:7:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-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/interpret-in-const-called-fn.rs:16:9 + --> $DIR/interpret-in-const-called-fn.rs:17:9 | LL | Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs index c409fae0bb9..f2e83f56f37 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs @@ -1,4 +1,5 @@ //@revisions: noopt opt +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr index 491131daf8d..6ab991b6471 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr @@ -6,18 +6,18 @@ error[E0080]: evaluation of constant value failed note: inside `unreachable_unchecked` --> $SRC_DIR/core/src/hint.rs:LL:COL note: inside `ub` - --> $DIR/interpret-in-promoted.rs:6:5 + --> $DIR/interpret-in-promoted.rs:7:5 | LL | std::hint::unreachable_unchecked(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: inside `FOO` - --> $DIR/interpret-in-promoted.rs:12:28 + --> $DIR/interpret-in-promoted.rs:13:28 | LL | let _x: &'static () = &ub(); | ^^^^ note: erroneous constant encountered - --> $DIR/interpret-in-promoted.rs:12:27 + --> $DIR/interpret-in-promoted.rs:13:27 | LL | let _x: &'static () = &ub(); | ^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr index 491131daf8d..6ab991b6471 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr @@ -6,18 +6,18 @@ error[E0080]: evaluation of constant value failed note: inside `unreachable_unchecked` --> $SRC_DIR/core/src/hint.rs:LL:COL note: inside `ub` - --> $DIR/interpret-in-promoted.rs:6:5 + --> $DIR/interpret-in-promoted.rs:7:5 | LL | std::hint::unreachable_unchecked(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: inside `FOO` - --> $DIR/interpret-in-promoted.rs:12:28 + --> $DIR/interpret-in-promoted.rs:13:28 | LL | let _x: &'static () = &ub(); | ^^^^ note: erroneous constant encountered - --> $DIR/interpret-in-promoted.rs:12:27 + --> $DIR/interpret-in-promoted.rs:13:27 | LL | let _x: &'static () = &ub(); | ^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.rs b/tests/ui/consts/required-consts/interpret-in-promoted.rs index 9c2cf4e70d3..187494180ad 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.rs +++ b/tests/ui/consts/required-consts/interpret-in-promoted.rs @@ -1,4 +1,5 @@ //@revisions: noopt opt +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. diff --git a/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr index 159c9449fc0..5e8da609e76 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/interpret-in-static.rs:7:19 + --> $DIR/interpret-in-static.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:7:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-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/interpret-in-static.rs:15:9 + --> $DIR/interpret-in-static.rs:16:9 | LL | Fail::::C; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-static.opt.stderr b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr index 159c9449fc0..5e8da609e76 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::::C` failed - --> $DIR/interpret-in-static.rs:7:19 + --> $DIR/interpret-in-static.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:7:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-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/interpret-in-static.rs:15:9 + --> $DIR/interpret-in-static.rs:16:9 | LL | Fail::::C; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-static.rs b/tests/ui/consts/required-consts/interpret-in-static.rs index 559e281b2b0..8bacd030440 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.rs +++ b/tests/ui/consts/required-consts/interpret-in-static.rs @@ -1,4 +1,5 @@ //@revisions: noopt opt +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. From feeffaeff9193b5efb22dd4ff569fbdf206112f2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 20 Mar 2024 11:16:11 +0100 Subject: [PATCH 08/10] mentioned_items: avoid adding str/slice unsizing casts --- .../src/mentioned_items.rs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index 765269a7c80..57b6126dece 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, Location, MentionedItem, MirPass}; -use rustc_middle::ty::{adjustment::PointerCoercion, TyCtxt}; +use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::Spanned; @@ -76,14 +76,21 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { ) | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { // This isn't monomorphized yet so we can't tell what the actual types are -- just - // add everything. - self.mentioned_items.push(Spanned { - node: MentionedItem::UnsizeCast { - source_ty: operand.ty(self.body, self.tcx), - target_ty, - }, - span: span(), - }); + // add everything that may involve a vtable. + let source_ty = operand.ty(self.body, self.tcx); + let may_involve_vtable = match ( + source_ty.builtin_deref(true).map(|t| t.ty.kind()), + target_ty.builtin_deref(true).map(|t| t.ty.kind()), + ) { + (Some(ty::Array(..)), Some(ty::Str | ty::Slice(..))) => false, // &str/&[T] unsizing + _ => true, + }; + if may_involve_vtable { + self.mentioned_items.push(Spanned { + node: MentionedItem::UnsizeCast { source_ty, target_ty }, + span: span(), + }); + } } // Similarly, record closures that are turned into function pointers. mir::Rvalue::Cast( From 0cb1065d7e035f4026d7388a0d072713835b1647 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 20 Mar 2024 11:55:58 +0100 Subject: [PATCH 09/10] collector: move functions around so that the 'root collection' section really only has root collection things under it --- compiler/rustc_monomorphize/src/collector.rs | 525 ++++++++++--------- 1 file changed, 265 insertions(+), 260 deletions(-) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 44950151203..2b89d17a937 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -28,6 +28,7 @@ //! - VTables //! - Object Shims //! +//! The main entry point is `collect_crate_mono_items`, at the bottom of this file. //! //! General Algorithm //! ----------------- @@ -322,85 +323,6 @@ impl<'tcx> UsageMap<'tcx> { } } -#[instrument(skip(tcx, strategy), level = "debug")] -pub fn collect_crate_mono_items( - tcx: TyCtxt<'_>, - strategy: MonoItemCollectionStrategy, -) -> (FxHashSet>, UsageMap<'_>) { - let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); - - let roots = tcx - .sess - .time("monomorphization_collector_root_collections", || collect_roots(tcx, strategy)); - - debug!("building mono item graph, beginning at roots"); - - let mut state = SharedState { - visited: MTLock::new(FxHashSet::default()), - mentioned: MTLock::new(FxHashSet::default()), - usage_map: MTLock::new(UsageMap::new()), - }; - let recursion_limit = tcx.recursion_limit(); - - { - let state: LRef<'_, _> = &mut state; - - tcx.sess.time("monomorphization_collector_graph_walk", || { - par_for_each_in(roots, |root| { - let mut recursion_depths = DefIdMap::default(); - collect_items_rec( - tcx, - dummy_spanned(root), - state, - &mut recursion_depths, - recursion_limit, - CollectionMode::UsedItems, - ); - }); - }); - } - - (state.visited.into_inner(), state.usage_map.into_inner()) -} - -// 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> { - debug!("collecting roots"); - let mut roots = Vec::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 crate_items = tcx.hir_crate_items(()); - - for id in crate_items.items() { - collector.process_item(id); - } - - for id in crate_items.impl_items() { - collector.process_impl_item(id); - } - - collector.push_extra_entry_roots(); - } - - // We can only codegen items that are instantiable - items all of - // whose predicates hold. Luckily, items that aren't instantiable - // can't actually be used, so we can just skip codegenning them. - roots - .into_iter() - .filter_map(|Spanned { node: mono_item, .. }| { - mono_item.is_instantiable(tcx).then_some(mono_item) - }) - .collect() -} - /// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a /// post-monomorphization error is encountered during a collection step. /// @@ -752,7 +674,7 @@ struct MirUsedCollector<'a, 'tcx> { } impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { - pub fn monomorphize(&self, value: T) -> T + fn monomorphize(&self, value: T) -> T where T: TypeFoldable>, { @@ -1400,10 +1322,237 @@ fn create_mono_items_for_vtable_methods<'tcx>( visit_drop_use(tcx, impl_ty, false, source, output); } +/// Scans the CTFE alloc in order to find function pointers and statics that must be monomorphized. +fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoItems<'tcx>) { + match tcx.global_alloc(alloc_id) { + GlobalAlloc::Static(def_id) => { + assert!(!tcx.is_thread_local_static(def_id)); + let instance = Instance::mono(tcx, def_id); + if should_codegen_locally(tcx, &instance) { + trace!("collecting static {:?}", def_id); + output.push(dummy_spanned(MonoItem::Static(def_id))); + } + } + GlobalAlloc::Memory(alloc) => { + trace!("collecting {:?} with {:#?}", alloc_id, alloc); + let ptrs = alloc.inner().provenance().ptrs(); + // avoid `ensure_sufficient_stack` in the common case of "no pointers" + if !ptrs.is_empty() { + rustc_data_structures::stack::ensure_sufficient_stack(move || { + for &prov in ptrs.values() { + collect_alloc(tcx, prov.alloc_id(), output); + } + }); + } + } + GlobalAlloc::Function(fn_instance) => { + if should_codegen_locally(tcx, &fn_instance) { + trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); + output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); + } + } + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + collect_alloc(tcx, alloc_id, output) + } + } +} + +fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { + for impl_def_id in tcx.inherent_impls(def_id).ok()? { + if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + tcx, + fn_ident, + AssocKind::Fn, + def_id, + ) { + return Some(new.def_id); + } + } + return None; +} + +fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec { + let fns = [ + (tcx.lang_items().owned_box(), "new"), + (tcx.get_diagnostic_item(sym::Rc), "new"), + (tcx.get_diagnostic_item(sym::Arc), "new"), + ]; + fns.into_iter() + .filter_map(|(def_id, fn_name)| { + def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) + }) + .collect::>() +} + +/// Scans the MIR in order to find function calls, closures, and drop-glue. +/// +/// Anything that's found is added to `output`. Furthermore the "mentioned items" of the MIR are returned. +#[instrument(skip(tcx, used_items, mentioned_items), level = "debug")] +fn collect_items_of_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + used_items: &mut MonoItems<'tcx>, + mentioned_items: &mut MonoItems<'tcx>, + mode: CollectionMode, +) { + let body = tcx.instance_mir(instance.def); + // Naively, in "used" collection mode, all functions get added to *both* `used_items` and + // `mentioned_items`. Mentioned items processing will then notice that they have already been + // visited, but at that point each mentioned item has been monomorphized, added to the + // `mentioned_items` worklist, and checked in the global set of visited items. To remove that + // overhead, we have a special optimization that avoids adding items to `mentioned_items` when + // they are already added in `used_items`. We could just scan `used_items`, but that's a linear + // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the + // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already + // added to `used_items` in a hash set, which can efficiently query in the + // `body.mentioned_items` loop below without even having to monomorphize the item. + let mut used_mentioned_items = FxHashSet::>::default(); + let mut collector = MirUsedCollector { + tcx, + body, + used_items, + used_mentioned_items: &mut used_mentioned_items, + instance, + move_size_spans: vec![], + visiting_call_terminator: false, + skip_move_check_fns: None, + }; + + if mode == CollectionMode::UsedItems { + // Visit everything. Here we rely on the visitor also visiting `required_consts`, so that we + // evaluate them and abort compilation if any of them errors. + collector.visit_body(body); + } else { + // We only need to evaluate all constants, but can ignore the rest of the MIR. + for const_op in &body.required_consts { + if let Some(val) = collector.eval_constant(const_op) { + collect_const_value(tcx, val, mentioned_items); + } + } + } + + // Always gather mentioned items. We try to avoid processing items that we have already added to + // `used_items` above. + for item in &body.mentioned_items { + if !collector.used_mentioned_items.contains(&item.node) { + let item_mono = collector.monomorphize(item.node); + visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); + } + } +} + +/// `item` must be already monomorphized. +#[instrument(skip(tcx, span, output), level = "debug")] +fn visit_mentioned_item<'tcx>( + tcx: TyCtxt<'tcx>, + item: &MentionedItem<'tcx>, + span: Span, + output: &mut MonoItems<'tcx>, +) { + match *item { + MentionedItem::Fn(ty) => { + if let ty::FnDef(def_id, args) = *ty.kind() { + let instance = + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); + // `visit_instance_use` was written for "used" item collection but works just as well + // for "mentioned" item collection. + // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway + // can't have their own failing constants. + visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); + } + } + MentionedItem::Drop(ty) => { + visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output); + } + MentionedItem::UnsizeCast { source_ty, target_ty } => { + let (source_ty, target_ty) = + find_vtable_types_for_unsizing(tcx.at(span), source_ty, target_ty); + // This could also be a different Unsize instruction, like + // from a fixed sized array to a slice. But we are only + // interested in things that produce a vtable. + if (target_ty.is_trait() && !source_ty.is_trait()) + || (target_ty.is_dyn_star() && !source_ty.is_dyn_star()) + { + create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, span, output); + } + } + MentionedItem::Closure(source_ty) => { + if let ty::Closure(def_id, args) = *source_ty.kind() { + let instance = + Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(tcx, &instance) { + output.push(create_fn_mono_item(tcx, instance, span)); + } + } else { + bug!() + } + } + } +} + +#[instrument(skip(tcx, output), level = "debug")] +fn collect_const_value<'tcx>( + tcx: TyCtxt<'tcx>, + value: mir::ConstValue<'tcx>, + output: &mut MonoItems<'tcx>, +) { + match value { + mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => { + collect_alloc(tcx, ptr.provenance.alloc_id(), output) + } + mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output), + mir::ConstValue::Slice { data, meta: _ } => { + for &prov in data.inner().provenance().ptrs().values() { + collect_alloc(tcx, prov.alloc_id(), output); + } + } + _ => {} + } +} + //=----------------------------------------------------------------------------- // Root Collection //=----------------------------------------------------------------------------- +// 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> { + debug!("collecting roots"); + let mut roots = Vec::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 crate_items = tcx.hir_crate_items(()); + + for id in crate_items.items() { + collector.process_item(id); + } + + for id in crate_items.impl_items() { + collector.process_impl_item(id); + } + + collector.push_extra_entry_roots(); + } + + // We can only codegen items that are instantiable - items all of + // whose predicates hold. Luckily, items that aren't instantiable + // can't actually be used, so we can just skip codegenning them. + roots + .into_iter() + .filter_map(|Spanned { node: mono_item, .. }| { + mono_item.is_instantiable(tcx).then_some(mono_item) + }) + .collect() +} + struct RootCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, strategy: MonoItemCollectionStrategy, @@ -1600,191 +1749,47 @@ fn create_mono_items_for_default_impls<'tcx>( } } -/// Scans the CTFE alloc in order to find function pointers and statics that must be monomorphized. -fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoItems<'tcx>) { - match tcx.global_alloc(alloc_id) { - GlobalAlloc::Static(def_id) => { - assert!(!tcx.is_thread_local_static(def_id)); - let instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, &instance) { - trace!("collecting static {:?}", def_id); - output.push(dummy_spanned(MonoItem::Static(def_id))); - } - } - GlobalAlloc::Memory(alloc) => { - trace!("collecting {:?} with {:#?}", alloc_id, alloc); - let ptrs = alloc.inner().provenance().ptrs(); - // avoid `ensure_sufficient_stack` in the common case of "no pointers" - if !ptrs.is_empty() { - rustc_data_structures::stack::ensure_sufficient_stack(move || { - for &prov in ptrs.values() { - collect_alloc(tcx, prov.alloc_id(), output); - } - }); - } - } - GlobalAlloc::Function(fn_instance) => { - if should_codegen_locally(tcx, &fn_instance) { - trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); - output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); - } - } - GlobalAlloc::VTable(ty, trait_ref) => { - let alloc_id = tcx.vtable_allocation((ty, trait_ref)); - collect_alloc(tcx, alloc_id, output) - } - } -} +//=----------------------------------------------------------------------------- +// Top-level entry point, tying it all together +//=----------------------------------------------------------------------------- -fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { - for impl_def_id in tcx.inherent_impls(def_id).ok()? { - if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( - tcx, - fn_ident, - AssocKind::Fn, - def_id, - ) { - return Some(new.def_id); - } - } - return None; -} +#[instrument(skip(tcx, strategy), level = "debug")] +pub fn collect_crate_mono_items( + tcx: TyCtxt<'_>, + strategy: MonoItemCollectionStrategy, +) -> (FxHashSet>, UsageMap<'_>) { + let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); -fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec { - let fns = [ - (tcx.lang_items().owned_box(), "new"), - (tcx.get_diagnostic_item(sym::Rc), "new"), - (tcx.get_diagnostic_item(sym::Arc), "new"), - ]; - fns.into_iter() - .filter_map(|(def_id, fn_name)| { - def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) - }) - .collect::>() -} + let roots = tcx + .sess + .time("monomorphization_collector_root_collections", || collect_roots(tcx, strategy)); -/// Scans the MIR in order to find function calls, closures, and drop-glue. -/// -/// Anything that's found is added to `output`. Furthermore the "mentioned items" of the MIR are returned. -#[instrument(skip(tcx, used_items, mentioned_items), level = "debug")] -fn collect_items_of_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - used_items: &mut MonoItems<'tcx>, - mentioned_items: &mut MonoItems<'tcx>, - mode: CollectionMode, -) { - let body = tcx.instance_mir(instance.def); - // Naively, in "used" collection mode, all functions get added to *both* `used_items` and - // `mentioned_items`. Mentioned items processing will then notice that they have already been - // visited, but at that point each mentioned item has been monomorphized, added to the - // `mentioned_items` worklist, and checked in the global set of visited items. To remove that - // overhead, we have a special optimization that avoids adding items to `mentioned_items` when - // they are already added in `used_items`. We could just scan `used_items`, but that's a linear - // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the - // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already - // added to `used_items` in a hash set, which can efficiently query in the - // `body.mentioned_items` loop below without even having to monomorphize the item. - let mut used_mentioned_items = FxHashSet::>::default(); - let mut collector = MirUsedCollector { - tcx, - body, - used_items, - used_mentioned_items: &mut used_mentioned_items, - instance, - move_size_spans: vec![], - visiting_call_terminator: false, - skip_move_check_fns: None, + debug!("building mono item graph, beginning at roots"); + + let mut state = SharedState { + visited: MTLock::new(FxHashSet::default()), + mentioned: MTLock::new(FxHashSet::default()), + usage_map: MTLock::new(UsageMap::new()), }; + let recursion_limit = tcx.recursion_limit(); - if mode == CollectionMode::UsedItems { - // Visit everything. Here we rely on the visitor also visiting `required_consts`, so that we - // evaluate them and abort compilation if any of them errors. - collector.visit_body(body); - } else { - // We only need to evaluate all constants, but can ignore the rest of the MIR. - for const_op in &body.required_consts { - if let Some(val) = collector.eval_constant(const_op) { - collect_const_value(tcx, val, mentioned_items); - } - } + { + let state: LRef<'_, _> = &mut state; + + tcx.sess.time("monomorphization_collector_graph_walk", || { + par_for_each_in(roots, |root| { + let mut recursion_depths = DefIdMap::default(); + collect_items_rec( + tcx, + dummy_spanned(root), + state, + &mut recursion_depths, + recursion_limit, + CollectionMode::UsedItems, + ); + }); + }); } - // Always gather mentioned items. We try to avoid processing items that we have already added to - // `used_items` above. - for item in &body.mentioned_items { - if !collector.used_mentioned_items.contains(&item.node) { - let item_mono = collector.monomorphize(item.node); - visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); - } - } -} - -/// `item` must be already monomorphized. -#[instrument(skip(tcx, span, output), level = "debug")] -fn visit_mentioned_item<'tcx>( - tcx: TyCtxt<'tcx>, - item: &MentionedItem<'tcx>, - span: Span, - output: &mut MonoItems<'tcx>, -) { - match *item { - MentionedItem::Fn(ty) => { - if let ty::FnDef(def_id, args) = *ty.kind() { - let instance = - Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); - // `visit_instance_use` was written for "used" item collection but works just as well - // for "mentioned" item collection. - // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway - // can't have their own failing constants. - visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); - } - } - MentionedItem::Drop(ty) => { - visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output); - } - MentionedItem::UnsizeCast { source_ty, target_ty } => { - let (source_ty, target_ty) = - find_vtable_types_for_unsizing(tcx.at(span), source_ty, target_ty); - // This could also be a different Unsize instruction, like - // from a fixed sized array to a slice. But we are only - // interested in things that produce a vtable. - if (target_ty.is_trait() && !source_ty.is_trait()) - || (target_ty.is_dyn_star() && !source_ty.is_dyn_star()) - { - create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, span, output); - } - } - MentionedItem::Closure(source_ty) => { - if let ty::Closure(def_id, args) = *source_ty.kind() { - let instance = - Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); - if should_codegen_locally(tcx, &instance) { - output.push(create_fn_mono_item(tcx, instance, span)); - } - } else { - bug!() - } - } - } -} - -#[instrument(skip(tcx, output), level = "debug")] -fn collect_const_value<'tcx>( - tcx: TyCtxt<'tcx>, - value: mir::ConstValue<'tcx>, - output: &mut MonoItems<'tcx>, -) { - match value { - mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => { - collect_alloc(tcx, ptr.provenance.alloc_id(), output) - } - mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output), - mir::ConstValue::Slice { data, meta: _ } => { - for &prov in data.inner().provenance().ptrs().values() { - collect_alloc(tcx, prov.alloc_id(), output); - } - } - _ => {} - } + (state.visited.into_inner(), state.usage_map.into_inner()) } From 8c01b85dba15f83724949769d45929cc449a9cf9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 21 Mar 2024 09:03:49 +0100 Subject: [PATCH 10/10] make sure we don't inline these generic fn as that could monomorphize them --- .../collect-in-dead-fn-behind-assoc-type.noopt.stderr | 2 +- .../collect-in-dead-fn-behind-assoc-type.opt.stderr | 2 +- .../required-consts/collect-in-dead-fn-behind-assoc-type.rs | 3 +++ .../collect-in-dead-fn-behind-generic.noopt.stderr | 2 +- .../collect-in-dead-fn-behind-generic.opt.stderr | 2 +- .../required-consts/collect-in-dead-fn-behind-generic.rs | 2 ++ .../collect-in-dead-fn-behind-opaque-type.noopt.stderr | 2 +- .../collect-in-dead-fn-behind-opaque-type.opt.stderr | 2 +- .../required-consts/collect-in-dead-fn-behind-opaque-type.rs | 1 + 9 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr index 38521061535..706c0d55b62 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr @@ -7,7 +7,7 @@ LL | const C: () = panic!(); = 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-in-dead-fn-behind-assoc-type.rs:15:17 + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:16:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr index 38521061535..706c0d55b62 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr @@ -7,7 +7,7 @@ LL | const C: () = panic!(); = 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-in-dead-fn-behind-assoc-type.rs:15:17 + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:16:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs index 77ebf28407c..9c36af50bb7 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs @@ -10,12 +10,14 @@ impl Fail { const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } +#[inline(never)] fn not_called() { if false { let _ = Fail::::C; } } +#[inline(never)] fn callit_not(f: impl Fn()) { if false { f(); @@ -33,6 +35,7 @@ impl Hideaway for () { const C: Self::T = not_called::; } +#[inline(never)] fn reveal() { if false { callit_not(T::C); diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr index 47515117417..581edd2b7b8 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr @@ -7,7 +7,7 @@ LL | const C: () = panic!(); = 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-in-dead-fn-behind-generic.rs:14:17 + --> $DIR/collect-in-dead-fn-behind-generic.rs:15:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr index 47515117417..581edd2b7b8 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr @@ -7,7 +7,7 @@ LL | const C: () = panic!(); = 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-in-dead-fn-behind-generic.rs:14:17 + --> $DIR/collect-in-dead-fn-behind-generic.rs:15:17 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs index de8ccfda97a..5829d914ee1 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs @@ -9,12 +9,14 @@ impl Fail { const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } +#[inline(never)] fn not_called() { if false { let _ = Fail::::C; } } +#[inline(never)] fn callit_not(f: impl Fn()) { if false { f(); diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr index d5a68a13e9d..07e46b8a816 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr @@ -7,7 +7,7 @@ LL | const C: () = panic!(); = 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-in-dead-fn-behind-opaque-type.rs:18:21 + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:19:21 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr index d5a68a13e9d..07e46b8a816 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr @@ -7,7 +7,7 @@ LL | const C: () = panic!(); = 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-in-dead-fn-behind-opaque-type.rs:18:21 + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:19:21 | LL | let _ = Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs index ee8c402c0e3..795e021ceb0 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs @@ -13,6 +13,7 @@ mod m { pub type NotCalledFn = impl Fn(); + #[inline(never)] fn not_called() { if false { let _ = Fail::::C;