diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4ab20c154ccd0..4420b10297050 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -8,11 +8,11 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; use rustc_hir::{lang_items, LangItem}; use rustc_middle::middle::codegen_fn_attrs::{ - CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, + CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, TargetFeature, }; use rustc_middle::mir::mono::Linkage; use rustc_middle::query::Providers; -use rustc_middle::ty::{self as ty, TyCtxt}; +use rustc_middle::ty::{self as ty, Ty, TyCtxt}; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::Ident; @@ -78,6 +78,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let mut link_ordinal_span = None; let mut no_sanitize_span = None; + 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. @@ -85,16 +92,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // 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 { @@ -613,7 +616,30 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } - // If a function uses #[target_feature] it can't be inlined into general + if let Some(sig) = fn_sig_outer() { + for ty in sig.skip_binder().inputs().skip_binder() { + let additional_tf = + tcx.struct_reachable_target_features(tcx.param_env(did.to_def_id()).and(*ty)); + // FIXME(struct_target_features): is this really necessary? + if !additional_tf.is_empty() && sig.skip_binder().abi() != abi::Abi::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(additional_tf.iter().map(|tf| TargetFeature { implied: true, ..*tf })); + } + } + + // If a function uses non-default target_features 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 // respected. @@ -758,6 +784,47 @@ fn check_link_name_xor_ordinal( } } +fn struct_target_features(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[TargetFeature] { + let mut features = vec![]; + let supported_features = tcx.supported_target_features(LOCAL_CRATE); + for attr in tcx.get_attrs(def_id, sym::target_feature) { + from_target_feature(tcx, attr, supported_features, &mut features); + } + tcx.arena.alloc_slice(&features) +} + +fn struct_reachable_target_features<'tcx>( + tcx: TyCtxt<'tcx>, + env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> &'tcx [TargetFeature] { + // Collect target features from types reachable from `env.value` by dereferencing a certain + // number of references and resolving aliases. + + let mut ty = env.value; + if matches!(ty.kind(), ty::Alias(..)) { + ty = match tcx.try_normalize_erasing_regions(env.param_env, ty) { + Ok(ty) => ty, + Err(_) => return tcx.arena.alloc_slice(&[]), + }; + } + while let ty::Ref(_, inner, _) = ty.kind() { + ty = *inner; + } + + let tf = if let ty::Adt(adt_def, ..) = ty.kind() { + tcx.struct_target_features(adt_def.did()) + } else { + &[] + }; + tcx.arena.alloc_slice(tf) +} + 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, + struct_reachable_target_features, + ..*providers + }; } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3254dab9a0344..74fcc8b331b11 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -599,6 +599,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. + (incomplete, struct_target_features, "CURRENT_RUSTC_VERSION", Some(129107)), /// 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/src/def.rs b/compiler/rustc_hir/src/def.rs index bd55617d84ea2..7ee630546f6da 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -326,6 +326,43 @@ impl DefKind { | DefKind::ExternCrate => false, } } + + /// Whether `query struct_target_features` should be used with this definition. + pub fn has_struct_target_features(self) -> bool { + match self { + DefKind::Struct => true, + DefKind::Fn + | DefKind::Union + | DefKind::Enum + | DefKind::AssocFn + | DefKind::Ctor(..) + | DefKind::Closure + | DefKind::Static { .. } + | DefKind::Mod + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::Const + | DefKind::AssocConst + | DefKind::Macro(..) + | DefKind::Use + | DefKind::ForeignMod + | DefKind::OpaqueTy + | DefKind::Impl { .. } + | DefKind::Field + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::LifetimeParam + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::SyntheticCoroutineBody + | DefKind::GlobalAsm + | DefKind::ExternCrate => false, + } + } } /// The resolution of a path or export. diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d97c590bd4109..865e9be0a67dd 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -851,6 +851,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). + // FIXME(struct_target_features): should this be true also for functions that inherit + // target features from structs? if b_hdr.safety == hir::Safety::Safe && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 53da07aeaa695..cce5bb1b51e92 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -254,6 +254,7 @@ provide! { tcx, def_id, other, cdata, variances_of => { table } fn_sig => { table } codegen_fn_attrs => { table } + struct_target_features => { table_defaulted_array } impl_trait_header => { table } const_param_default => { table } object_lifetime_default => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b617d5236b980..b4a4a7359453e 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1398,6 +1398,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if def_kind.has_codegen_attrs() { record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); } + if def_kind.has_struct_target_features() { + record_defaulted_array!(self.tables.struct_target_features[def_id] <- self.tcx.struct_target_features(def_id)); + } if should_encode_visibility(def_kind) { let vis = self.tcx.local_visibility(local_id).map_id(|def_id| def_id.local_def_index); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 8180a507a514d..ed7dc9da19abf 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -19,7 +19,7 @@ use rustc_macros::{ Decodable, Encodable, MetadataDecodable, MetadataEncodable, TyDecodable, TyEncodable, }; use rustc_middle::metadata::ModChild; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::middle::lib_features::FeatureStability; @@ -404,6 +404,7 @@ define_tables! { // individually instead of `DefId`s. module_children_reexports: Table>, cross_crate_inlinable: Table, + struct_target_features: Table>, - optional: attributes: Table>, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index b7d290e58d22b..c098a73959230 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -26,8 +26,8 @@ pub struct CodegenFnAttrs { /// be set when `link_name` is set. This is for foreign items with the /// "raw-dylib" kind. pub link_ordinal: Option, - /// The `#[target_feature(enable = "...")]` attribute and the enabled - /// features (only enabled features are supported right now). + /// All the target features that are enabled for this function. Some features might be enabled + /// implicitly. pub target_features: Vec, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option, @@ -55,8 +55,8 @@ pub struct CodegenFnAttrs { pub struct TargetFeature { /// The name of the target feature (e.g. "avx") pub name: Symbol, - /// The feature is implied by another feature, rather than explicitly added by the - /// `#[target_feature]` attribute + /// The feature is implied by another feature or by an argument, rather than explicitly + /// added by the `#[target_feature]` attribute pub implied: bool, } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 969374cb0e540..faecbe44ffdf6 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -47,7 +47,7 @@ use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; -use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use crate::middle::lib_features::LibFeatures; @@ -1249,6 +1249,15 @@ rustc_queries! { feedable } + query struct_target_features(def_id: DefId) -> &'tcx [TargetFeature] { + separate_provide_extern + desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) } + } + + query struct_reachable_target_features(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx [TargetFeature] { + desc { |tcx| "computing target features reachable from {}", env.value } + } + 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/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 7e1255f606c35..be611e19b49a7 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -59,6 +59,7 @@ trivially_parameterized_over_tcx! { std::string::String, crate::metadata::ModChild, crate::middle::codegen_fn_attrs::CodegenFnAttrs, + crate::middle::codegen_fn_attrs::TargetFeature, crate::middle::debugger_visualizer::DebuggerVisualizerFile, crate::middle::exported_symbols::SymbolExportInfo, crate::middle::lib_features::FeatureStability, diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 7a10e627ccd89..fb89378514750 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -116,15 +116,56 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant mir_build_initializing_type_with_requires_unsafe = - initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block - .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior - .label = initializing type with `rustc_layout_scalar_valid_range` attr + call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block + .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> + [1] feature + *[count] features + }: {$missing_target_features} + .note = the {$build_target_features} target {$build_target_features_count -> + [1] feature + *[count] features + } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> + [1] it + *[count] them + } in `#[target_feature]` + .label = call to function with `#[target_feature]` mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr +mir_build_initializing_type_with_target_feature_requires_unsafe = + initializing type `{$adt}` with `#[target_feature]` is unsafe and requires unsafe block + .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> + [1] feature + *[count] features + }: {$missing_target_features} + .note = the {$build_target_features} target {$build_target_features_count -> + [1] feature + *[count] features + } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> + [1] it + *[count] them + } in `#[target_feature]` + .label = call to function with `#[target_feature]` + +mir_build_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + initializing type `{$adt}` with `#[target_feature]` is unsafe and requires unsafe function or block + .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> + [1] feature + *[count] features + }: {$missing_target_features} + .note = the {$build_target_features} target {$build_target_features_count -> + [1] feature + *[count] features + } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> + [1] it + *[count] them + } in `#[target_feature]` + .label = call to function with `#[target_feature]` + + mir_build_inline_assembly_requires_unsafe = use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior @@ -387,6 +428,11 @@ mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe = .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr +mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe = + initializing type with `target_feature` attr is unsafe and requires unsafe block + .note = this struct can only be constructed if the corresponding `target_feature`s are available + .label = initializing type with `target_feature` attr + mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe = use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index c7fcfe3ce2aa0..a6f00b822507a 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -461,14 +461,18 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { }; self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id)); } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() { - // If the called function has target features the calling function hasn't, + // If the called function has explicit target features the calling function hasn't, // the call requires `unsafe`. Don't check this on wasm // targets, though. For more information on wasm see the // is_like_wasm check in hir_analysis/src/collect.rs + // Implicit target features are OK because they are either a consequence of some + // explicit target feature (which is checked to be present in the caller) or + // come from a witness argument. let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; if !self.tcx.sess.target.options.is_like_wasm && !callee_features.iter().all(|feature| { - self.body_target_features.iter().any(|f| f.name == feature.name) + feature.implied + || self.body_target_features.iter().any(|f| f.name == feature.name) }) { let missing: Vec<_> = callee_features @@ -542,10 +546,52 @@ 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), + } + let struct_features = self.tcx.struct_target_features(adt_def.did()); + if !struct_features.is_empty() { + if !self.tcx.sess.target.options.is_like_wasm + && !struct_features.iter().all(|feature| { + feature.implied + || self.body_target_features.iter().any(|f| f.name == feature.name) + }) + { + // Matches the logic for calling non-unsafe `target_feature` functions. + let missing: Vec<_> = struct_features + .iter() + .copied() + .filter(|feature| { + !feature.implied + && !self + .body_target_features + .iter() + .any(|body_feature| body_feature.name == feature.name) + }) + .map(|feature| feature.name) + .collect(); + let build_enabled = self + .tcx + .sess + .target_features + .iter() + .copied() + .filter(|feature| missing.contains(feature)) + .collect(); + self.requires_unsafe( + expr.span, + ConstructingTargetFeaturesTypeWith { + adt: adt_def.did(), + missing, + build_enabled, + }, + ); + } + } + } + ExprKind::Closure(box ClosureExpr { closure_id, args: _, @@ -647,6 +693,15 @@ enum UnsafeOpKind { CallToUnsafeFunction(Option), UseOfInlineAssembly, InitializingTypeWith, + ConstructingTargetFeaturesTypeWith { + adt: DefId, + /// Target features enabled in callee's `#[target_feature]` but missing in + /// caller's `#[target_feature]`. + missing: Vec, + /// Target features in `missing` that are enabled at compile time + /// (e.g., with `-C target-feature`). + build_enabled: Vec, + }, UseOfMutableStatic, UseOfExternStatic, DerefOfRawPointer, @@ -728,6 +783,29 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), + ConstructingTargetFeaturesTypeWith { adt, missing, build_enabled } => tcx + .emit_node_span_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnInitializingTypeWithTargetFeatureRequiresUnsafe { + span, + adt: with_no_trimmed_paths!(tcx.def_path_str(*adt)), + missing_target_features: DiagArgValue::StrListSepByAnd( + missing.iter().map(|feature| Cow::from(feature.to_string())).collect(), + ), + missing_target_features_count: missing.len(), + note: !build_enabled.is_empty(), + build_target_features: DiagArgValue::StrListSepByAnd( + build_enabled + .iter() + .map(|feature| Cow::from(feature.to_string())) + .collect(), + ), + build_target_features_count: build_enabled.len(), + unsafe_not_inherited_note, + }, + ), UseOfMutableStatic => tcx.emit_node_span_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, @@ -885,6 +963,48 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }); } + ConstructingTargetFeaturesTypeWith { adt, missing, build_enabled } + if unsafe_op_in_unsafe_fn_allowed => + { + dcx.emit_err( + InitializingTypeWithTargetFeatureRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + adt: with_no_trimmed_paths!(tcx.def_path_str(*adt)), + missing_target_features: DiagArgValue::StrListSepByAnd( + missing.iter().map(|feature| Cow::from(feature.to_string())).collect(), + ), + missing_target_features_count: missing.len(), + note: !build_enabled.is_empty(), + build_target_features: DiagArgValue::StrListSepByAnd( + build_enabled + .iter() + .map(|feature| Cow::from(feature.to_string())) + .collect(), + ), + build_target_features_count: build_enabled.len(), + unsafe_not_inherited_note, + }, + ); + } + ConstructingTargetFeaturesTypeWith { adt, missing, build_enabled } => { + dcx.emit_err(InitializingTypeWithTargetFeatureRequiresUnsafe { + span, + adt: with_no_trimmed_paths!(tcx.def_path_str(*adt)), + missing_target_features: DiagArgValue::StrListSepByAnd( + missing.iter().map(|feature| Cow::from(feature.to_string())).collect(), + ), + missing_target_features_count: missing.len(), + note: !build_enabled.is_empty(), + build_target_features: DiagArgValue::StrListSepByAnd( + build_enabled + .iter() + .map(|feature| Cow::from(feature.to_string())) + .collect(), + ), + build_target_features_count: build_enabled.len(), + unsafe_not_inherited_note, + }); + } UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => { dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 7f9eefd1d52e3..53fffc0b9e40f 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -86,6 +86,23 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithTargetFeatureRequiresUnsafe { + #[label] + pub(crate) span: Span, + pub(crate) adt: String, + pub(crate) missing_target_features: DiagArgValue, + pub(crate) missing_target_features_count: usize, + #[note] + pub(crate) note: bool, + pub(crate) build_target_features: DiagArgValue, + pub(crate) build_target_features_count: usize, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(LintDiagnostic)] #[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)] #[note] @@ -250,6 +267,24 @@ pub(crate) struct InitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag(mir_build_initializing_type_with_target_feature_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct InitializingTypeWithTargetFeatureRequiresUnsafe { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) adt: String, + pub(crate) missing_target_features: DiagArgValue, + pub(crate) missing_target_features_count: usize, + #[note] + pub(crate) note: bool, + pub(crate) build_target_features: DiagArgValue, + pub(crate) build_target_features_count: usize, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Diagnostic)] #[diag( mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, @@ -264,6 +299,27 @@ pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag( + mir_build_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = E0133 +)] +#[note] +pub(crate) struct InitializingTypeWithTargetFeatureRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) adt: String, + pub(crate) missing_target_features: DiagArgValue, + pub(crate) missing_target_features_count: usize, + #[note] + pub(crate) note: bool, + pub(crate) build_target_features: DiagArgValue, + pub(crate) build_target_features_count: usize, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Diagnostic)] #[diag(mir_build_mutable_static_requires_unsafe, code = E0133)] #[note] diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index e7f208d5ad544..18a1e17369f96 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -672,6 +672,10 @@ passes_should_be_applied_to_fn = *[false] not a function definition } +passes_should_be_applied_to_fn_or_struct = + attribute should be applied to a function definition or struct + .label = not a function definition or a struct + passes_should_be_applied_to_static = attribute should be applied to a static .label = not a static diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e41f89a3c9da9..240f65c831d7a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -729,6 +729,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }); } } + Target::Struct if self.tcx.features().struct_target_features => {} Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} // FIXME: #[target_feature] was previously erroneously allowed on statements and some // crates used this, so only emit a warning. @@ -748,11 +749,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); } _ => { - 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 { + self.dcx().emit_err(errors::AttrShouldBeAppliedToFnOrStruct { + attr_span: attr.span, + defn_span: span, + }); + } else { + self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + on_crate: hir_id == CRATE_HIR_ID, + }); + } } } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f5d982e1a5cd1..82fa80b9fdb76 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -82,6 +82,15 @@ pub(crate) struct AttrShouldBeAppliedToFn { pub on_crate: bool, } +#[derive(Diagnostic)] +#[diag(passes_should_be_applied_to_fn_or_struct)] +pub(crate) struct AttrShouldBeAppliedToFnOrStruct { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + #[derive(Diagnostic)] #[diag(passes_should_be_applied_to_fn, code = E0739)] pub(crate) struct TrackedCallerWrongLocation { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7572d57309c88..1ce6c2be2d508 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1861,6 +1861,7 @@ symbols! { stringify, struct_field_attributes, struct_inherit, + struct_target_features, struct_variant, structural_match, structural_peq, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5918686213a78..de93434bd6011 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -439,6 +439,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let is_target_feature_fn = if let ty::FnDef(def_id, _) = *leaf_trait_ref.skip_binder().self_ty().kind() { + // FIXME(struct_target_features): should a function that inherits + // target_features through arguments implement Fn traits? !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() } else { false diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 77efc2fc2dbfd..de076a1504afd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -552,6 +552,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). ty::FnDef(def_id, args) => { let tcx = self.tcx(); + // FIXME(struct_target_features): should a function that inherits target_features + // through an argument implement Fn traits? if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() && tcx.codegen_fn_attrs(def_id).target_features.is_empty() { diff --git a/src/doc/unstable-book/src/language-features/struct-target-features.md b/src/doc/unstable-book/src/language-features/struct-target-features.md new file mode 100644 index 0000000000000..e814fe9007240 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/struct-target-features.md @@ -0,0 +1,7 @@ +# `struct_target_features` + +The tracking issue for this feature is: [#129107] + +[#129107]: https://github.com/rust-lang/rust/issues/129107 + +------------------------ diff --git a/tests/assembly/struct-target-features.rs b/tests/assembly/struct-target-features.rs new file mode 100644 index 0000000000000..b446c22db854c --- /dev/null +++ b/tests/assembly/struct-target-features.rs @@ -0,0 +1,31 @@ +//@ compile-flags: -O +//@ assembly-output: emit-asm +//@ only-x86_64 + +#![crate_type = "lib"] +#![feature(struct_target_features)] + +// Check that a struct_target_features type causes the compiler to effectively inline intrinsics. + +use std::arch::x86_64::*; + +#[target_feature(enable = "avx")] +struct Avx {} + +#[target_feature(enable = "fma")] +struct Fma {} + +pub fn add_simple(_: Avx, v: __m256) -> __m256 { + // CHECK-NOT: call + // CHECK: vaddps + unsafe { _mm256_add_ps(v, v) } +} + +pub fn add_fma_combined(_: &Avx, _: &Fma, v: __m256) -> (__m256, __m256) { + // CHECK-NOT: call + // CHECK-DAG: vaddps + let r1 = unsafe { _mm256_add_ps(v, v) }; + // CHECK-DAG: vfmadd213ps + let r2 = unsafe { _mm256_fmadd_ps(v, v, v) }; + (r1, r2) +} diff --git a/tests/ui/feature-gates/feature-gate-struct-target-features.rs b/tests/ui/feature-gates/feature-gate-struct-target-features.rs new file mode 100644 index 0000000000000..854948811469b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-struct-target-features.rs @@ -0,0 +1,4 @@ +#[target_feature(enable = "avx")] //~ ERROR attribute should be applied to a function definition +struct Avx {} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-struct-target-features.stderr b/tests/ui/feature-gates/feature-gate-struct-target-features.stderr new file mode 100644 index 0000000000000..1e18d3ee1e18d --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-struct-target-features.stderr @@ -0,0 +1,10 @@ +error: attribute should be applied to a function definition + --> $DIR/feature-gate-struct-target-features.rs:1:1 + | +LL | #[target_feature(enable = "avx")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | struct Avx {} + | ------------- not a function definition + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/auxiliary/struct-target-features-crate-dep.rs b/tests/ui/target-feature/auxiliary/struct-target-features-crate-dep.rs new file mode 100644 index 0000000000000..2c5ab9503b0d7 --- /dev/null +++ b/tests/ui/target-feature/auxiliary/struct-target-features-crate-dep.rs @@ -0,0 +1,6 @@ +#![feature(struct_target_features)] + +#[target_feature(enable = "avx")] +pub struct Avx {} + +pub struct NoFeatures {} diff --git a/tests/ui/target-feature/struct-target-features-crate.rs b/tests/ui/target-feature/struct-target-features-crate.rs new file mode 100644 index 0000000000000..84bc02b927939 --- /dev/null +++ b/tests/ui/target-feature/struct-target-features-crate.rs @@ -0,0 +1,23 @@ +//@ only-x86_64 +//@ aux-build: struct-target-features-crate-dep.rs +//@ check-pass +#![feature(target_feature_11)] + +extern crate struct_target_features_crate_dep; + +#[target_feature(enable = "avx")] +fn avx() {} + +fn f(_: struct_target_features_crate_dep::Avx) { + avx(); +} + +fn g(_: struct_target_features_crate_dep::NoFeatures) {} + +fn main() { + if is_x86_feature_detected!("avx") { + let avx = unsafe { struct_target_features_crate_dep::Avx {} }; + f(avx); + } + g(struct_target_features_crate_dep::NoFeatures {}); +} diff --git a/tests/ui/target-feature/struct-target-features.rs b/tests/ui/target-feature/struct-target-features.rs new file mode 100644 index 0000000000000..c74d0edad58ca --- /dev/null +++ b/tests/ui/target-feature/struct-target-features.rs @@ -0,0 +1,100 @@ +//@ only-x86_64 +#![feature(struct_target_features)] +//~^ WARNING the feature `struct_target_features` is incomplete and may not be safe to use and/or cause compiler crashes +#![feature(target_feature_11)] + +use std::arch::x86_64::*; + +#[target_feature(enable = "avx")] +struct Invalid(u32); + +#[target_feature(enable = "avx")] +struct Avx {} + +#[target_feature(enable = "sse")] +struct Sse(); + +#[target_feature(enable = "avx")] +fn avx() {} + +trait TFAssociatedType { + type Assoc; +} + +impl TFAssociatedType for () { + type Assoc = Avx; +} + +fn avx_self(_: <() as TFAssociatedType>::Assoc) { + avx(); +} + +fn avx_avx(_: Avx) { + avx(); +} + +extern "C" fn bad_fun(_: Avx) {} +//~^ ERROR cannot use a struct with target features in a function with non-Rust ABI + +#[inline(always)] +//~^ ERROR cannot use `#[inline(always)]` with `#[target_feature]` +fn inline_fun(_: Avx) {} +//~^ ERROR cannot use a struct with target features in a #[inline(always)] function + +trait Simd { + fn do_something(&self); +} + +impl Simd for Avx { + fn do_something(&self) { + unsafe { + println!("{:?}", _mm256_setzero_ps()); + } + } +} + +impl Simd for Sse { + fn do_something(&self) { + unsafe { + println!("{:?}", _mm_setzero_ps()); + } + } +} + +struct WithAvx { + #[allow(dead_code)] + avx: Avx, +} + +impl Simd for WithAvx { + fn do_something(&self) { + unsafe { + println!("{:?}", _mm256_setzero_ps()); + } + } +} + +#[inline(never)] +fn dosomething(simd: &S) { + simd.do_something(); +} + +fn avxfn(_: &Avx) { + // This is not unsafe because we already have the feature at function-level. + let _ = Avx {}; +} + +fn main() { + Avx {}; + //~^ ERROR initializing type `Avx` with `#[target_feature]` is unsafe and requires unsafe function or block [E0133] + + if is_x86_feature_detected!("avx") { + let avx = unsafe { Avx {} }; + avxfn(&avx); + dosomething(&avx); + dosomething(&WithAvx { avx }); + } + if is_x86_feature_detected!("sse") { + dosomething(&unsafe { Sse {} }) + } +} diff --git a/tests/ui/target-feature/struct-target-features.stderr b/tests/ui/target-feature/struct-target-features.stderr new file mode 100644 index 0000000000000..b4fbd557ab3e0 --- /dev/null +++ b/tests/ui/target-feature/struct-target-features.stderr @@ -0,0 +1,38 @@ +warning: the feature `struct_target_features` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/struct-target-features.rs:2:12 + | +LL | #![feature(struct_target_features)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #129107 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: cannot use a struct with target features in a function with non-Rust ABI + --> $DIR/struct-target-features.rs:36:1 + | +LL | extern "C" fn bad_fun(_: Avx) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: cannot use a struct with target features in a #[inline(always)] function + --> $DIR/struct-target-features.rs:41:1 + | +LL | fn inline_fun(_: Avx) {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: cannot use `#[inline(always)]` with `#[target_feature]` + --> $DIR/struct-target-features.rs:39:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + +error[E0133]: initializing type `Avx` with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/struct-target-features.rs:88:5 + | +LL | Avx {}; + | ^^^^^^ call to function with `#[target_feature]` + | + = note: the target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + +error: aborting due to 4 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0133`.