collector: recursively traverse 'mentioned' items to evaluate their constants
This commit is contained in:
parent
d31b6fb8c0
commit
712fe36611
24 changed files with 641 additions and 142 deletions
|
@ -20,6 +20,7 @@
|
||||||
//! | ----------------------- | ------------------- | ------------------------------- |
|
//! | ----------------------- | ------------------- | ------------------------------- |
|
||||||
//! | `Lrc<T>` | `rc::Rc<T>` | `sync::Arc<T>` |
|
//! | `Lrc<T>` | `rc::Rc<T>` | `sync::Arc<T>` |
|
||||||
//! |` Weak<T>` | `rc::Weak<T>` | `sync::Weak<T>` |
|
//! |` Weak<T>` | `rc::Weak<T>` | `sync::Weak<T>` |
|
||||||
|
//! | `LRef<'a, T>` [^2] | `&'a mut T` | `&'a T` |
|
||||||
//! | | | |
|
//! | | | |
|
||||||
//! | `AtomicBool` | `Cell<bool>` | `atomic::AtomicBool` |
|
//! | `AtomicBool` | `Cell<bool>` | `atomic::AtomicBool` |
|
||||||
//! | `AtomicU32` | `Cell<u32>` | `atomic::AtomicU32` |
|
//! | `AtomicU32` | `Cell<u32>` | `atomic::AtomicU32` |
|
||||||
|
@ -38,7 +39,7 @@
|
||||||
//! of a `RefCell`. This is appropriate when interior mutability is not
|
//! of a `RefCell`. This is appropriate when interior mutability is not
|
||||||
//! required.
|
//! required.
|
||||||
//!
|
//!
|
||||||
//! [^2] `MTLockRef` is a typedef.
|
//! [^2] `MTRef`, `MTLockRef` are type aliases.
|
||||||
|
|
||||||
pub use crate::marker::*;
|
pub use crate::marker::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -208,7 +209,7 @@ cfg_match! {
|
||||||
|
|
||||||
use std::cell::RefCell as InnerRwLock;
|
use std::cell::RefCell as InnerRwLock;
|
||||||
|
|
||||||
pub type MTLockRef<'a, T> = &'a mut MTLock<T>;
|
pub type LRef<'a, T> = &'a mut T;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MTLock<T>(T);
|
pub struct MTLock<T>(T);
|
||||||
|
@ -274,7 +275,7 @@ cfg_match! {
|
||||||
pub use std::sync::Arc as Lrc;
|
pub use std::sync::Arc as Lrc;
|
||||||
pub use std::sync::Weak as Weak;
|
pub use std::sync::Weak as Weak;
|
||||||
|
|
||||||
pub type MTLockRef<'a, T> = &'a MTLock<T>;
|
pub type LRef<'a, T> = &'a T;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MTLock<T>(Lock<T>);
|
pub struct MTLock<T>(Lock<T>);
|
||||||
|
@ -314,6 +315,8 @@ cfg_match! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type MTLockRef<'a, T> = LRef<'a, MTLock<T>>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
#[cfg_attr(parallel_compiler, repr(align(64)))]
|
#[cfg_attr(parallel_compiler, repr(align(64)))]
|
||||||
pub struct CacheAligned<T>(pub T);
|
pub struct CacheAligned<T>(pub T);
|
||||||
|
|
|
@ -20,6 +20,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
|
||||||
use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind};
|
use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind};
|
||||||
use rustc_hir::{self as hir, HirId};
|
use rustc_hir::{self as hir, HirId};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||||
|
|
||||||
use polonius_engine::Atom;
|
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.
|
/// The lowered representation of a single function.
|
||||||
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
|
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
|
||||||
pub struct Body<'tcx> {
|
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.
|
/// 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.
|
/// 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<ConstOperand<'tcx>>,
|
pub required_consts: Vec<ConstOperand<'tcx>>,
|
||||||
|
|
||||||
|
/// 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<Spanned<MentionedItem<'tcx>>>,
|
||||||
|
|
||||||
/// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
|
/// 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.
|
/// 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,
|
var_debug_info,
|
||||||
span,
|
span,
|
||||||
required_consts: Vec::new(),
|
required_consts: Vec::new(),
|
||||||
|
mentioned_items: Vec::new(),
|
||||||
is_polymorphic: false,
|
is_polymorphic: false,
|
||||||
injection_phase: None,
|
injection_phase: None,
|
||||||
tainted_by_errors,
|
tainted_by_errors,
|
||||||
|
@ -482,6 +510,7 @@ impl<'tcx> Body<'tcx> {
|
||||||
spread_arg: None,
|
spread_arg: None,
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
required_consts: Vec::new(),
|
required_consts: Vec::new(),
|
||||||
|
mentioned_items: Vec::new(),
|
||||||
var_debug_info: Vec::new(),
|
var_debug_info: Vec::new(),
|
||||||
is_polymorphic: false,
|
is_polymorphic: false,
|
||||||
injection_phase: None,
|
injection_phase: None,
|
||||||
|
|
|
@ -56,6 +56,7 @@ pub(super) fn build_custom_mir<'tcx>(
|
||||||
var_debug_info: Vec::new(),
|
var_debug_info: Vec::new(),
|
||||||
span,
|
span,
|
||||||
required_consts: Vec::new(),
|
required_consts: Vec::new(),
|
||||||
|
mentioned_items: Vec::new(),
|
||||||
is_polymorphic: false,
|
is_polymorphic: false,
|
||||||
tainted_by_errors: None,
|
tainted_by_errors: None,
|
||||||
injection_phase: None,
|
injection_phase: None,
|
||||||
|
|
|
@ -565,7 +565,8 @@ impl<'tcx> Inliner<'tcx> {
|
||||||
mut callee_body: Body<'tcx>,
|
mut callee_body: Body<'tcx>,
|
||||||
) {
|
) {
|
||||||
let terminator = caller_body[callsite.block].terminator.take().unwrap();
|
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);
|
bug!("unexpected terminator kind {:?}", terminator.kind);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -717,6 +718,30 @@ impl<'tcx> Inliner<'tcx> {
|
||||||
Const::Val(..) | Const::Unevaluated(..) => true,
|
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(
|
fn make_call_args(
|
||||||
|
|
|
@ -89,6 +89,7 @@ mod lint;
|
||||||
mod lower_intrinsics;
|
mod lower_intrinsics;
|
||||||
mod lower_slice_len;
|
mod lower_slice_len;
|
||||||
mod match_branches;
|
mod match_branches;
|
||||||
|
mod mentioned_items;
|
||||||
mod multiple_return_terminators;
|
mod multiple_return_terminators;
|
||||||
mod normalize_array_len;
|
mod normalize_array_len;
|
||||||
mod nrvo;
|
mod nrvo;
|
||||||
|
@ -566,6 +567,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
tcx,
|
tcx,
|
||||||
body,
|
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,
|
&check_alignment::CheckAlignment,
|
||||||
// Before inlining: trim down MIR with passes to reduce inlining work.
|
// Before inlining: trim down MIR with passes to reduce inlining work.
|
||||||
|
|
||||||
|
|
57
compiler/rustc_mir_transform/src/mentioned_items.rs
Normal file
57
compiler/rustc_mir_transform/src/mentioned_items.rs
Normal file
|
@ -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<Spanned<MentionedItem<'tcx>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ use std::iter;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator,
|
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_middle::mir::patch::MirPatch;
|
||||||
use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
|
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,
|
tcx,
|
||||||
&mut body,
|
&mut body,
|
||||||
&[
|
&[
|
||||||
|
&mentioned_items::MentionedItems,
|
||||||
&abort_unwinding_calls::AbortUnwindingCalls,
|
&abort_unwinding_calls::AbortUnwindingCalls,
|
||||||
&add_call_guards::CriticalCallEdges,
|
&add_call_guards::CriticalCallEdges,
|
||||||
],
|
],
|
||||||
|
@ -178,6 +179,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
||||||
tcx,
|
tcx,
|
||||||
&mut result,
|
&mut result,
|
||||||
&[
|
&[
|
||||||
|
&mentioned_items::MentionedItems,
|
||||||
&add_moves_for_packed_drops::AddMovesForPackedDrops,
|
&add_moves_for_packed_drops::AddMovesForPackedDrops,
|
||||||
&deref_separator::Derefer,
|
&deref_separator::Derefer,
|
||||||
&remove_noop_landing_pads::RemoveNoopLandingPads,
|
&remove_noop_landing_pads::RemoveNoopLandingPads,
|
||||||
|
|
|
@ -137,21 +137,41 @@
|
||||||
//! just linked to and no node is created; which is exactly what we want, since
|
//! 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.
|
//! no machine code should be generated in the current crate for such an item.
|
||||||
//!
|
//!
|
||||||
//! Eager and Lazy Collection Mode
|
//! Eager and Lazy Collection Strategy
|
||||||
//! ------------------------------
|
//! ----------------------------------
|
||||||
//! Mono item collection can be performed in one of two modes:
|
//! 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
|
//! used. The goal is to produce the least amount of machine code
|
||||||
//! possible.
|
//! 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
|
//! 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
|
//! in the crate, even if no drop call for that type exists (yet). It will
|
||||||
//! also instantiate default implementations of trait methods, something that
|
//! also instantiate default implementations of trait methods, something that
|
||||||
//! otherwise is only done on demand.
|
//! 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
|
//! Open Issues
|
||||||
//! -----------
|
//! -----------
|
||||||
|
@ -165,15 +185,16 @@
|
||||||
//! regardless of whether it is actually needed or not.
|
//! regardless of whether it is actually needed or not.
|
||||||
|
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
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 as hir;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
|
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
|
||||||
use rustc_hir::lang_items::LangItem;
|
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::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar};
|
||||||
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
|
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
|
||||||
use rustc_middle::mir::visit::Visitor as MirVisitor;
|
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::query::TyCtxtAt;
|
||||||
use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion};
|
use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion};
|
||||||
use rustc_middle::ty::layout::ValidityRequirement;
|
use rustc_middle::ty::layout::ValidityRequirement;
|
||||||
|
@ -183,7 +204,6 @@ use rustc_middle::ty::{
|
||||||
TypeVisitableExt, VtblEntry,
|
TypeVisitableExt, VtblEntry,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{GenericArgKind, GenericArgs};
|
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::config::EntryFnType;
|
||||||
use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
|
use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
|
||||||
use rustc_session::Limit;
|
use rustc_session::Limit;
|
||||||
|
@ -199,7 +219,7 @@ use crate::errors::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum MonoItemCollectionMode {
|
pub enum MonoItemCollectionStrategy {
|
||||||
Eager,
|
Eager,
|
||||||
Lazy,
|
Lazy,
|
||||||
}
|
}
|
||||||
|
@ -214,6 +234,35 @@ pub struct UsageMap<'tcx> {
|
||||||
|
|
||||||
type MonoItems<'tcx> = Vec<Spanned<MonoItem<'tcx>>>;
|
type MonoItems<'tcx> = Vec<Spanned<MonoItem<'tcx>>>;
|
||||||
|
|
||||||
|
/// 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<FxHashSet<MonoItem<'tcx>>>,
|
||||||
|
/// 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<FxHashSet<MonoItem<'tcx>>>,
|
||||||
|
/// Which items are being used where, for better errors.
|
||||||
|
usage_map: MTLock<UsageMap<'tcx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
impl<'tcx> UsageMap<'tcx> {
|
||||||
fn new() -> UsageMap<'tcx> {
|
fn new() -> UsageMap<'tcx> {
|
||||||
UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() }
|
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(
|
pub fn collect_crate_mono_items(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
mode: MonoItemCollectionMode,
|
strategy: MonoItemCollectionStrategy,
|
||||||
) -> (FxHashSet<MonoItem<'_>>, UsageMap<'_>) {
|
) -> (FxHashSet<MonoItem<'_>>, UsageMap<'_>) {
|
||||||
let _prof_timer = tcx.prof.generic_activity("monomorphization_collector");
|
let _prof_timer = tcx.prof.generic_activity("monomorphization_collector");
|
||||||
|
|
||||||
let roots =
|
let roots = tcx
|
||||||
tcx.sess.time("monomorphization_collector_root_collections", || collect_roots(tcx, mode));
|
.sess
|
||||||
|
.time("monomorphization_collector_root_collections", || collect_roots(tcx, strategy));
|
||||||
|
|
||||||
debug!("building mono item graph, beginning at roots");
|
debug!("building mono item graph, beginning at roots");
|
||||||
|
|
||||||
let mut visited = MTLock::new(FxHashSet::default());
|
let mut state = SharedState {
|
||||||
let mut usage_map = MTLock::new(UsageMap::new());
|
visited: MTLock::new(FxHashSet::default()),
|
||||||
|
mentioned: MTLock::new(FxHashSet::default()),
|
||||||
|
usage_map: MTLock::new(UsageMap::new()),
|
||||||
|
};
|
||||||
let recursion_limit = tcx.recursion_limit();
|
let recursion_limit = tcx.recursion_limit();
|
||||||
|
|
||||||
{
|
{
|
||||||
let visited: MTLockRef<'_, _> = &mut visited;
|
let state: LRef<'_, _> = &mut state;
|
||||||
let usage_map: MTLockRef<'_, _> = &mut usage_map;
|
|
||||||
|
|
||||||
tcx.sess.time("monomorphization_collector_graph_walk", || {
|
tcx.sess.time("monomorphization_collector_graph_walk", || {
|
||||||
par_for_each_in(roots, |root| {
|
par_for_each_in(roots, |root| {
|
||||||
|
@ -279,22 +331,22 @@ pub fn collect_crate_mono_items(
|
||||||
collect_items_rec(
|
collect_items_rec(
|
||||||
tcx,
|
tcx,
|
||||||
dummy_spanned(root),
|
dummy_spanned(root),
|
||||||
visited,
|
state,
|
||||||
&mut recursion_depths,
|
&mut recursion_depths,
|
||||||
recursion_limit,
|
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
|
// Find all non-generic items by walking the HIR. These items serve as roots to
|
||||||
// start monomorphizing from.
|
// start monomorphizing from.
|
||||||
#[instrument(skip(tcx, mode), level = "debug")]
|
#[instrument(skip(tcx, mode), level = "debug")]
|
||||||
fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<'_>> {
|
fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec<MonoItem<'_>> {
|
||||||
debug!("collecting roots");
|
debug!("collecting roots");
|
||||||
let mut roots = Vec::new();
|
let mut roots = Vec::new();
|
||||||
|
|
||||||
|
@ -303,7 +355,7 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<
|
||||||
|
|
||||||
debug!("collect_roots: entry_fn = {:?}", entry_fn);
|
debug!("collect_roots: entry_fn = {:?}", entry_fn);
|
||||||
|
|
||||||
let mut collector = RootCollector { tcx, mode, entry_fn, output: &mut roots };
|
let mut collector = RootCollector { tcx, strategy: mode, entry_fn, output: &mut roots };
|
||||||
|
|
||||||
let crate_items = tcx.hir_crate_items(());
|
let crate_items = tcx.hir_crate_items(());
|
||||||
|
|
||||||
|
@ -331,21 +383,38 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<
|
||||||
|
|
||||||
/// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a
|
/// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a
|
||||||
/// post-monomorphization error is encountered during a collection step.
|
/// post-monomorphization error is encountered during a collection step.
|
||||||
#[instrument(skip(tcx, visited, recursion_depths, recursion_limit, usage_map), level = "debug")]
|
///
|
||||||
|
/// `mode` determined whether we are scanning for [used items][CollectionMode::UsedItems]
|
||||||
|
/// or [mentioned items][CollectionMode::MentionedItems].
|
||||||
|
#[instrument(skip(tcx, state, recursion_depths, recursion_limit), level = "debug")]
|
||||||
fn collect_items_rec<'tcx>(
|
fn collect_items_rec<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
starting_item: Spanned<MonoItem<'tcx>>,
|
starting_item: Spanned<MonoItem<'tcx>>,
|
||||||
visited: MTLockRef<'_, FxHashSet<MonoItem<'tcx>>>,
|
state: LRef<'_, SharedState<'tcx>>,
|
||||||
recursion_depths: &mut DefIdMap<usize>,
|
recursion_depths: &mut DefIdMap<usize>,
|
||||||
recursion_limit: Limit,
|
recursion_limit: Limit,
|
||||||
usage_map: MTLockRef<'_, UsageMap<'tcx>>,
|
mode: CollectionMode,
|
||||||
) {
|
) {
|
||||||
if !visited.lock_mut().insert(starting_item.node) {
|
if mode == CollectionMode::UsedItems {
|
||||||
// We've been here already, no need to search again.
|
if !state.visited.lock_mut().insert(starting_item.node) {
|
||||||
return;
|
// 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;
|
let recursion_depth_reset;
|
||||||
|
|
||||||
// Post-monomorphization errors MVP
|
// 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.
|
// 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();
|
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 {
|
match starting_item.node {
|
||||||
MonoItem::Static(def_id) => {
|
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;
|
recursion_depth_reset = None;
|
||||||
|
|
||||||
if let Ok(alloc) = tcx.eval_static_initializer(def_id) {
|
// Statics always get evaluted (which is possible because they can't be generic), so for
|
||||||
for &prov in alloc.inner().provenance().ptrs().values() {
|
// `MentionedItems` collection there's nothing to do here.
|
||||||
collect_alloc(tcx, prov.alloc_id(), &mut used_items);
|
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) {
|
// mentioned_items stays empty since there's no codegen for statics. statics don't get
|
||||||
used_items.push(respan(
|
// optimized, and if they did then the const-eval interpreter would have to worry about
|
||||||
starting_item.span,
|
// mentioned_items.
|
||||||
MonoItem::Fn(Instance {
|
|
||||||
def: InstanceDef::ThreadLocalShim(def_id),
|
|
||||||
args: GenericArgs::empty(),
|
|
||||||
}),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MonoItem::Fn(instance) => {
|
MonoItem::Fn(instance) => {
|
||||||
// Sanity check whether this ended up being collected accidentally
|
// Sanity check whether this ended up being collected accidentally
|
||||||
|
@ -420,10 +500,20 @@ fn collect_items_rec<'tcx>(
|
||||||
check_type_length_limit(tcx, instance);
|
check_type_length_limit(tcx, instance);
|
||||||
|
|
||||||
rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
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) => {
|
MonoItem::GlobalAsm(item_id) => {
|
||||||
|
assert!(
|
||||||
|
mode == CollectionMode::UsedItems,
|
||||||
|
"should never encounter global_asm when collecting mentioned items"
|
||||||
|
);
|
||||||
recursion_depth_reset = None;
|
recursion_depth_reset = None;
|
||||||
|
|
||||||
let item = tcx.hir().item(item_id);
|
let item = tcx.hir().item(item_id);
|
||||||
|
@ -459,8 +549,10 @@ fn collect_items_rec<'tcx>(
|
||||||
} else {
|
} else {
|
||||||
span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type")
|
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
|
// Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
|
||||||
// mono item graph.
|
// mono item graph.
|
||||||
|
@ -474,10 +566,41 @@ fn collect_items_rec<'tcx>(
|
||||||
formatted_item,
|
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 {
|
if mode == CollectionMode::MentionedItems {
|
||||||
collect_items_rec(tcx, used_item, visited, recursion_depths, recursion_limit, usage_map);
|
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 {
|
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> {
|
struct MirUsedCollector<'a, 'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &'a mir::Body<'tcx>,
|
body: &'a mir::Body<'tcx>,
|
||||||
output: &'a mut MonoItems<'tcx>,
|
used_items: &'a mut MonoItems<'tcx>,
|
||||||
instance: Instance<'tcx>,
|
instance: Instance<'tcx>,
|
||||||
/// Spans for move size lints already emitted. Helps avoid duplicate lints.
|
/// Spans for move size lints already emitted. Helps avoid duplicate lints.
|
||||||
move_size_spans: Vec<Span>,
|
move_size_spans: Vec<Span>,
|
||||||
|
@ -735,6 +858,31 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
self.move_size_spans.push(span);
|
self.move_size_spans.push(span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates a *not yet monomorphized* constant.
|
||||||
|
fn eval_constant(
|
||||||
|
&mut self,
|
||||||
|
constant: &mir::ConstOperand<'tcx>,
|
||||||
|
) -> Option<mir::ConstValue<'tcx>> {
|
||||||
|
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> {
|
impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
||||||
|
@ -769,7 +917,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
||||||
target_ty,
|
target_ty,
|
||||||
source_ty,
|
source_ty,
|
||||||
span,
|
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 = operand.ty(self.body, self.tcx);
|
||||||
let fn_ty = self.monomorphize(fn_ty);
|
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::Rvalue::Cast(
|
||||||
mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)),
|
mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)),
|
||||||
|
@ -798,7 +946,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
||||||
ty::ClosureKind::FnOnce,
|
ty::ClosureKind::FnOnce,
|
||||||
);
|
);
|
||||||
if should_codegen_locally(self.tcx, &instance) {
|
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!(),
|
_ => bug!(),
|
||||||
|
@ -809,7 +957,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
||||||
let instance = Instance::mono(self.tcx, def_id);
|
let instance = Instance::mono(self.tcx, def_id);
|
||||||
if should_codegen_locally(self.tcx, &instance) {
|
if should_codegen_locally(self.tcx, &instance) {
|
||||||
trace!("collecting thread-local static {:?}", def_id);
|
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 */ }
|
_ => { /* 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.
|
/// to ensure that the constant evaluates successfully and walk the result.
|
||||||
#[instrument(skip(self), level = "debug")]
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) {
|
fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) {
|
||||||
let const_ = self.monomorphize(constant.const_);
|
// No `super_constant` as we don't care about `visit_ty`/`visit_ty_const`.
|
||||||
let param_env = ty::ParamEnv::reveal_all();
|
let Some(val) = self.eval_constant(constant) else { return };
|
||||||
// Evaluate the constant. This makes const eval failure a collection-time error (rather than
|
collect_const_value(self.tcx, val, self.used_items);
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
|
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 push_mono_lang_item = |this: &mut Self, lang_item: LangItem| {
|
||||||
let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source)));
|
let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source)));
|
||||||
if should_codegen_locally(tcx, &instance) {
|
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 = func.ty(self.body, tcx);
|
||||||
let callee_ty = self.monomorphize(callee_ty);
|
let callee_ty = self.monomorphize(callee_ty);
|
||||||
self.check_fn_args_move_size(callee_ty, args, *fn_span, location);
|
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, .. } => {
|
mir::TerminatorKind::Drop { ref place, .. } => {
|
||||||
let ty = place.ty(self.body, self.tcx).ty;
|
let ty = place.ty(self.body, self.tcx).ty;
|
||||||
let ty = self.monomorphize(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, .. } => {
|
mir::TerminatorKind::InlineAsm { ref operands, .. } => {
|
||||||
for op in operands {
|
for op in operands {
|
||||||
match *op {
|
match *op {
|
||||||
mir::InlineAsmOperand::SymFn { ref value } => {
|
mir::InlineAsmOperand::SymFn { ref value } => {
|
||||||
let fn_ty = self.monomorphize(value.const_.ty());
|
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 } => {
|
mir::InlineAsmOperand::SymStatic { def_id } => {
|
||||||
let instance = Instance::mono(self.tcx, def_id);
|
let instance = Instance::mono(self.tcx, def_id);
|
||||||
if should_codegen_locally(self.tcx, &instance) {
|
if should_codegen_locally(self.tcx, &instance) {
|
||||||
trace!("collecting asm sym static {:?}", def_id);
|
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> {
|
struct RootCollector<'a, 'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
mode: MonoItemCollectionMode,
|
strategy: MonoItemCollectionStrategy,
|
||||||
output: &'a mut MonoItems<'tcx>,
|
output: &'a mut MonoItems<'tcx>,
|
||||||
entry_fn: Option<(DefId, EntryFnType)>,
|
entry_fn: Option<(DefId, EntryFnType)>,
|
||||||
}
|
}
|
||||||
|
@ -1248,7 +1379,7 @@ impl<'v> RootCollector<'_, 'v> {
|
||||||
fn process_item(&mut self, id: hir::ItemId) {
|
fn process_item(&mut self, id: hir::ItemId) {
|
||||||
match self.tcx.def_kind(id.owner_id) {
|
match self.tcx.def_kind(id.owner_id) {
|
||||||
DefKind::Enum | DefKind::Struct | DefKind::Union => {
|
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
|
&& self.tcx.generics_of(id.owner_id).count() == 0
|
||||||
{
|
{
|
||||||
debug!("RootCollector: ADT drop-glue for `{id:?}`",);
|
debug!("RootCollector: ADT drop-glue for `{id:?}`",);
|
||||||
|
@ -1279,7 +1410,7 @@ impl<'v> RootCollector<'_, 'v> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DefKind::Impl { .. } => {
|
DefKind::Impl { .. } => {
|
||||||
if self.mode == MonoItemCollectionMode::Eager {
|
if self.strategy == MonoItemCollectionStrategy::Eager {
|
||||||
create_mono_items_for_default_impls(self.tcx, id, self.output);
|
create_mono_items_for_default_impls(self.tcx, id, self.output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1298,9 +1429,9 @@ impl<'v> RootCollector<'_, 'v> {
|
||||||
|
|
||||||
fn is_root(&self, def_id: LocalDefId) -> bool {
|
fn is_root(&self, def_id: LocalDefId) -> bool {
|
||||||
!self.tcx.generics_of(def_id).requires_monomorphization(self.tcx)
|
!self.tcx.generics_of(def_id).requires_monomorphization(self.tcx)
|
||||||
&& match self.mode {
|
&& match self.strategy {
|
||||||
MonoItemCollectionMode::Eager => true,
|
MonoItemCollectionStrategy::Eager => true,
|
||||||
MonoItemCollectionMode::Lazy => {
|
MonoItemCollectionStrategy::Lazy => {
|
||||||
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
|
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
|
||||||
|| self.tcx.is_reachable_non_generic(def_id)
|
|| self.tcx.is_reachable_non_generic(def_id)
|
||||||
|| self
|
|| self
|
||||||
|
@ -1497,26 +1628,67 @@ fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec<DefId> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scans the MIR in order to find function calls, closures, and drop-glue.
|
/// 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>,
|
tcx: TyCtxt<'tcx>,
|
||||||
instance: Instance<'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);
|
let body = tcx.instance_mir(instance.def);
|
||||||
|
let mut collector = MirUsedCollector {
|
||||||
// Here we rely on the visitor also visiting `required_consts`, so that we evaluate them
|
|
||||||
// and abort compilation if any of them errors.
|
|
||||||
MirUsedCollector {
|
|
||||||
tcx,
|
tcx,
|
||||||
body: body,
|
body,
|
||||||
output,
|
used_items,
|
||||||
instance,
|
instance,
|
||||||
move_size_spans: vec![],
|
move_size_spans: vec![],
|
||||||
visiting_call_terminator: false,
|
visiting_call_terminator: false,
|
||||||
skip_move_check_fns: None,
|
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")]
|
#[instrument(skip(tcx, output), level = "debug")]
|
||||||
|
|
|
@ -117,7 +117,7 @@ use rustc_session::CodegenUnits;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
|
||||||
use crate::collector::UsageMap;
|
use crate::collector::UsageMap;
|
||||||
use crate::collector::{self, MonoItemCollectionMode};
|
use crate::collector::{self, MonoItemCollectionStrategy};
|
||||||
use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode};
|
use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode};
|
||||||
|
|
||||||
struct PartitioningCx<'a, 'tcx> {
|
struct PartitioningCx<'a, 'tcx> {
|
||||||
|
@ -1087,30 +1087,30 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) {
|
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) => {
|
Some(ref s) => {
|
||||||
let mode = s.to_lowercase();
|
let mode = s.to_lowercase();
|
||||||
let mode = mode.trim();
|
let mode = mode.trim();
|
||||||
if mode == "eager" {
|
if mode == "eager" {
|
||||||
MonoItemCollectionMode::Eager
|
MonoItemCollectionStrategy::Eager
|
||||||
} else {
|
} else {
|
||||||
if mode != "lazy" {
|
if mode != "lazy" {
|
||||||
tcx.dcx().emit_warn(UnknownCguCollectionMode { mode });
|
tcx.dcx().emit_warn(UnknownCguCollectionMode { mode });
|
||||||
}
|
}
|
||||||
|
|
||||||
MonoItemCollectionMode::Lazy
|
MonoItemCollectionStrategy::Lazy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if tcx.sess.link_dead_code() {
|
if tcx.sess.link_dead_code() {
|
||||||
MonoItemCollectionMode::Eager
|
MonoItemCollectionStrategy::Eager
|
||||||
} else {
|
} 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),
|
// 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.
|
// then we stop here. This way codegen does not have to worry about failing constants.
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
error[E0080]: evaluation of `Fail::<i32>::C` failed
|
error[E0080]: evaluation of `Fail::<i32>::C` failed
|
||||||
--> $DIR/collect-in-dead-drop.rs:12:19
|
--> $DIR/collect-in-dead-drop.rs:9:19
|
||||||
|
|
|
|
||||||
LL | const C: () = panic!();
|
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: 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
|
note: erroneous constant encountered
|
||||||
--> $DIR/collect-in-dead-drop.rs:19:17
|
--> $DIR/collect-in-dead-drop.rs:16:17
|
||||||
|
|
|
|
||||||
LL | let _ = Fail::<T>::C;
|
LL | let _ = Fail::<T>::C;
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: the above error was encountered while instantiating `fn <Fail<i32> 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`.
|
|
@ -1,15 +1,12 @@
|
||||||
//@revisions: noopt opt
|
//@revisions: noopt opt
|
||||||
//@[noopt] build-fail
|
//@ build-fail
|
||||||
//@[opt] compile-flags: -O
|
//@[opt] compile-flags: -O
|
||||||
//FIXME: `opt` revision currently does not stop with an error due to
|
|
||||||
//<https://github.com/rust-lang/rust/issues/107503>.
|
|
||||||
//@[opt] build-pass
|
|
||||||
//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is
|
//! 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)
|
//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090)
|
||||||
|
|
||||||
struct Fail<T>(T);
|
struct Fail<T>(T);
|
||||||
impl<T> Fail<T> {
|
impl<T> Fail<T> {
|
||||||
const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::<i32>::C` failed
|
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is not actually called, but is mentioned implicitly as destructor in dead code in a
|
// This function is not actually called, but is mentioned implicitly as destructor in dead code in a
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
error[E0080]: evaluation of `Fail::<i32>::C` failed
|
error[E0080]: evaluation of `Fail::<i32>::C` failed
|
||||||
--> $DIR/collect-in-dead-fn.rs:12:19
|
--> $DIR/collect-in-dead-fn.rs:9:19
|
||||||
|
|
|
|
||||||
LL | const C: () = panic!();
|
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: 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
|
note: erroneous constant encountered
|
||||||
--> $DIR/collect-in-dead-fn.rs:22:17
|
--> $DIR/collect-in-dead-fn.rs:19:17
|
||||||
|
|
|
|
||||||
LL | let _ = Fail::<T>::C;
|
LL | let _ = Fail::<T>::C;
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||||
--> $DIR/collect-in-dead-fn.rs:29:9
|
--> $DIR/collect-in-dead-fn.rs:26:9
|
||||||
|
|
|
|
||||||
LL | not_called::<T>();
|
LL | not_called::<T>();
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||||
|
--> $DIR/collect-in-dead-fn.rs:26:9
|
||||||
|
|
|
||||||
|
LL | not_called::<T>();
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0080`.
|
|
@ -1,15 +1,12 @@
|
||||||
//@revisions: noopt opt
|
//@revisions: noopt opt
|
||||||
//@[noopt] build-fail
|
//@ build-fail
|
||||||
//@[opt] compile-flags: -O
|
//@[opt] compile-flags: -O
|
||||||
//FIXME: `opt` revision currently does not stop with an error due to
|
|
||||||
//<https://github.com/rust-lang/rust/issues/107503>.
|
|
||||||
//@[opt] build-pass
|
|
||||||
//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is
|
//! 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)
|
//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090)
|
||||||
|
|
||||||
struct Fail<T>(T);
|
struct Fail<T>(T);
|
||||||
impl<T> Fail<T> {
|
impl<T> Fail<T> {
|
||||||
const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::<i32>::C` failed
|
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is not actually called, but it is mentioned in dead code in a function that is
|
// This function is not actually called, but it is mentioned in dead code in a function that is
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
error[E0080]: evaluation of `Late::<i32>::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::<i32>::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`.
|
|
@ -0,0 +1,20 @@
|
||||||
|
error[E0080]: evaluation of `Late::<i32>::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::<i32>::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`.
|
|
@ -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>(T);
|
||||||
|
impl<T> Late<T> {
|
||||||
|
const FAIL: () = panic!(); //~ERROR evaluation of `Late::<i32>::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<T>() {
|
||||||
|
if false {
|
||||||
|
let _ = Late::<T>::FNPTR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn called<T>() {
|
||||||
|
if false {
|
||||||
|
not_called::<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
called::<i32>();
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||||
|
--> $DIR/collect-in-dead-fnptr.rs:26:28
|
||||||
|
|
|
||||||
|
LL | let _fnptr: fn() = not_called::<T>;
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0080`.
|
|
@ -0,0 +1,23 @@
|
||||||
|
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||||
|
--> $DIR/collect-in-dead-fnptr.rs:26:28
|
||||||
|
|
|
||||||
|
LL | let _fnptr: fn() = not_called::<T>;
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0080`.
|
32
tests/ui/consts/required-consts/collect-in-dead-fnptr.rs
Normal file
32
tests/ui/consts/required-consts/collect-in-dead-fnptr.rs
Normal file
|
@ -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>(T);
|
||||||
|
impl<T> Fail<T> {
|
||||||
|
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::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<T>() {
|
||||||
|
if false {
|
||||||
|
let _ = Fail::<T>::C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn called<T>() {
|
||||||
|
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::<T>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
called::<i32>();
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
error[E0080]: evaluation of `Fail::<i32>::C` failed
|
error[E0080]: evaluation of `Fail::<i32>::C` failed
|
||||||
--> $DIR/collect-in-dead-move.rs:12:19
|
--> $DIR/collect-in-dead-move.rs:9:19
|
||||||
|
|
|
|
||||||
LL | const C: () = panic!();
|
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: 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
|
note: erroneous constant encountered
|
||||||
--> $DIR/collect-in-dead-move.rs:19:17
|
--> $DIR/collect-in-dead-move.rs:16:17
|
||||||
|
|
|
|
||||||
LL | let _ = Fail::<T>::C;
|
LL | let _ = Fail::<T>::C;
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: the above error was encountered while instantiating `fn <Fail<i32> 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`.
|
|
@ -1,15 +1,12 @@
|
||||||
//@revisions: noopt opt
|
//@revisions: noopt opt
|
||||||
//@[noopt] build-fail
|
//@ build-fail
|
||||||
//@[opt] compile-flags: -O
|
//@[opt] compile-flags: -O
|
||||||
//FIXME: `opt` revision currently does not stop with an error due to
|
|
||||||
//<https://github.com/rust-lang/rust/issues/107503>.
|
|
||||||
//@[opt] build-pass
|
|
||||||
//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is
|
//! 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)
|
//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090)
|
||||||
|
|
||||||
struct Fail<T>(T);
|
struct Fail<T>(T);
|
||||||
impl<T> Fail<T> {
|
impl<T> Fail<T> {
|
||||||
const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::<i32>::C` failed
|
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is not actually called, but is mentioned implicitly as destructor in dead code in a
|
// This function is not actually called, but is mentioned implicitly as destructor in dead code in a
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue