diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 427340c333e5..7296c2015fff 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -35,9 +35,11 @@ impl ConstantCx { pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { let mut all_constants_ok = true; - for constant in &fx.mir.required_consts { - if eval_mir_constant(fx, constant).is_none() { - all_constants_ok = false; + for (item, _) in &fx.mir.required_items { + if let rustc_middle::mir::MonoItem::Const(constant) = item { + if eval_mir_constant(fx, constant).is_none() { + all_constants_ok = false; + } } } all_constants_ok diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 2809ec2deb55..74498d921190 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -2,6 +2,7 @@ use crate::base; use crate::traits::*; use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::MonoItem; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_target::abi::call::{FnAbi, PassMode}; @@ -202,14 +203,20 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Evaluate all required consts; codegen later assumes that CTFE will never fail. let mut all_consts_ok = true; - for const_ in &mir.required_consts { - if let Err(err) = fx.eval_mir_constant(const_) { - all_consts_ok = false; - match err { - // errored or at least linted - ErrorHandled::Reported(_) => {} - ErrorHandled::TooGeneric => { - span_bug!(const_.span, "codegen encountered polymorphic constant: {:?}", err) + for (const_, _) in &mir.required_items { + if let MonoItem::Const(const_) = const_ { + if let Err(err) = fx.eval_mir_constant(const_) { + all_consts_ok = false; + match err { + // errored or at least linted + ErrorHandled::Reported(_) => {} + ErrorHandled::TooGeneric => { + span_bug!( + const_.span, + "codegen encountered polymorphic constant: {:?}", + err + ) + } } } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 36606ff69a6c..31a5e593117b 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -703,10 +703,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.stack_mut().push(frame); // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). - for ct in &body.required_consts { - let span = ct.span; - let ct = self.subst_from_current_frame_and_normalize_erasing_regions(ct.literal)?; - self.eval_mir_constant(&ct, Some(span), None)?; + for (ct, span) in &body.required_items { + if let mir::MonoItem::Const(ct) = ct { + match ct.literal { + mir::ConstantKind::Ty(c) => match c.kind() { + ty::ConstKind::Unevaluated(_) => {} + _ => continue, + }, + mir::ConstantKind::Unevaluated(_, _) => {} + mir::ConstantKind::Val(_, _) => continue, + } + let ct = self.subst_from_current_frame_and_normalize_erasing_regions(ct.literal)?; + self.eval_mir_constant(&ct, Some(*span), None)?; + } } // Most locals are initially dead. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 669c609d9957..2e8205cb1dc8 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -220,6 +220,23 @@ pub struct GeneratorInfo<'tcx> { pub generator_kind: GeneratorKind, } +#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] +/// Items that the monomorphization collector needs to walk into. +pub enum MonoItem<'tcx> { + /// The `bool` states whether this is a direct call or not. + Fn(DefId, SubstsRef<'tcx>, bool), + Static(DefId), + Vtable { + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>, + }, + Const(Constant<'tcx>), + Drop(Ty<'tcx>), + Closure(DefId, SubstsRef<'tcx>), +} + +pub type MonoItems<'tcx> = Vec<(MonoItem<'tcx>, Span)>; + /// The lowered representation of a single function. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct Body<'tcx> { @@ -275,9 +292,11 @@ pub struct Body<'tcx> { /// A span representing this MIR, for error reporting. pub span: Span, - /// 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. - pub required_consts: Vec>, + /// Items that this body needs to monomorphize if monomorphized itself. + /// This field avoids the issue that optimizations can remove function calls in dead code + /// causing differences in monomorphization time errors depending on the optimizations + /// enabled. + pub required_items: MonoItems<'tcx>, /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. /// @@ -347,7 +366,7 @@ impl<'tcx> Body<'tcx> { spread_arg: None, var_debug_info, span, - required_consts: Vec::new(), + required_items: Vec::new(), is_polymorphic: false, injection_phase: None, tainted_by_errors, @@ -374,7 +393,7 @@ impl<'tcx> Body<'tcx> { arg_count: 0, spread_arg: None, span: DUMMY_SP, - required_consts: Vec::new(), + required_items: Vec::new(), var_debug_info: Vec::new(), is_polymorphic: false, injection_phase: None, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 205dc9ec7465..59de7e739330 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1039,9 +1039,14 @@ macro_rules! super_body { $self.visit_span($(& $mutability)? $body.span); - for const_ in &$($mutability)? $body.required_consts { + for (item, _) in &$($mutability)? $body.required_items { let location = Location::START; - $self.visit_constant(const_, location); + match item { + MonoItem::Const(const_) => if let ConstantKind::Unevaluated(..) = const_.literal { + $self.visit_constant(const_, location) + }, + _ => {} + } } } } diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index e5c2cc6c7bbc..f574d9e2e82f 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -55,7 +55,7 @@ pub(super) fn build_custom_mir<'tcx>( spread_arg: None, var_debug_info: Vec::new(), span, - required_consts: Vec::new(), + required_items: Vec::new(), is_polymorphic: false, tainted_by_errors: None, injection_phase: None, diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 240beabfc1a9..74b5a7301118 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -625,18 +625,7 @@ impl<'tcx> Inliner<'tcx> { kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, }); - // Copy only unevaluated constants from the callee_body into the caller_body. - // Although we are only pushing `ConstKind::Unevaluated` consts to - // `required_consts`, here we may not only have `ConstKind::Unevaluated` - // because we are calling `subst_and_normalize_erasing_regions`. - caller_body.required_consts.extend( - callee_body.required_consts.iter().copied().filter(|&ct| match ct.literal { - ConstantKind::Ty(_) => { - bug!("should never encounter ty::UnevaluatedConst in `required_consts`") - } - ConstantKind::Val(..) | ConstantKind::Unevaluated(..) => true, - }), - ); + caller_body.required_items.extend(callee_body.required_items.iter().cloned()); } kind => bug!("unexpected terminator kind {:?}", kind), } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index fa8257cf9849..8f9a05a81441 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -19,7 +19,6 @@ extern crate tracing; #[macro_use] extern crate rustc_middle; -use required_consts::RequiredConstsVisitor; use rustc_const_eval::util; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::steal::Steal; @@ -303,12 +302,13 @@ fn mir_promoted( body.tainted_by_errors = Some(error_reported); } - let mut required_consts = Vec::new(); - let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); + let param_env = tcx.param_env(def); + let mut required_consts_visitor = + required_consts::MirNeighborCollector { tcx, body: &body, output: vec![], param_env }; for (bb, bb_data) in traversal::reverse_postorder(&body) { required_consts_visitor.visit_basic_block_data(bb, bb_data); } - body.required_consts = required_consts; + body.required_items = required_consts_visitor.output; // What we need to run borrowck etc. let promote_pass = promote_consts::PromoteTemps::default(); diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index 243cb463560e..d37f729d875f 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -1,27 +1,326 @@ -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{Constant, ConstantKind, Location}; -use rustc_middle::ty::ConstKind; +//! Mono Item Collection +//! ==================== +//! +//! This module is responsible for discovering all items that will contribute +//! to code generation of a single function. The important part here is that it not only +//! needs to find syntax-level items (functions, structs, etc) but also all +//! their monomorphized instantiations. Every non-generic, non-const function +//! maps to one LLVM artifact. Every generic function can produce +//! from zero to N artifacts, depending on the sets of type arguments it +//! is instantiated with. +//! This also applies to generic items from other crates: A generic definition +//! in crate X might produce monomorphizations that are compiled into crate Y. +//! We also have to collect these here. +//! +//! The following kinds of "mono items" are handled here: +//! +//! - Functions +//! - Methods +//! - Closures +//! - Statics +//! - Drop glue +//! +//! The following things also result in LLVM artifacts, but are not collected +//! here, since we instantiate them locally on demand when needed in a given +//! codegen unit: +//! +//! - Constants +//! - VTables +//! - Object Shims +//! +//! ### Finding neighbor nodes +//! Given an item's MIR, we can discover neighbors by walking the MIR and +//! any time we hit upon something that signifies a +//! reference to another mono item, we have found a neighbor. +//! The specific forms a reference to a neighboring node can take +//! in MIR are quite diverse. Here is an overview: +//! +//! #### Calling Functions/Methods +//! The most obvious form of one mono item referencing another is a +//! function or method call (represented by a CALL terminator in MIR). But +//! calls are not the only thing that might introduce a reference between two +//! function mono items, and as we will see below, they are just a +//! specialization of the form described next, and consequently will not get any +//! special treatment in the algorithm. +//! +//! #### Taking a reference to a function or method +//! A function does not need to actually be called in order to be a neighbor of +//! another function. It suffices to just take a reference in order to introduce +//! an edge. Consider the following example: +//! +//! ``` +//! # use core::fmt::Display; +//! fn print_val(x: T) { +//! println!("{}", x); +//! } +//! +//! fn call_fn(f: &dyn Fn(i32), x: i32) { +//! f(x); +//! } +//! +//! fn main() { +//! let print_i32 = print_val::; +//! call_fn(&print_i32, 0); +//! } +//! ``` +//! The MIR of none of these functions will contain an explicit call to +//! `print_val::`. Nonetheless, in order to mono this program, we need +//! an instance of this function. Thus, whenever we encounter a function or +//! method in operand position, we treat it as a neighbor of the current +//! mono item. Calls are just a special case of that. +//! +//! #### Drop glue +//! Drop glue mono items are introduced by MIR drop-statements. The +//! generated mono item will again have drop-glue item neighbors if the +//! type to be dropped contains nested values that also need to be dropped. It +//! might also have a function item neighbor for the explicit `Drop::drop` +//! implementation of its type. +//! +//! #### Unsizing Casts +//! A subtle way of introducing neighbor edges is by casting to a trait object. +//! Since the resulting fat-pointer contains a reference to a vtable, we need to +//! instantiate all object-safe methods of the trait, as we need to store +//! pointers to these functions even if they never get called anywhere. This can +//! be seen as a special case of taking a function reference. +//! +//! #### Boxes +//! Since `Box` expression have special compiler support, no explicit calls to +//! `exchange_malloc()` and `box_free()` may show up in MIR, even if the +//! compiler will generate them. We have to observe `Rvalue::Box` expressions +//! and Box-typed drop-statements for that purpose. +//! +//! Open Issues +//! ----------- +//! Some things are not yet fully implemented in the current version of this +//! module. +//! +//! ### Const Fns +//! Ideally, no mono item should be generated for const fns unless there +//! is a call to them that cannot be evaluated at compile time. At the moment +//! this is not implemented however: a mono item will be produced +//! regardless of whether it is actually needed or not. -pub struct RequiredConstsVisitor<'a, 'tcx> { - required_consts: &'a mut Vec>, +use rustc_hir::def::DefKind; +use rustc_hir::lang_items::LangItem; +use rustc_middle::mir::visit::TyContext; +use rustc_middle::mir::visit::Visitor as MirVisitor; +use rustc_middle::mir::MonoItem; +use rustc_middle::mir::MonoItems; +use rustc_middle::mir::{self, Local, Location}; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_span::def_id::DefId; +use rustc_span::source_map::Span; + +pub struct MirNeighborCollector<'a, 'tcx> { + pub tcx: TyCtxt<'tcx>, + pub body: &'a mir::Body<'tcx>, + pub output: MonoItems<'tcx>, + /// This is just the identity param env of the current MIR body cached here + /// for convenience. + pub param_env: ty::ParamEnv<'tcx>, } -impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { - pub fn new(required_consts: &'a mut Vec>) -> Self { - RequiredConstsVisitor { required_consts } +impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { + debug!("visiting rvalue {:?}", *rvalue); + + let span = self.body.source_info(location).span; + + match *rvalue { + // When doing an cast from a regular pointer to a fat pointer, we + // have to instantiate all methods of the trait being cast to, so we + // can build the appropriate vtable. + mir::Rvalue::Cast( + mir::CastKind::Pointer(PointerCast::Unsize), + ref operand, + target_ty, + ) + | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { + self.output.push(( + MonoItem::Vtable { source_ty: operand.ty(self.body, self.tcx), target_ty }, + span, + )); + } + mir::Rvalue::Cast( + mir::CastKind::Pointer(PointerCast::ReifyFnPointer), + ref operand, + _, + ) => { + let fn_ty = operand.ty(self.body, self.tcx); + self.visit_fn_use(fn_ty, false, span); + } + mir::Rvalue::Cast( + mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)), + ref operand, + _, + ) => { + let source_ty = operand.ty(self.body, self.tcx); + match *source_ty.kind() { + ty::Closure(def_id, substs) => { + if should_codegen_locally(self.tcx, def_id) { + self.output.push((MonoItem::Closure(def_id, substs), span)); + } + } + _ => bug!(), + } + } + mir::Rvalue::ThreadLocalRef(def_id) => { + assert!(self.tcx.is_thread_local_static(def_id)); + if should_codegen_locally(self.tcx, def_id) { + trace!("collecting thread-local static {:?}", def_id); + self.output.push((MonoItem::Static(def_id), span)); + } + } + _ => { /* not interesting */ } + } + + self.super_rvalue(rvalue, location); + } + + /// This does not walk the constant, as it has been handled entirely here and trying + /// to walk it would attempt to evaluate the `ty::Const` inside, which doesn't necessarily + /// work, as some constants cannot be represented in the type system. + #[instrument(skip(self), level = "debug")] + fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) { + self.output.push((MonoItem::Const(*constant), constant.span)); + MirVisitor::visit_ty(self, constant.literal.ty(), TyContext::Location(location)); + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + debug!("visiting terminator {:?} @ {:?}", terminator, location); + let source = self.body.source_info(location).span; + + let tcx = self.tcx; + match terminator.kind { + mir::TerminatorKind::Call { ref func, .. } => { + let callee_ty = func.ty(self.body, tcx); + self.visit_fn_use(callee_ty, true, source) + } + mir::TerminatorKind::Drop { ref place, .. } => { + let ty = place.ty(self.body, self.tcx).ty; + self.output.push((MonoItem::Drop(ty), source)); + } + mir::TerminatorKind::InlineAsm { ref operands, .. } => { + for op in operands { + match *op { + mir::InlineAsmOperand::SymFn { ref value } => { + self.visit_fn_use(value.literal.ty(), false, source); + } + mir::InlineAsmOperand::SymStatic { def_id } => { + if should_codegen_locally(self.tcx, def_id) { + trace!("collecting asm sym static {:?}", def_id); + self.output.push((MonoItem::Static(def_id), source)); + } + } + _ => {} + } + } + } + mir::TerminatorKind::Assert { ref msg, .. } => { + let lang_item = match &**msg { + mir::AssertKind::BoundsCheck { .. } => LangItem::PanicBoundsCheck, + _ => LangItem::Panic, + }; + let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source))); + if should_codegen_locally(tcx, instance.def_id()) { + self.output + .push((MonoItem::Fn(instance.def_id(), instance.substs, true), source)); + } + } + mir::TerminatorKind::Terminate { .. } => { + let instance = Instance::mono( + tcx, + tcx.require_lang_item(LangItem::PanicCannotUnwind, Some(source)), + ); + if should_codegen_locally(tcx, instance.def_id()) { + self.output + .push((MonoItem::Fn(instance.def_id(), instance.substs, true), source)); + } + } + // The contents of these are walked with `super_terminator` below. They don't have any + // special constants or call-like behavior. + mir::TerminatorKind::Yield { .. } + | mir::TerminatorKind::Goto { .. } + | mir::TerminatorKind::SwitchInt { .. } + | mir::TerminatorKind::Resume + | mir::TerminatorKind::Return + | mir::TerminatorKind::FalseUnwind { .. } + | mir::TerminatorKind::FalseEdge { .. } + | mir::TerminatorKind::Unreachable + | mir::TerminatorKind::GeneratorDrop => {} + } + + if let Some(mir::UnwindAction::Terminate) = terminator.unwind() { + let instance = Instance::mono( + tcx, + tcx.require_lang_item(LangItem::PanicCannotUnwind, Some(source)), + ); + if should_codegen_locally(tcx, instance.def_id()) { + self.output.push((MonoItem::Fn(instance.def_id(), instance.substs, true), source)); + } + } + + self.super_terminator(terminator, location); + } + + fn visit_local( + &mut self, + _place_local: Local, + _context: mir::visit::PlaceContext, + _location: Location, + ) { } } -impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { - fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) { - let literal = constant.literal; - match literal { - ConstantKind::Ty(c) => match c.kind() { - ConstKind::Param(_) | ConstKind::Error(_) | ConstKind::Value(_) => {} - _ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c), - }, - ConstantKind::Unevaluated(..) => self.required_consts.push(*constant), - ConstantKind::Val(..) => {} +impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { + fn visit_fn_use(&mut self, ty: Ty<'tcx>, is_direct_call: bool, source: Span) { + if let ty::FnDef(def_id, substs) = *ty.kind() { + let should_codegen_locally = || { + if self.tcx.try_normalize_erasing_regions(self.param_env, substs).is_err() { + return true; + } + if let Ok(Some(instance)) = + ty::Instance::resolve(self.tcx, self.param_env, def_id, substs) + { + if let ty::InstanceDef::Item(..) = instance.def { + should_codegen_locally(self.tcx, instance.def_id()) + } else { + true + } + } else { + true + } + }; + if !is_direct_call || should_codegen_locally() { + self.output.push((MonoItem::Fn(def_id, substs, is_direct_call), source)) + } } } } + +/// Returns `true` if we should codegen an item in the local crate, or returns `false` if we +/// can just link to the upstream crate and therefore don't need a mono item. +fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { + if tcx.is_foreign_item(def_id) { + // Foreign items are always linked against, there's no way of instantiating them. + return false; + } + + if def_id.is_local() { + // Local items cannot be referred to locally without monomorphizing them locally. + return true; + } + + if tcx.is_reachable_non_generic(def_id) { + // We can link to the item in question, no instance needed in this crate. + return false; + } + + if let DefKind::Static(_) = tcx.def_kind(def_id) { + // We cannot monomorphize statics from upstream crates. + return false; + } + + tcx.is_mir_available(def_id) +} diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 4a5953c11492..8e8e7391908a 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -264,23 +264,34 @@ pub fn collect_crate_mono_items( let mut visited = MTLock::new(FxHashSet::default()); let mut usage_map = MTLock::new(UsageMap::new()); + let mut all_items = MTLock::new(vec![]); let recursion_limit = tcx.recursion_limit(); { let visited: MTLockRef<'_, _> = &mut visited; let usage_map: MTLockRef<'_, _> = &mut usage_map; - tcx.sess.time("monomorphization_collector_graph_walk", || { + tcx.sess.time("monomorphization_collector_graph_walk", move || { + let all_items_ref: MTLockRef<'_, _> = &mut all_items; par_for_each_in(roots, |root| { let mut recursion_depths = DefIdMap::default(); + let mut all_thread_items = vec![]; collect_items_rec( tcx, dummy_spanned(root), visited, + &mut all_thread_items, &mut recursion_depths, recursion_limit, usage_map, ); + all_thread_items.retain(|item| !visited.lock().contains(&item.node)); + all_items_ref.lock_mut().extend(all_thread_items); + }); + let visited = &mut MTLock::new(visited.lock().clone()); + par_for_each_in(all_items.into_inner(), |root| { + let mut recursion_depths = DefIdMap::default(); + collect_all_items_rec(tcx, root, visited, &mut recursion_depths, recursion_limit); }); }); } @@ -333,6 +344,7 @@ fn collect_items_rec<'tcx>( tcx: TyCtxt<'tcx>, starting_item: Spanned>, visited: MTLockRef<'_, FxHashSet>>, + all_items: &mut Vec>>, recursion_depths: &mut DefIdMap, recursion_limit: Limit, usage_map: MTLockRef<'_, UsageMap<'tcx>>, @@ -388,6 +400,9 @@ fn collect_items_rec<'tcx>( } } + let body = tcx.mir_for_ctfe(def_id); + collect_mono_items(tcx, instance, body, all_items); + if tcx.needs_thread_local_shim(def_id) { used_items.push(respan( starting_item.span, @@ -413,7 +428,7 @@ 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_used_items(tcx, instance, &mut used_items, all_items); }); } MonoItem::GlobalAsm(item_id) => { @@ -469,7 +484,79 @@ fn collect_items_rec<'tcx>( 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); + collect_items_rec( + tcx, + used_item, + visited, + all_items, + recursion_depths, + recursion_limit, + usage_map, + ); + } + + if let Some((def_id, depth)) = recursion_depth_reset { + recursion_depths.insert(def_id, depth); + } +} + +#[instrument(skip(tcx, visited, recursion_depths, recursion_limit), level = "debug")] +fn collect_all_items_rec<'tcx>( + tcx: TyCtxt<'tcx>, + starting_item: Spanned>, + visited: MTLockRef<'_, FxHashSet>>, + recursion_depths: &mut DefIdMap, + recursion_limit: Limit, +) { + if !visited.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + + let mut all_items = Vec::new(); + let recursion_depth_reset; + let error_count = tcx.sess.diagnostic().err_count(); + + match starting_item.node { + MonoItem::Static(def_id) => { + let instance = Instance::mono(tcx, def_id); + + recursion_depth_reset = None; + + let body = tcx.mir_for_ctfe(def_id); + collect_mono_items(tcx, instance, body, &mut all_items); + } + MonoItem::Fn(instance) => { + // Keep track of the monomorphization recursion depth + recursion_depth_reset = Some(check_recursion_limit( + tcx, + instance, + starting_item.span, + recursion_depths, + recursion_limit, + )); + check_type_length_limit(tcx, instance); + let body = tcx.instance_mir(instance.def); + collect_mono_items(tcx, instance, body, &mut all_items); + } + MonoItem::GlobalAsm(_) => unreachable!(), + } + + // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the + // mono item graph. + if tcx.sess.diagnostic().err_count() > error_count + && starting_item.node.is_generic_fn() + && starting_item.node.is_user_defined() + { + let formatted_item = with_no_trimmed_paths!(starting_item.node.to_string()); + tcx.sess.emit_note(EncounteredErrorWhileInstantiating { + span: starting_item.span, + formatted_item, + }); + } + + for item in all_items { + collect_all_items_rec(tcx, item, visited, recursion_depths, recursion_limit); } if let Some((def_id, depth)) = recursion_depth_reset { @@ -1364,14 +1451,67 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte } } +/// Monomorphizes all items, even those in dead code. +#[instrument(skip(tcx, output), level = "debug")] +fn collect_mono_items<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &mir::Body<'tcx>, + output: &mut MonoItems<'tcx>, +) { + let body = tcx.instance_mir(instance.def); + + // We collect into a separate collector to avoid codegenning things + // from dead code. + let mut col = MirUsedCollector { tcx, body, output, instance }; + for (item, span) in &body.required_items { + match *item { + mir::MonoItem::Fn(def_id, substs, is_direct_call) => { + let callee_ty = tcx.mk_fn_def(def_id, substs); + let callee_ty = col.monomorphize(callee_ty); + visit_fn_use(tcx, callee_ty, is_direct_call, *span, col.output) + } + mir::MonoItem::Static(def_id) => { + let instance = Instance::mono(tcx, def_id); + if should_codegen_locally(tcx, &instance) { + col.output.push(respan(*span, MonoItem::Static(def_id))); + } + } + mir::MonoItem::Vtable { source_ty, target_ty } => { + let source_ty = col.monomorphize(source_ty); + let target_ty = col.monomorphize(target_ty); + create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, *span, col.output) + } + mir::MonoItem::Const(ref constant) => col.visit_constant(constant, Location::START), + mir::MonoItem::Drop(ty) => { + let ty = col.monomorphize(ty); + visit_drop_use(tcx, ty, true, *span, col.output) + } + mir::MonoItem::Closure(def_id, substs) => { + let substs = col.monomorphize(substs); + let instance = + Instance::resolve_closure(tcx, def_id, substs, ty::ClosureKind::FnOnce) + .expect("failed to normalize and resolve closure during codegen"); + if should_codegen_locally(tcx, &instance) { + col.output.push(create_fn_mono_item(tcx, instance, *span)); + } + } + } + } +} + /// Scans the MIR in order to find function calls, closures, and drop-glue. #[instrument(skip(tcx, output), level = "debug")] fn collect_used_items<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, output: &mut MonoItems<'tcx>, + all_mono_items: &mut MonoItems<'tcx>, ) { let body = tcx.instance_mir(instance.def); + + collect_mono_items(tcx, instance, body, all_mono_items); + MirUsedCollector { tcx, body: &body, output, instance }.visit_body(&body); } diff --git a/tests/ui/inline-const/optimization_dependent_errors.debug.stderr b/tests/ui/inline-const/optimization_dependent_errors.debug.stderr new file mode 100644 index 000000000000..ce9ede5d5f6b --- /dev/null +++ b/tests/ui/inline-const/optimization_dependent_errors.debug.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Foo::<()>::BAR` failed + --> $DIR/optimization_dependent_errors.rs:10:9 + | +LL | panic!() + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/optimization_dependent_errors.rs:10:9 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn bop::<()>` + --> $DIR/optimization_dependent_errors.rs:22:9 + | +LL | bop::(); + | ^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/inline-const/optimization_dependent_errors.opt.stderr b/tests/ui/inline-const/optimization_dependent_errors.opt.stderr new file mode 100644 index 000000000000..ce9ede5d5f6b --- /dev/null +++ b/tests/ui/inline-const/optimization_dependent_errors.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Foo::<()>::BAR` failed + --> $DIR/optimization_dependent_errors.rs:10:9 + | +LL | panic!() + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/optimization_dependent_errors.rs:10:9 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn bop::<()>` + --> $DIR/optimization_dependent_errors.rs:22:9 + | +LL | bop::(); + | ^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/inline-const/optimization_dependent_errors.rs b/tests/ui/inline-const/optimization_dependent_errors.rs new file mode 100644 index 000000000000..0e9c1efb6bba --- /dev/null +++ b/tests/ui/inline-const/optimization_dependent_errors.rs @@ -0,0 +1,29 @@ +// revisions: opt debug +//[opt] compile-flags: -O + +// build-fail + +struct Foo(T); + +impl Foo { + const BAR: () = if std::mem::size_of::() == 0 { + panic!() + //~^ ERROR evaluation of `Foo::<()>::BAR` failed + }; +} + +#[inline(never)] +fn bop() { + Foo::::BAR; +} + +fn fop() { + if false { + bop::(); + } +} + +fn main() { + fop::(); + fop::<()>(); +}