diff options
Diffstat (limited to 'compiler/rustc_monomorphize/src')
| -rw-r--r-- | compiler/rustc_monomorphize/src/collector.rs | 228 | ||||
| -rw-r--r-- | compiler/rustc_monomorphize/src/errors.rs | 32 | ||||
| -rw-r--r-- | compiler/rustc_monomorphize/src/lib.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_monomorphize/src/mono_checks/abi_check.rs | 172 | ||||
| -rw-r--r-- | compiler/rustc_monomorphize/src/mono_checks/mod.rs | 23 | ||||
| -rw-r--r-- | compiler/rustc_monomorphize/src/mono_checks/move_check.rs (renamed from compiler/rustc_monomorphize/src/collector/move_check.rs) | 115 | ||||
| -rw-r--r-- | compiler/rustc_monomorphize/src/partitioning.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_monomorphize/src/util.rs | 10 |
8 files changed, 459 insertions, 145 deletions
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 8df6e63deeb..5efe7ffc7b9 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -205,11 +205,9 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. -mod move_check; - use std::path::PathBuf; -use move_check::MoveCheckState; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{LRef, MTLock, par_for_each_in}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; @@ -218,7 +216,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar}; -use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::mir::mono::{CollectionMode, InstantiationMode, MonoItem}; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Location, MentionedItem, traversal}; use rustc_middle::query::TyCtxtAt; @@ -226,17 +224,16 @@ use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::print::{shrunk_instance_name, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, AssocKind, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, - TypeFoldable, TypeVisitableExt, VtblEntry, + self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, VtblEntry, }; use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; use rustc_session::Limit; use rustc_session::config::EntryFnType; use rustc_span::source_map::{Spanned, dummy_spanned, respan}; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Span}; -use rustc_target::abi::Size; use tracing::{debug, instrument, trace}; use crate::errors::{self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit}; @@ -247,16 +244,6 @@ pub(crate) enum MonoItemCollectionStrategy { Lazy, } -pub(crate) struct UsageMap<'tcx> { - // Maps every mono item to the mono items used by it. - used_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>, - - // Maps every mono item to the mono items that use it. - user_map: UnordMap<MonoItem<'tcx>, Vec<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. @@ -268,22 +255,12 @@ struct SharedState<'tcx> { 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, +pub(crate) struct UsageMap<'tcx> { + // Maps every mono item to the mono items used by it. + used_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>, + + // Maps every mono item to the mono items that use it. + user_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>, } impl<'tcx> UsageMap<'tcx> { @@ -291,19 +268,15 @@ impl<'tcx> UsageMap<'tcx> { UsageMap { used_map: Default::default(), user_map: Default::default() } } - fn record_used<'a>( - &mut self, - user_item: MonoItem<'tcx>, - used_items: &'a [Spanned<MonoItem<'tcx>>], - ) where + fn record_used<'a>(&mut self, user_item: MonoItem<'tcx>, used_items: &'a MonoItems<'tcx>) + where 'tcx: 'a, { - let used_items: Vec<_> = used_items.iter().map(|item| item.node).collect(); - for &used_item in used_items.iter() { + for used_item in used_items.items() { self.user_map.entry(used_item).or_default().push(user_item); } - assert!(self.used_map.insert(user_item, used_items).is_none()); + assert!(self.used_map.insert(user_item, used_items.items().collect()).is_none()); } pub(crate) fn get_user_items(&self, item: MonoItem<'tcx>) -> &[MonoItem<'tcx>] { @@ -329,6 +302,52 @@ impl<'tcx> UsageMap<'tcx> { } } +struct MonoItems<'tcx> { + // We want a set of MonoItem + Span where trying to re-insert a MonoItem with a different Span + // is ignored. Map does that, but it looks odd. + items: FxIndexMap<MonoItem<'tcx>, Span>, +} + +impl<'tcx> MonoItems<'tcx> { + fn new() -> Self { + Self { items: FxIndexMap::default() } + } + + fn is_empty(&self) -> bool { + self.items.is_empty() + } + + fn push(&mut self, item: Spanned<MonoItem<'tcx>>) { + // Insert only if the entry does not exist. A normal insert would stomp the first span that + // got inserted. + self.items.entry(item.node).or_insert(item.span); + } + + fn items(&self) -> impl Iterator<Item = MonoItem<'tcx>> + '_ { + self.items.keys().cloned() + } +} + +impl<'tcx> IntoIterator for MonoItems<'tcx> { + type Item = Spanned<MonoItem<'tcx>>; + type IntoIter = impl Iterator<Item = Spanned<MonoItem<'tcx>>>; + + fn into_iter(self) -> Self::IntoIter { + self.items.into_iter().map(|(item, span)| respan(span, item)) + } +} + +impl<'tcx> Extend<Spanned<MonoItem<'tcx>>> for MonoItems<'tcx> { + fn extend<I>(&mut self, iter: I) + where + I: IntoIterator<Item = Spanned<MonoItem<'tcx>>>, + { + for item in iter { + self.push(item) + } + } +} + /// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a /// post-monomorphization error is encountered during a collection step. /// @@ -408,7 +427,7 @@ fn collect_items_rec<'tcx>( 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()); + let ty = instance.ty(tcx, ty::TypingEnv::fully_monomorphized()); visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); } @@ -447,13 +466,9 @@ fn collect_items_rec<'tcx>( )); rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_items_of_instance( - tcx, - instance, - &mut used_items, - &mut mentioned_items, - mode, - ) + let (used, mentioned) = tcx.items_of_instance((instance, mode)); + used_items.extend(used.into_iter().copied()); + mentioned_items.extend(mentioned.into_iter().copied()); }); } MonoItem::GlobalAsm(item_id) => { @@ -611,8 +626,6 @@ struct MirUsedCollector<'a, 'tcx> { /// Note that this contains *not-monomorphized* items! used_mentioned_items: &'a mut UnordSet<MentionedItem<'tcx>>, instance: Instance<'tcx>, - visiting_call_terminator: bool, - move_check: move_check::MoveCheckState, } impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { @@ -623,7 +636,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { trace!("monomorphize: self.instance={:?}", self.instance); self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, - ty::ParamEnv::reveal_all(), + ty::TypingEnv::fully_monomorphized(), ty::EarlyBinder::bind(value), ) } @@ -634,12 +647,11 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { 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) { + match const_.eval(self.tcx, ty::TypingEnv::fully_monomorphized(), constant.span) { Ok(v) => Some(v), Err(ErrorHandled::TooGeneric(..)) => span_bug!( constant.span, @@ -759,13 +771,12 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { }; match terminator.kind { - mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } - | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => { + mir::TerminatorKind::Call { ref func, .. } + | mir::TerminatorKind::TailCall { ref func, .. } => { let callee_ty = func.ty(self.body, tcx); // *Before* monomorphizing, record that we already handled this mention. self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); let callee_ty = self.monomorphize(callee_ty); - self.check_fn_args_move_size(callee_ty, args, *fn_span, location); visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) } mir::TerminatorKind::Drop { ref place, .. } => { @@ -825,14 +836,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { push_mono_lang_item(self, reason.lang_item()); } - self.visiting_call_terminator = matches!(terminator.kind, mir::TerminatorKind::Call { .. }); self.super_terminator(terminator, location); - self.visiting_call_terminator = false; - } - - fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) { - self.super_operand(operand, location); - self.check_operand_move_size(operand, location); } } @@ -858,9 +862,20 @@ fn visit_fn_use<'tcx>( ) { if let ty::FnDef(def_id, args) = *ty.kind() { let instance = if is_direct_call { - ty::Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args, source) + ty::Instance::expect_resolve( + tcx, + ty::TypingEnv::fully_monomorphized(), + def_id, + args, + source, + ) } else { - match ty::Instance::resolve_for_fn_ptr(tcx, ty::ParamEnv::reveal_all(), def_id, args) { + match ty::Instance::resolve_for_fn_ptr( + tcx, + ty::TypingEnv::fully_monomorphized(), + def_id, + args, + ) { Some(instance) => instance, _ => bug!("failed to resolve instance for {ty}"), } @@ -1019,12 +1034,12 @@ fn find_vtable_types_for_unsizing<'tcx>( target_ty: Ty<'tcx>, ) -> (Ty<'tcx>, Ty<'tcx>) { let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| { - let param_env = ty::ParamEnv::reveal_all(); + let typing_env = ty::TypingEnv::fully_monomorphized(); let type_has_metadata = |ty: Ty<'tcx>| -> bool { - if ty.is_sized(tcx.tcx, param_env) { + if ty.is_sized(tcx.tcx, typing_env) { return false; } - let tail = tcx.struct_tail_for_codegen(ty, param_env); + let tail = tcx.struct_tail_for_codegen(ty, typing_env); match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, @@ -1034,7 +1049,7 @@ fn find_vtable_types_for_unsizing<'tcx>( if type_has_metadata(inner_source) { (inner_source, inner_target) } else { - tcx.struct_lockstep_tails_for_codegen(inner_source, inner_target, param_env) + tcx.struct_lockstep_tails_for_codegen(inner_source, inner_target, typing_env) } }; @@ -1182,31 +1197,18 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt } } -fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> { - for impl_def_id in tcx.inherent_impls(def_id) { - if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( - tcx, - fn_ident, - AssocKind::Fn, - def_id, - ) { - return Some(new.def_id); - } - } - None -} - /// Scans the MIR in order to find function calls, closures, and drop-glue. /// /// Anything that's found is added to `output`. Furthermore the "mentioned items" of the MIR are returned. -#[instrument(skip(tcx, used_items, mentioned_items), level = "debug")] +#[instrument(skip(tcx), level = "debug")] fn collect_items_of_instance<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, - used_items: &mut MonoItems<'tcx>, - mentioned_items: &mut MonoItems<'tcx>, mode: CollectionMode, -) { +) -> (MonoItems<'tcx>, MonoItems<'tcx>) { + // This item is getting monomorphized, do mono-time checks. + tcx.ensure().check_mono_item(instance); + let body = tcx.instance_mir(instance.def); // Naively, in "used" collection mode, all functions get added to *both* `used_items` and // `mentioned_items`. Mentioned items processing will then notice that they have already been @@ -1218,15 +1220,15 @@ fn collect_items_of_instance<'tcx>( // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already // added to `used_items` in a hash set, which can efficiently query in the // `body.mentioned_items` loop below without even having to monomorphize the item. + let mut used_items = MonoItems::new(); + let mut mentioned_items = MonoItems::new(); let mut used_mentioned_items = Default::default(); let mut collector = MirUsedCollector { tcx, body, - used_items, + used_items: &mut used_items, used_mentioned_items: &mut used_mentioned_items, instance, - visiting_call_terminator: false, - move_check: MoveCheckState::new(), }; if mode == CollectionMode::UsedItems { @@ -1239,7 +1241,7 @@ fn collect_items_of_instance<'tcx>( // them errors. for const_op in body.required_consts() { if let Some(val) = collector.eval_constant(const_op) { - collect_const_value(tcx, val, mentioned_items); + collect_const_value(tcx, val, &mut mentioned_items); } } @@ -1248,9 +1250,23 @@ fn collect_items_of_instance<'tcx>( for item in body.mentioned_items() { if !collector.used_mentioned_items.contains(&item.node) { let item_mono = collector.monomorphize(item.node); - visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); + visit_mentioned_item(tcx, &item_mono, item.span, &mut mentioned_items); } } + + (used_items, mentioned_items) +} + +fn items_of_instance<'tcx>( + tcx: TyCtxt<'tcx>, + (instance, mode): (Instance<'tcx>, CollectionMode), +) -> (&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]) { + let (used_items, mentioned_items) = collect_items_of_instance(tcx, instance, mode); + + let used_items = tcx.arena.alloc_from_iter(used_items); + let mentioned_items = tcx.arena.alloc_from_iter(mentioned_items); + + (used_items, mentioned_items) } /// `item` must be already monomorphized. @@ -1264,8 +1280,13 @@ fn visit_mentioned_item<'tcx>( match *item { MentionedItem::Fn(ty) => { if let ty::FnDef(def_id, args) = *ty.kind() { - let instance = - Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args, span); + let instance = Instance::expect_resolve( + tcx, + ty::TypingEnv::fully_monomorphized(), + def_id, + args, + span, + ); // `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 @@ -1331,7 +1352,7 @@ fn collect_const_value<'tcx>( #[instrument(skip(tcx, mode), level = "debug")] fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec<MonoItem<'_>> { debug!("collecting roots"); - let mut roots = Vec::new(); + let mut roots = MonoItems::new(); { let entry_fn = tcx.entry_fn(()); @@ -1481,13 +1502,13 @@ impl<'v> RootCollector<'_, 'v> { // regions must appear in the argument // listing. let main_ret_ty = self.tcx.normalize_erasing_regions( - ty::ParamEnv::reveal_all(), + ty::TypingEnv::fully_monomorphized(), main_ret_ty.no_bound_vars().unwrap(), ); let start_instance = Instance::expect_resolve( self.tcx, - ty::ParamEnv::reveal_all(), + ty::TypingEnv::fully_monomorphized(), start_def_id, self.tcx.mk_args(&[main_ret_ty.into()]), DUMMY_SP, @@ -1535,7 +1556,7 @@ fn create_mono_items_for_default_impls<'tcx>( // Unlike 'lazy' monomorphization that begins by collecting items transitively // called by `main` or other global items, when eagerly monomorphizing impl // items, we never actually check that the predicates of this impl are satisfied - // in a empty reveal-all param env (i.e. with no assumptions). + // in a empty param env (i.e. with no assumptions). // // Even though this impl has no type or const generic parameters, because we don't // consider higher-ranked predicates such as `for<'a> &'a mut [u8]: Copy` to @@ -1545,8 +1566,8 @@ fn create_mono_items_for_default_impls<'tcx>( return; } - let param_env = ty::ParamEnv::reveal_all(); - let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref); + let typing_env = ty::TypingEnv::fully_monomorphized(); + let trait_ref = tcx.normalize_erasing_regions(typing_env, trait_ref); let overridden_methods = tcx.impl_item_implementor_ids(item.owner_id); for method in tcx.provided_trait_methods(trait_ref.def_id) { if overridden_methods.contains_key(&method.def_id) { @@ -1561,7 +1582,7 @@ fn create_mono_items_for_default_impls<'tcx>( // only has lifetime generic parameters. This is validated by calling // `own_requires_monomorphization` on both the impl and method. let args = trait_ref.args.extend_to(tcx, method.def_id, only_region_params); - let instance = ty::Instance::expect_resolve(tcx, param_env, method.def_id, args, DUMMY_SP); + let instance = ty::Instance::expect_resolve(tcx, typing_env, method.def_id, args, DUMMY_SP); let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); if mono_item.node.is_instantiable(tcx) && tcx.should_codegen_locally(instance) { @@ -1623,4 +1644,5 @@ pub(crate) fn collect_crate_mono_items<'tcx>( pub(crate) fn provide(providers: &mut Providers) { providers.hooks.should_codegen_locally = should_codegen_locally; + providers.items_of_instance = items_of_instance; } diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index d5fae6e23cb..02865cad302 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -92,3 +92,35 @@ pub(crate) struct StartNotFound; pub(crate) struct UnknownCguCollectionMode<'a> { pub mode: &'a str, } + +#[derive(LintDiagnostic)] +#[diag(monomorphize_abi_error_disabled_vector_type_def)] +#[help] +pub(crate) struct AbiErrorDisabledVectorTypeDef<'a> { + #[label] + pub span: Span, + pub required_feature: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(monomorphize_abi_error_disabled_vector_type_call)] +#[help] +pub(crate) struct AbiErrorDisabledVectorTypeCall<'a> { + #[label] + pub span: Span, + pub required_feature: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(monomorphize_abi_error_unsupported_vector_type_def)] +pub(crate) struct AbiErrorUnsupportedVectorTypeDef { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(monomorphize_abi_error_unsupported_vector_type_call)] +pub(crate) struct AbiErrorUnsupportedVectorTypeCall { + #[label] + pub span: Span, +} diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index e92e6978d0f..0f08930fb4c 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -2,6 +2,7 @@ #![feature(array_windows)] #![feature(file_buffered)] #![feature(if_let_guard)] +#![feature(impl_trait_in_assoc_type)] #![feature(let_chains)] #![warn(unreachable_pub)] // tidy-alphabetical-end @@ -16,6 +17,7 @@ use rustc_span::ErrorGuaranteed; mod collector; mod errors; +mod mono_checks; mod partitioning; mod polymorphize; mod util; @@ -33,7 +35,9 @@ fn custom_coerce_unsize_info<'tcx>( [source_ty, target_ty], ); - match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) { + match tcx + .codegen_select_candidate(ty::TypingEnv::fully_monomorphized().as_query_input(trait_ref)) + { Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { impl_def_id, .. @@ -47,4 +51,5 @@ fn custom_coerce_unsize_info<'tcx>( pub fn provide(providers: &mut Providers) { partitioning::provide(providers); polymorphize::provide(providers); + mono_checks::provide(providers); } diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs new file mode 100644 index 00000000000..30e634d8252 --- /dev/null +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -0,0 +1,172 @@ +//! This module ensures that if a function's ABI requires a particular target feature, +//! that target feature is enabled both on the callee and all callers. +use rustc_hir::CRATE_HIR_ID; +use rustc_middle::mir::{self, traversal}; +use rustc_middle::ty::inherent::*; +use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt}; +use rustc_session::lint::builtin::ABI_UNSUPPORTED_VECTOR_TYPES; +use rustc_span::def_id::DefId; +use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_target::abi::call::{FnAbi, PassMode}; +use rustc_target::abi::{BackendRepr, RegKind}; + +use crate::errors::{ + AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef, + AbiErrorUnsupportedVectorTypeCall, AbiErrorUnsupportedVectorTypeDef, +}; + +fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool { + match mode { + PassMode::Ignore | PassMode::Indirect { .. } => false, + PassMode::Cast { pad_i32: _, cast } => { + cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector)) + || cast.rest.unit.kind == RegKind::Vector + } + PassMode::Direct(..) | PassMode::Pair(..) => matches!(repr, BackendRepr::Vector { .. }), + } +} + +/// Checks whether a certain function ABI is compatible with the target features currently enabled +/// for a certain function. +/// If not, `emit_err` is called, with `Some(feature)` if a certain feature should be enabled and +/// with `None` if no feature is known that would make the ABI compatible. +fn do_check_abi<'tcx>( + tcx: TyCtxt<'tcx>, + abi: &FnAbi<'tcx, Ty<'tcx>>, + target_feature_def: DefId, + mut emit_err: impl FnMut(Option<&'static str>), +) { + let feature_def = tcx.sess.target.features_for_correct_vector_abi(); + let codegen_attrs = tcx.codegen_fn_attrs(target_feature_def); + for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) { + let size = arg_abi.layout.size; + if uses_vector_registers(&arg_abi.mode, &arg_abi.layout.backend_repr) { + // Find the first feature that provides at least this vector size. + let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) { + Some((_, feature)) => feature, + None => { + emit_err(None); + continue; + } + }; + let feature_sym = Symbol::intern(feature); + if !tcx.sess.unstable_target_features.contains(&feature_sym) + && !codegen_attrs.target_features.iter().any(|x| x.name == feature_sym) + { + emit_err(Some(&feature)); + } + } + } +} + +/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments +/// or return values for which the corresponding target feature is not enabled. +fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { + let typing_env = ty::TypingEnv::fully_monomorphized(); + let Ok(abi) = tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty()))) + else { + // An error will be reported during codegen if we cannot determine the ABI of this + // function. + return; + }; + do_check_abi(tcx, abi, instance.def_id(), |required_feature| { + let span = tcx.def_span(instance.def_id()); + if let Some(required_feature) = required_feature { + tcx.emit_node_span_lint( + ABI_UNSUPPORTED_VECTOR_TYPES, + CRATE_HIR_ID, + span, + AbiErrorDisabledVectorTypeDef { span, required_feature }, + ); + } else { + tcx.emit_node_span_lint( + ABI_UNSUPPORTED_VECTOR_TYPES, + CRATE_HIR_ID, + span, + AbiErrorUnsupportedVectorTypeDef { span }, + ); + } + }) +} + +/// Checks that a call expression does not try to pass a vector-passed argument which requires a +/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch. +fn check_call_site_abi<'tcx>( + tcx: TyCtxt<'tcx>, + callee: Ty<'tcx>, + span: Span, + caller: InstanceKind<'tcx>, +) { + if callee.fn_sig(tcx).abi().is_rust() { + // "Rust" ABI never passes arguments in vector registers. + return; + } + let typing_env = ty::TypingEnv::fully_monomorphized(); + let callee_abi = match *callee.kind() { + ty::FnPtr(..) => { + tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((callee.fn_sig(tcx), ty::List::empty()))) + } + ty::FnDef(def_id, args) => { + // Intrinsics are handled separately by the compiler. + if tcx.intrinsic(def_id).is_some() { + return; + } + let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP); + tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty()))) + } + _ => { + panic!("Invalid function call"); + } + }; + + let Ok(callee_abi) = callee_abi else { + // ABI failed to compute; this will not get through codegen. + return; + }; + do_check_abi(tcx, callee_abi, caller.def_id(), |required_feature| { + if let Some(required_feature) = required_feature { + tcx.emit_node_span_lint( + ABI_UNSUPPORTED_VECTOR_TYPES, + CRATE_HIR_ID, + span, + AbiErrorDisabledVectorTypeCall { span, required_feature }, + ); + } else { + tcx.emit_node_span_lint( + ABI_UNSUPPORTED_VECTOR_TYPES, + CRATE_HIR_ID, + span, + AbiErrorUnsupportedVectorTypeCall { span }, + ); + } + }); +} + +fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) { + // Check all function call terminators. + for (bb, _data) in traversal::mono_reachable(body, tcx, instance) { + let terminator = body.basic_blocks[bb].terminator(); + match terminator.kind { + mir::TerminatorKind::Call { ref func, ref fn_span, .. } + | mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => { + let callee_ty = func.ty(body, tcx); + let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions( + tcx, + ty::TypingEnv::fully_monomorphized(), + ty::EarlyBinder::bind(callee_ty), + ); + check_call_site_abi(tcx, callee_ty, *fn_span, body.source.instance); + } + _ => {} + } + } +} + +pub(crate) fn check_feature_dependent_abi<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, +) { + check_instance_abi(tcx, instance); + check_callees_abi(tcx, instance, body); +} diff --git a/compiler/rustc_monomorphize/src/mono_checks/mod.rs b/compiler/rustc_monomorphize/src/mono_checks/mod.rs new file mode 100644 index 00000000000..1ecda824fb8 --- /dev/null +++ b/compiler/rustc_monomorphize/src/mono_checks/mod.rs @@ -0,0 +1,23 @@ +//! This implements a single query, `check_mono_fn`, that gets fired for each +//! monomorphization of all functions. This lets us implement monomorphization-time +//! checks in a way that is friendly to incremental compilation. + +use rustc_middle::query::Providers; +use rustc_middle::ty::{Instance, TyCtxt}; + +mod abi_check; +mod move_check; + +fn check_mono_item<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { + let body = tcx.instance_mir(instance.def); + abi_check::check_feature_dependent_abi(tcx, instance, body); + move_check::check_moves(tcx, instance, body); +} + +pub(super) fn provide(providers: &mut Providers) { + *providers = Providers { + check_mono_item, + skip_move_check_fns: move_check::skip_move_check_fns, + ..*providers + } +} diff --git a/compiler/rustc_monomorphize/src/collector/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index b86ef1e7373..438d49fd7fb 100644 --- a/compiler/rustc_monomorphize/src/collector/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -1,38 +1,74 @@ +use rustc_abi::Size; +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::visit::Visitor as MirVisitor; +use rustc_middle::mir::{self, Location, traversal}; +use rustc_middle::ty::{self, AssocKind, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_session::Limit; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; -use tracing::debug; +use rustc_span::Span; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::{Ident, sym}; +use tracing::{debug, trace}; -use super::*; use crate::errors::LargeAssignmentsLint; -pub(super) struct MoveCheckState { +struct MoveCheckVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, /// Spans for move size lints already emitted. Helps avoid duplicate lints. move_size_spans: Vec<Span>, - /// Set of functions for which it is OK to move large data into. - skip_move_check_fns: Option<Vec<DefId>>, } -impl MoveCheckState { - pub(super) fn new() -> Self { - MoveCheckState { move_size_spans: vec![], skip_move_check_fns: None } +pub(crate) fn check_moves<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, +) { + let mut visitor = MoveCheckVisitor { tcx, instance, body, move_size_spans: vec![] }; + for (bb, data) in traversal::mono_reachable(body, tcx, instance) { + visitor.visit_basic_block_data(bb, data) } } -impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { - pub(super) fn check_operand_move_size( - &mut self, - operand: &mir::Operand<'tcx>, - location: Location, - ) { - let limit = self.tcx.move_size_limit(); - if limit.0 == 0 { - return; +impl<'tcx> MirVisitor<'tcx> for MoveCheckVisitor<'tcx> { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + match terminator.kind { + mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } + | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => { + let callee_ty = func.ty(self.body, self.tcx); + let callee_ty = self.monomorphize(callee_ty); + self.check_fn_args_move_size(callee_ty, args, *fn_span, location); + } + _ => {} } - // This function is called by visit_operand() which visits _all_ - // operands, including TerminatorKind::Call operands. But if - // check_fn_args_move_size() has been called, the operands have already - // been visited. Do not visit them again. - if self.visiting_call_terminator { + // We deliberately do *not* visit the nested operands here, to avoid + // hitting `visit_operand` for function arguments. + } + + fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) { + self.check_operand_move_size(operand, location); + } +} + +impl<'tcx> MoveCheckVisitor<'tcx> { + fn monomorphize<T>(&self, value: T) -> T + where + T: TypeFoldable<TyCtxt<'tcx>>, + { + trace!("monomorphize: self.instance={:?}", self.instance); + self.instance.instantiate_mir_and_normalize_erasing_regions( + self.tcx, + ty::TypingEnv::fully_monomorphized(), + ty::EarlyBinder::bind(value), + ) + } + + fn check_operand_move_size(&mut self, operand: &mir::Operand<'tcx>, location: Location) { + let limit = self.tcx.move_size_limit(); + if limit.0 == 0 { return; } @@ -64,12 +100,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { let ty::FnDef(def_id, _) = *callee_ty.kind() else { return; }; - if self - .move_check - .skip_move_check_fns - .get_or_insert_with(|| build_skip_move_check_fns(self.tcx)) - .contains(&def_id) - { + if self.tcx.skip_move_check_fns(()).contains(&def_id) { return; } @@ -97,7 +128,9 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { ) -> Option<Size> { let ty = operand.ty(self.body, self.tcx); let ty = self.monomorphize(ty); - let Ok(layout) = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) else { + let Ok(layout) = + self.tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) + else { return None; }; if layout.size.bytes_usize() > limit.0 { @@ -116,14 +149,12 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { span: Span, ) { let source_info = self.body.source_info(location); - debug!(?source_info); - for reported_span in &self.move_check.move_size_spans { + for reported_span in &self.move_size_spans { if reported_span.overlaps(span) { return; } } let lint_root = source_info.scope.lint_root(&self.body.source_scopes); - debug!(?lint_root); let Some(lint_root) = lint_root else { // This happens when the issue is in a function from a foreign crate that // we monomorphized in the current crate. We can't get a `HirId` for things @@ -137,11 +168,25 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { size: too_large_size.bytes(), limit: limit as u64, }); - self.move_check.move_size_spans.push(span); + self.move_size_spans.push(span); + } +} + +fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> { + for impl_def_id in tcx.inherent_impls(def_id) { + if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + tcx, + fn_ident, + AssocKind::Fn, + def_id, + ) { + return Some(new.def_id); + } } + None } -fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec<DefId> { +pub(crate) fn skip_move_check_fns(tcx: TyCtxt<'_>, _: ()) -> FxIndexSet<DefId> { let fns = [ (tcx.lang_items().owned_box(), "new"), (tcx.get_diagnostic_item(sym::Rc), "new"), @@ -151,5 +196,5 @@ fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec<DefId> { .filter_map(|(def_id, fn_name)| { def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) }) - .collect::<Vec<_>>() + .collect() } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index e2a6d392ca0..7240cfce0f7 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -666,7 +666,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( // This is a method within an impl, find out what the self-type is: let impl_self_ty = tcx.instantiate_and_normalize_erasing_regions( instance.args, - ty::ParamEnv::reveal_all(), + ty::TypingEnv::fully_monomorphized(), tcx.type_of(impl_def_id), ); if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) { @@ -1319,5 +1319,20 @@ pub(crate) fn provide(providers: &mut Providers) { .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}")) }; + providers.size_estimate = |tcx, instance| { + match instance.def { + // "Normal" functions size estimate: the number of + // statements, plus one for the terminator. + InstanceKind::Item(..) + | InstanceKind::DropGlue(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => { + let mir = tcx.instance_mir(instance.def); + mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum() + } + // Other compiler-generated shims size estimate: 1 + _ => 1, + } + }; + collector::provide(providers); } diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index 093a697beeb..deb4ab433bf 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs @@ -22,29 +22,29 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In let typeck_results = tcx.typeck(closure_def_id); if typeck_results.closure_size_eval.contains_key(&closure_def_id) { - let param_env = ty::ParamEnv::reveal_all(); + let typing_env = ty::TypingEnv::fully_monomorphized(); let ClosureSizeProfileData { before_feature_tys, after_feature_tys } = typeck_results.closure_size_eval[&closure_def_id]; let before_feature_tys = tcx.instantiate_and_normalize_erasing_regions( closure_instance.args, - param_env, + typing_env, ty::EarlyBinder::bind(before_feature_tys), ); let after_feature_tys = tcx.instantiate_and_normalize_erasing_regions( closure_instance.args, - param_env, + typing_env, ty::EarlyBinder::bind(after_feature_tys), ); let new_size = tcx - .layout_of(param_env.and(after_feature_tys)) + .layout_of(typing_env.as_query_input(after_feature_tys)) .map(|l| format!("{:?}", l.size.bytes())) .unwrap_or_else(|e| format!("Failed {e:?}")); let old_size = tcx - .layout_of(param_env.and(before_feature_tys)) + .layout_of(typing_env.as_query_input(before_feature_tys)) .map(|l| format!("{:?}", l.size.bytes())) .unwrap_or_else(|e| format!("Failed {e:?}")); |
