diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 56a893738df60..0efc1a5734fa1 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,5 +1,6 @@ use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, struct_span_code_err, DiagMessage, SubdiagMessage}; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -13,8 +14,10 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::{lint, parse::feature_err}; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, Symbol}; +use rustc_target::abi::VariantIdx; use rustc_target::spec::{abi, SanitizerSet}; +use rustc_type_ir::inherent::Abi; use crate::errors; use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature}; @@ -75,23 +78,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let mut link_ordinal_span = None; let mut no_sanitize_span = None; + // In some cases, attribute are only valid on functions, but it's the `check_attr` + // pass that check that they aren't used anywhere else, rather this module. + // In these cases, we bail from performing further checks that are only meaningful for + // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also + // report a delayed bug, just in case `check_attr` isn't doing its job. + let fn_sig_outer = || { + use DefKind::*; + + let def_kind = tcx.def_kind(did); + if let Fn | AssocFn | Variant | Ctor(..) = def_kind { Some(tcx.fn_sig(did)) } else { None } + }; + for attr in attrs.iter() { - // In some cases, attribute are only valid on functions, but it's the `check_attr` - // pass that check that they aren't used anywhere else, rather this module. - // In these cases, we bail from performing further checks that are only meaningful for - // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also - // report a delayed bug, just in case `check_attr` isn't doing its job. let fn_sig = || { - use DefKind::*; - - let def_kind = tcx.def_kind(did); - if let Fn | AssocFn | Variant | Ctor(..) = def_kind { - Some(tcx.fn_sig(did)) - } else { + let sig = fn_sig_outer(); + if sig.is_none() { tcx.dcx() .span_delayed_bug(attr.span, "this attribute can only be applied to functions"); - None } + sig }; let Some(Ident { name, .. }) = attr.ident() else { @@ -610,6 +616,57 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } + if tcx.features().struct_target_features + && let Some(sig) = fn_sig_outer() + { + // Collect target features from types reachable from arguments. + // We define a type as "reachable" if: + // - it is a function argument + // - it is a field of a reachable struct + // - there is a reachable reference to it + // TODO: presumably we want to cache the result of this computation. + let mut visited_types = FxHashSet::default(); + let mut reachable_types: Vec<_> = sig.skip_binder().inputs().skip_binder().to_owned(); + let mut additional_tf = vec![]; + + while let Some(ty) = reachable_types.pop() { + if visited_types.contains(&ty) { + continue; + } + visited_types.insert(ty); + if ty.is_ref() { + reachable_types.push(ty.builtin_deref(false).unwrap()); + } else if matches!(ty.kind(), ty::Tuple(_)) { + reachable_types.extend(ty.tuple_fields().iter()); + } else if let ty::Adt(adt_def, args) = ty.kind() { + additional_tf.extend_from_slice(&adt_def.target_features()); + if adt_def.is_struct() { + reachable_types.extend( + adt_def + .variant(VariantIdx::from_usize(0)) + .fields + .iter() + .map(|field| field.ty(tcx, args)), + ); + } + } + } + + if !additional_tf.is_empty() && !sig.skip_binder().abi().is_rust() { + tcx.dcx().span_err( + tcx.hir().span(tcx.local_def_id_to_hir_id(did)), + "cannot use a struct with target features in a function with non-Rust ABI", + ); + } + if !additional_tf.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always { + tcx.dcx().span_err( + tcx.hir().span(tcx.local_def_id_to_hir_id(did)), + "cannot use a struct with target features in a #[inline(always)] function", + ); + } + codegen_fn_attrs.target_features.extend_from_slice(&additional_tf); + } + // If a function uses #[target_feature] it can't be inlined into general // purpose functions as they wouldn't have the right target features // enabled. For that reason we also forbid #[inline(always)] as it can't be @@ -755,6 +812,20 @@ fn check_link_name_xor_ordinal( } } +fn struct_target_features(tcx: TyCtxt<'_>, def_id: DefId) -> Vec { + let mut features = vec![]; + let supported_features = tcx.supported_target_features(def_id.krate); + for attr in tcx.get_attrs(def_id, sym::target_feature) { + from_target_feature(tcx, attr, supported_features, &mut features); + } + features +} + pub fn provide(providers: &mut Providers) { - *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; + *providers = Providers { + codegen_fn_attrs, + should_inherit_track_caller, + struct_target_features, + ..*providers + }; } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1db3774222a09..0065f5ecf7601 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -602,6 +602,8 @@ declare_features! ( (unstable, strict_provenance, "1.61.0", Some(95228)), /// Allows string patterns to dereference values to match them. (unstable, string_deref_patterns, "1.67.0", Some(87121)), + /// Allows structs to carry target_feature information. TODO + (unstable, struct_target_features, "CURRENT_RUSTC_VERSION", None), /// Allows the use of `#[target_feature]` on safe functions. (unstable, target_feature_11, "1.45.0", Some(69098)), /// Allows using `#[thread_local]` on `static` items. diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e0aad29916323..a299c152cbf1d 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1147,7 +1147,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { } else { tcx.repr_options_of_def(def_id) }; - let (kind, variants) = match &item.kind { + let (kind, variants, features) = match &item.kind { ItemKind::Enum(def, _) => { let mut distance_from_explicit = 0; let variants = def @@ -1175,7 +1175,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { }) .collect(); - (AdtKind::Enum, variants) + (AdtKind::Enum, variants, vec![]) } ItemKind::Struct(def, _) | ItemKind::Union(def, _) => { let adt_kind = match item.kind { @@ -1194,11 +1194,11 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { )) .collect(); - (adt_kind, variants) + (adt_kind, variants, tcx.struct_target_features(def_id).clone()) } _ => bug!("{:?} is not an ADT", item.owner_id.def_id), }; - tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, is_anonymous) + tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, features, is_anonymous) } fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 0ba9b940eed10..f4511c401aa7b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1179,6 +1179,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { adt_kind, variants.into_iter().map(|(_, variant)| variant).collect(), repr, + self.root + .tables + .adt_target_features + .get(self, item_id) + .expect("target features not encoded") + .decode(self) + .collect(), false, ) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 209316ca20fdb..92712497dc2fb 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1568,6 +1568,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.fn_sig[variant.def_id] <- fn_sig); } } + record_array!(self.tables.adt_target_features[def_id] <- adt_def.target_features()); } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2a44b3423ae2f..311dd47e85d37 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -459,6 +459,7 @@ define_tables! { def_keys: Table>, proc_macro_quoted_spans: Table>, variant_data: Table>, + adt_target_features: Table>, assoc_container: Table, macro_definition: Table>, proc_macro: Table, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 817c7157b6820..f9eef93c0081d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1253,6 +1253,11 @@ rustc_queries! { feedable } + query struct_target_features(def_id: DefId) -> &'tcx Vec { + arena_cache + desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) } + } + query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet { desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 88ee32eae9529..c219f851d15b2 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -16,6 +16,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::DataTypeKind; use rustc_span::symbol::sym; +use rustc_span::Symbol; use rustc_target::abi::{ReprOptions, VariantIdx, FIRST_VARIANT}; use tracing::{debug, info, trace}; @@ -103,6 +104,8 @@ pub struct AdtDefData { flags: AdtFlags, /// Repr options provided by the user. repr: ReprOptions, + /// Target features that functions taking objects of this type by value will enable. + target_features: Vec, } impl PartialEq for AdtDefData { @@ -115,8 +118,8 @@ impl PartialEq for AdtDefData { // definition of `AdtDefData` changes, a compile-error will be produced, // reminding us to revisit this assumption. - let Self { did: self_def_id, variants: _, flags: _, repr: _ } = self; - let Self { did: other_def_id, variants: _, flags: _, repr: _ } = other; + let Self { did: self_def_id, variants: _, flags: _, repr: _, target_features: _ } = self; + let Self { did: other_def_id, variants: _, flags: _, repr: _, target_features: _ } = other; let res = self_def_id == other_def_id; @@ -153,13 +156,15 @@ impl<'a> HashStable> for AdtDefData { let addr = self as *const AdtDefData as usize; let hashing_controls = hcx.hashing_controls(); *cache.borrow_mut().entry((addr, hashing_controls)).or_insert_with(|| { - let ty::AdtDefData { did, ref variants, ref flags, ref repr } = *self; + let ty::AdtDefData { did, ref variants, ref flags, ref repr, ref target_features } = + *self; let mut hasher = StableHasher::new(); did.hash_stable(hcx, &mut hasher); variants.hash_stable(hcx, &mut hasher); flags.hash_stable(hcx, &mut hasher); repr.hash_stable(hcx, &mut hasher); + target_features.hash_stable(hcx, &mut hasher); hasher.finish() }) @@ -198,6 +203,11 @@ impl<'tcx> AdtDef<'tcx> { pub fn repr(self) -> ReprOptions { self.0.0.repr } + + #[inline] + pub fn target_features(self) -> &'tcx Vec { + &self.0.0.target_features + } } impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { @@ -260,6 +270,7 @@ impl AdtDefData { kind: AdtKind, variants: IndexVec, repr: ReprOptions, + target_features: Vec, is_anonymous: bool, ) -> Self { debug!( @@ -302,7 +313,7 @@ impl AdtDefData { flags |= AdtFlags::IS_ANONYMOUS; } - AdtDefData { did, variants, flags, repr } + AdtDefData { did, variants, flags, repr, target_features } } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fd41668ae44c8..955ff7aea0c6f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1429,6 +1429,7 @@ impl<'tcx> TyCtxt<'tcx> { kind: AdtKind, variants: IndexVec, repr: ReprOptions, + features: Vec, is_anonymous: bool, ) -> ty::AdtDef<'tcx> { self.mk_adt_def_from_data(ty::AdtDefData::new( @@ -1437,6 +1438,7 @@ impl<'tcx> TyCtxt<'tcx> { kind, variants, repr, + features, is_anonymous, )) } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index a65586ccdb7e7..45b7edad08bc2 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -492,10 +492,16 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { user_ty: _, fields: _, base: _, - }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) { - (Bound::Unbounded, Bound::Unbounded) => {} - _ => self.requires_unsafe(expr.span, InitializingTypeWith), - }, + }) => { + match self.tcx.layout_scalar_valid_range(adt_def.did()) { + (Bound::Unbounded, Bound::Unbounded) => {} + _ => self.requires_unsafe(expr.span, InitializingTypeWith), + } + if !adt_def.target_features().is_empty() { + self.requires_unsafe(expr.span, ConstructingTargetFeaturesType) + } + } + ExprKind::Closure(box ClosureExpr { closure_id, args: _, @@ -597,6 +603,7 @@ enum UnsafeOpKind { CallToUnsafeFunction(Option), UseOfInlineAssembly, InitializingTypeWith, + ConstructingTargetFeaturesType, UseOfMutableStatic, UseOfExternStatic, DerefOfRawPointer, @@ -678,6 +685,16 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), + ConstructingTargetFeaturesType => tcx.emit_node_span_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + // TODO: better message + UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, + ), UseOfMutableStatic => tcx.emit_node_span_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, @@ -835,6 +852,18 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }); } + ConstructingTargetFeaturesType if unsafe_op_in_unsafe_fn_allowed => { + dcx.emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + unsafe_not_inherited_note, + }); + } + ConstructingTargetFeaturesType => { + dcx.emit_err(InitializingTypeWithRequiresUnsafe { + span, + unsafe_not_inherited_note, + }); + } UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => { dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ce2fa83810fe8..aa6edf8a0555c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -5,6 +5,7 @@ //! item. use crate::{errors, fluent_generated as fluent}; +use core::panic; use rustc_ast::{ast, AttrKind, AttrStyle, Attribute, LitKind}; use rustc_ast::{MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; @@ -643,12 +644,42 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); true } + Target::Struct if self.tcx.features().struct_target_features => { + let ty = self.tcx.hir_node(hir_id).expect_item(); + match ty.kind { + ItemKind::Struct(data, _) => { + if data.fields().len() == 0 { + true + } else { + // TODO: proper error message. + self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + on_crate: hir_id == CRATE_HIR_ID, + }); + false + } + } + _ => { + panic!("Target::Struct for a non-struct"); + } + } + } _ => { - self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, - defn_span: span, - on_crate: hir_id == CRATE_HIR_ID, - }); + if self.tcx.features().struct_target_features { + // TODO: proper error message. + self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + on_crate: hir_id == CRATE_HIR_ID, + }); + } else { + self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + on_crate: hir_id == CRATE_HIR_ID, + }); + } false } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b64efadb2619e..b0bbf7cf317f5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1839,6 +1839,7 @@ symbols! { stringify, struct_field_attributes, struct_inherit, + struct_target_features, struct_variant, structural_match, structural_peq,