Auto merge of #127537 - veluca93:struct_tf, r=BoxyUwU

Implement a first version of RFC 3525: struct target features

This PR is an attempt at implementing https://github.com/rust-lang/rfcs/pull/3525, behind a feature gate `struct_target_features`.

There's obviously a few tasks that ought to be done before this is merged; in no particular order:
- add proper error messages
- add tests
- create a tracking issue for the RFC
- properly serialize/deserialize the new target_features field in `rmeta` (assuming I even understood that correctly :-))

That said, as I am definitely not a `rustc` expert, I'd like to get some early feedback on the overall approach before fixing those things (and perhaps some pointers for `rmeta`...), hence this early PR :-)

Here's an example piece of code that I have been using for testing - with the new code, the calls to intrinsics get correctly inlined:
```rust
#![feature(struct_target_features)]

use std::arch::x86_64::*;

/*
// fails to compile
#[target_feature(enable = "avx")]
struct Invalid(u32);
*/

#[target_feature(enable = "avx")]
struct Avx {}

#[target_feature(enable = "sse")]
struct Sse();

/*
// fails to compile
extern "C" fn bad_fun(_: Avx) {}
*/

/*
// fails to compile
#[inline(always)]
fn inline_fun(_: Avx) {}
*/

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<S: Simd>(simd: &S) {
    simd.do_something();
}

fn main() {
    /*
    // fails to compile
    Avx {};
    */

    if is_x86_feature_detected!("avx") {
        let avx = unsafe { Avx {} };
        dosomething(&avx);
        dosomething(&WithAvx { avx });
    }
    if is_x86_feature_detected!("sse") {
        dosomething(&unsafe { Sse {} })
    }
}
```

Tracking:

- https://github.com/rust-lang/rust/issues/129107
This commit is contained in:
bors 2024-08-28 22:54:55 +00:00
commit acb4e8b625
25 changed files with 512 additions and 28 deletions

View File

@ -1,5 +1,6 @@
use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem}; use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{struct_span_code_err, DiagMessage, SubdiagMessage}; use rustc_errors::{struct_span_code_err, DiagMessage, SubdiagMessage};
use rustc_hir as hir; use rustc_hir as hir;
@ -8,7 +9,7 @@
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
use rustc_hir::{lang_items, LangItem}; use rustc_hir::{lang_items, LangItem};
use rustc_middle::middle::codegen_fn_attrs::{ use rustc_middle::middle::codegen_fn_attrs::{
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, TargetFeature,
}; };
use rustc_middle::mir::mono::Linkage; use rustc_middle::mir::mono::Linkage;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
@ -17,6 +18,7 @@
use rustc_session::parse::feature_err; use rustc_session::parse::feature_err;
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{sym, Span}; use rustc_span::{sym, Span};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::{abi, SanitizerSet}; use rustc_target::spec::{abi, SanitizerSet};
use crate::errors; use crate::errors;
@ -78,6 +80,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let mut link_ordinal_span = None; let mut link_ordinal_span = None;
let mut no_sanitize_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() { for attr in attrs.iter() {
// In some cases, attribute are only valid on functions, but it's the `check_attr` // 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. // pass that check that they aren't used anywhere else, rather this module.
@ -85,16 +94,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 // 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. // report a delayed bug, just in case `check_attr` isn't doing its job.
let fn_sig = || { let fn_sig = || {
use DefKind::*; let sig = fn_sig_outer();
if sig.is_none() {
let def_kind = tcx.def_kind(did);
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
Some(tcx.fn_sig(did))
} else {
tcx.dcx() tcx.dcx()
.span_delayed_bug(attr.span, "this attribute can only be applied to functions"); .span_delayed_bug(attr.span, "this attribute can only be applied to functions");
None
} }
sig
}; };
let Some(Ident { name, .. }) = attr.ident() else { let Some(Ident { name, .. }) = attr.ident() else {
@ -613,7 +618,93 @@ fn emit_error_with_label(
} }
} }
// If a function uses #[target_feature] it can't be inlined into general if 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
// FIXME(struct_target_features): we may 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);
match ty.kind() {
ty::Alias(..) => {
if let Ok(t) =
tcx.try_normalize_erasing_regions(tcx.param_env(did.to_def_id()), ty)
{
reachable_types.push(t)
}
}
ty::Ref(_, inner, _) => reachable_types.push(*inner),
ty::Tuple(tys) => reachable_types.extend(tys.iter()),
ty::Adt(adt_def, args) => {
additional_tf.extend_from_slice(tcx.struct_target_features(adt_def.did()));
// This only recurses into structs as i.e. an Option<TargetFeature> is an ADT
// that doesn't actually always contain a TargetFeature.
if adt_def.is_struct() {
reachable_types.extend(
adt_def
.variant(VariantIdx::from_usize(0))
.fields
.iter()
.map(|field| field.ty(tcx, args)),
);
}
}
ty::Bool
| ty::Char
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Foreign(..)
| ty::Str
| ty::Array(..)
| ty::Pat(..)
| ty::Slice(..)
| ty::RawPtr(..)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::Dynamic(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Never
| ty::Param(..)
| ty::Bound(..)
| ty::Placeholder(..)
| ty::Infer(..)
| ty::Error(..) => (),
}
}
// 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 // 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 // enabled. For that reason we also forbid #[inline(always)] as it can't be
// respected. // respected.
@ -758,6 +849,20 @@ fn check_link_name_xor_ordinal(
} }
} }
pub fn provide(providers: &mut Providers) { fn struct_target_features(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[TargetFeature] {
*providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; 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)
}
pub fn provide(providers: &mut Providers) {
*providers = Providers {
codegen_fn_attrs,
should_inherit_track_caller,
struct_target_features,
..*providers
};
} }

View File

@ -594,6 +594,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
(unstable, strict_provenance, "1.61.0", Some(95228)), (unstable, strict_provenance, "1.61.0", Some(95228)),
/// Allows string patterns to dereference values to match them. /// Allows string patterns to dereference values to match them.
(unstable, string_deref_patterns, "1.67.0", Some(87121)), (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. /// Allows the use of `#[target_feature]` on safe functions.
(unstable, target_feature_11, "1.45.0", Some(69098)), (unstable, target_feature_11, "1.45.0", Some(69098)),
/// Allows using `#[thread_local]` on `static` items. /// Allows using `#[thread_local]` on `static` items.

View File

@ -326,6 +326,41 @@ pub fn has_codegen_attrs(self) -> bool {
| DefKind::ExternCrate => false, | 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 | DefKind::Union | DefKind::Enum => true,
DefKind::Fn
| 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. /// The resolution of a path or export.

View File

@ -850,6 +850,8 @@ fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
} }
// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). // 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 if b_hdr.safety == hir::Safety::Safe
&& !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()

View File

@ -224,6 +224,7 @@ fn into_args(self) -> (DefId, SimplifiedType) {
variances_of => { table } variances_of => { table }
fn_sig => { table } fn_sig => { table }
codegen_fn_attrs => { table } codegen_fn_attrs => { table }
struct_target_features => { table }
impl_trait_header => { table } impl_trait_header => { table }
const_param_default => { table } const_param_default => { table }
object_lifetime_default => { table } object_lifetime_default => { table }

View File

@ -1392,6 +1392,9 @@ fn encode_def_ids(&mut self) {
if def_kind.has_codegen_attrs() { if def_kind.has_codegen_attrs() {
record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id));
} }
if def_kind.has_struct_target_features() {
record_array!(self.tables.struct_target_features[def_id] <- self.tcx.struct_target_features(def_id));
}
if should_encode_visibility(def_kind) { if should_encode_visibility(def_kind) {
let vis = let vis =
self.tcx.local_visibility(local_id).map_id(|def_id| def_id.local_def_index); self.tcx.local_visibility(local_id).map_id(|def_id| def_id.local_def_index);

View File

@ -19,7 +19,7 @@
Decodable, Encodable, MetadataDecodable, MetadataEncodable, TyDecodable, TyEncodable, Decodable, Encodable, MetadataDecodable, MetadataEncodable, TyDecodable, TyEncodable,
}; };
use rustc_middle::metadata::ModChild; 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::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
use rustc_middle::middle::lib_features::FeatureStability; use rustc_middle::middle::lib_features::FeatureStability;
@ -427,6 +427,7 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
variances_of: Table<DefIndex, LazyArray<ty::Variance>>, variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::PolyFnSig<'static>>>>, fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::PolyFnSig<'static>>>>,
codegen_fn_attrs: Table<DefIndex, LazyValue<CodegenFnAttrs>>, codegen_fn_attrs: Table<DefIndex, LazyValue<CodegenFnAttrs>>,
struct_target_features: Table<DefIndex, LazyArray<TargetFeature>>,
impl_trait_header: Table<DefIndex, LazyValue<ty::ImplTraitHeader<'static>>>, impl_trait_header: Table<DefIndex, LazyValue<ty::ImplTraitHeader<'static>>>,
const_param_default: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, rustc_middle::ty::Const<'static>>>>, const_param_default: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, rustc_middle::ty::Const<'static>>>>,
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>, object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,

View File

@ -26,8 +26,8 @@ pub struct CodegenFnAttrs {
/// be set when `link_name` is set. This is for foreign items with the /// be set when `link_name` is set. This is for foreign items with the
/// "raw-dylib" kind. /// "raw-dylib" kind.
pub link_ordinal: Option<u16>, pub link_ordinal: Option<u16>,
/// The `#[target_feature(enable = "...")]` attribute and the enabled /// All the target features that are enabled for this function. Some features might be enabled
/// features (only enabled features are supported right now). /// implicitly.
pub target_features: Vec<TargetFeature>, pub target_features: Vec<TargetFeature>,
/// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
pub linkage: Option<Linkage>, pub linkage: Option<Linkage>,
@ -55,8 +55,8 @@ pub struct CodegenFnAttrs {
pub struct TargetFeature { pub struct TargetFeature {
/// The name of the target feature (e.g. "avx") /// The name of the target feature (e.g. "avx")
pub name: Symbol, pub name: Symbol,
/// The feature is implied by another feature, rather than explicitly added by the /// The feature is implied by another feature or by an argument, rather than explicitly
/// `#[target_feature]` attribute /// added by the `#[target_feature]` attribute
pub implied: bool, pub implied: bool,
} }

View File

@ -47,7 +47,7 @@
use crate::infer::canonical::{self, Canonical}; use crate::infer::canonical::{self, Canonical};
use crate::lint::LintExpectation; use crate::lint::LintExpectation;
use crate::metadata::ModChild; 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::debugger_visualizer::DebuggerVisualizerFile;
use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
use crate::middle::lib_features::LibFeatures; use crate::middle::lib_features::LibFeatures;
@ -1245,6 +1245,11 @@
feedable 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 asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> { query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> {
desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) }
} }

View File

@ -59,6 +59,7 @@ impl $crate::ty::ParameterizedOverTcx for $ty {
std::string::String, std::string::String,
crate::metadata::ModChild, crate::metadata::ModChild,
crate::middle::codegen_fn_attrs::CodegenFnAttrs, crate::middle::codegen_fn_attrs::CodegenFnAttrs,
crate::middle::codegen_fn_attrs::TargetFeature,
crate::middle::debugger_visualizer::DebuggerVisualizerFile, crate::middle::debugger_visualizer::DebuggerVisualizerFile,
crate::middle::exported_symbols::SymbolExportInfo, crate::middle::exported_symbols::SymbolExportInfo,
crate::middle::lib_features::FeatureStability, crate::middle::lib_features::FeatureStability,

View File

@ -125,6 +125,17 @@ mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .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 .label = initializing type with `rustc_layout_scalar_valid_range` attr
mir_build_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_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
initializing type with `target_feature` attr is unsafe and requires unsafe function or 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_inline_assembly_requires_unsafe = mir_build_inline_assembly_requires_unsafe =
use of inline assembly is unsafe and requires unsafe block use of inline assembly is unsafe and requires unsafe block
.note = inline assembly is entirely unchecked and can cause undefined behavior .note = inline assembly is entirely unchecked and can cause undefined behavior
@ -387,6 +398,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 .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 .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 = mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe =
use of inline assembly is unsafe and requires unsafe block use of inline assembly is unsafe and requires unsafe block
.note = inline assembly is entirely unchecked and can cause undefined behavior .note = inline assembly is entirely unchecked and can cause undefined behavior

View File

@ -461,14 +461,18 @@ fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
}; };
self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id)); self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
} else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() { } 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 // the call requires `unsafe`. Don't check this on wasm
// targets, though. For more information on wasm see the // targets, though. For more information on wasm see the
// is_like_wasm check in hir_analysis/src/collect.rs // 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; let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
if !self.tcx.sess.target.options.is_like_wasm if !self.tcx.sess.target.options.is_like_wasm
&& !callee_features.iter().all(|feature| { && !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 let missing: Vec<_> = callee_features
@ -542,10 +546,16 @@ fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
user_ty: _, user_ty: _,
fields: _, fields: _,
base: _, base: _,
}) => match self.tcx.layout_scalar_valid_range(adt_def.did()) { }) => {
(Bound::Unbounded, Bound::Unbounded) => {} match self.tcx.layout_scalar_valid_range(adt_def.did()) {
_ => self.requires_unsafe(expr.span, InitializingTypeWith), (Bound::Unbounded, Bound::Unbounded) => {}
}, _ => self.requires_unsafe(expr.span, InitializingTypeWith),
}
if !self.tcx.struct_target_features(adt_def.did()).is_empty() {
self.requires_unsafe(expr.span, ConstructingTargetFeaturesType)
}
}
ExprKind::Closure(box ClosureExpr { ExprKind::Closure(box ClosureExpr {
closure_id, closure_id,
args: _, args: _,
@ -647,6 +657,7 @@ enum UnsafeOpKind {
CallToUnsafeFunction(Option<DefId>), CallToUnsafeFunction(Option<DefId>),
UseOfInlineAssembly, UseOfInlineAssembly,
InitializingTypeWith, InitializingTypeWith,
ConstructingTargetFeaturesType,
UseOfMutableStatic, UseOfMutableStatic,
UseOfExternStatic, UseOfExternStatic,
DerefOfRawPointer, DerefOfRawPointer,
@ -728,6 +739,15 @@ fn emit_unsafe_op_in_unsafe_fn_lint(
unsafe_not_inherited_note, unsafe_not_inherited_note,
}, },
), ),
ConstructingTargetFeaturesType => tcx.emit_node_span_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnInitializingTypeWithTargetFeatureRequiresUnsafe {
span,
unsafe_not_inherited_note,
},
),
UseOfMutableStatic => tcx.emit_node_span_lint( UseOfMutableStatic => tcx.emit_node_span_lint(
UNSAFE_OP_IN_UNSAFE_FN, UNSAFE_OP_IN_UNSAFE_FN,
hir_id, hir_id,
@ -885,6 +905,20 @@ fn emit_requires_unsafe_err(
unsafe_not_inherited_note, unsafe_not_inherited_note,
}); });
} }
ConstructingTargetFeaturesType if unsafe_op_in_unsafe_fn_allowed => {
dcx.emit_err(
InitializingTypeWithTargetFeatureRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span,
unsafe_not_inherited_note,
},
);
}
ConstructingTargetFeaturesType => {
dcx.emit_err(InitializingTypeWithTargetFeatureRequiresUnsafe {
span,
unsafe_not_inherited_note,
});
}
UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => { UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span, span,

View File

@ -86,6 +86,16 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>, pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
} }
#[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,
#[subdiagnostic]
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
}
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)] #[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)]
#[note] #[note]
@ -250,6 +260,17 @@ pub(crate) struct InitializingTypeWithRequiresUnsafe {
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>, pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
} }
#[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,
#[subdiagnostic]
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag( #[diag(
mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
@ -264,6 +285,20 @@ pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>, pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
} }
#[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,
#[subdiagnostic]
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(mir_build_mutable_static_requires_unsafe, code = E0133)] #[diag(mir_build_mutable_static_requires_unsafe, code = E0133)]
#[note] #[note]

View File

@ -672,6 +672,10 @@ passes_should_be_applied_to_fn =
*[false] not a function definition *[false] not a function definition
} }
passes_should_be_applied_to_fn_or_unit_struct =
attribute should be applied to a function definition or unit struct
.label = not a function definition or a unit struct
passes_should_be_applied_to_static = passes_should_be_applied_to_static =
attribute should be applied to a static attribute should be applied to a static
.label = not a static .label = not a static

View File

@ -747,12 +747,35 @@ fn check_target_feature(
Target::Field | Target::Arm | Target::MacroDef => { Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
} }
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 {
self.dcx().emit_err(errors::AttrShouldBeAppliedToFnOrUnitStruct {
attr_span: attr.span,
defn_span: span,
});
}
}
_ => {
panic!("Target::Struct for a non-struct");
}
}
}
_ => { _ => {
self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { if self.tcx.features().struct_target_features {
attr_span: attr.span, self.dcx().emit_err(errors::AttrShouldBeAppliedToFnOrUnitStruct {
defn_span: span, attr_span: attr.span,
on_crate: hir_id == CRATE_HIR_ID, defn_span: span,
}); });
} else {
self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
attr_span: attr.span,
defn_span: span,
on_crate: hir_id == CRATE_HIR_ID,
});
}
} }
} }
} }

View File

@ -82,6 +82,15 @@ pub struct AttrShouldBeAppliedToFn {
pub on_crate: bool, pub on_crate: bool,
} }
#[derive(Diagnostic)]
#[diag(passes_should_be_applied_to_fn_or_unit_struct)]
pub struct AttrShouldBeAppliedToFnOrUnitStruct {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(passes_should_be_applied_to_fn, code = E0739)] #[diag(passes_should_be_applied_to_fn, code = E0739)]
pub struct TrackedCallerWrongLocation { pub struct TrackedCallerWrongLocation {

View File

@ -1852,6 +1852,7 @@
stringify, stringify,
struct_field_attributes, struct_field_attributes,
struct_inherit, struct_inherit,
struct_target_features,
struct_variant, struct_variant,
structural_match, structural_match,
structural_peq, structural_peq,

View File

@ -438,6 +438,8 @@ pub fn report_selection_error(
let is_target_feature_fn = if let ty::FnDef(def_id, _) = let is_target_feature_fn = if let ty::FnDef(def_id, _) =
*leaf_trait_ref.skip_binder().self_ty().kind() *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() !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
} else { } else {
false false

View File

@ -545,6 +545,8 @@ fn assemble_fn_pointer_candidates(
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
ty::FnDef(def_id, args) => { ty::FnDef(def_id, args) => {
let tcx = self.tcx(); 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() if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
&& tcx.codegen_fn_attrs(def_id).target_features.is_empty() && tcx.codegen_fn_attrs(def_id).target_features.is_empty()
{ {

View File

@ -0,0 +1,7 @@
# `struct_target_features`
The tracking issue for this feature is: [#129107]
[#129107]: https://github.com/rust-lang/rust/issues/129107
------------------------

View File

@ -0,0 +1,37 @@
//@ 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_complex_type(_: (&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)
}

View File

@ -0,0 +1,4 @@
#[target_feature(enable = "avx")] //~ ERROR attribute should be applied to a function definition
struct Avx {}
fn main() {}

View File

@ -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

View File

@ -0,0 +1,98 @@
//@ 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")]
//~^ ERROR attribute should be applied to a function definition or unit struct
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<S: Simd>(simd: &S) {
simd.do_something();
}
fn avxfn(_: &Avx) {}
fn main() {
Avx {};
//~^ ERROR initializing type with `target_feature` attr 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 {} })
}
}

View File

@ -0,0 +1,47 @@
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 <https://github.com/rust-lang/rust/issues/129107> for more information
= note: `#[warn(incomplete_features)]` on by default
error: attribute should be applied to a function definition or unit struct
--> $DIR/struct-target-features.rs:8:1
|
LL | #[target_feature(enable = "avx")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | struct Invalid(u32);
| -------------------- not a function definition or a unit struct
error: cannot use a struct with target features in a function with non-Rust ABI
--> $DIR/struct-target-features.rs:37: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:42:1
|
LL | fn inline_fun(_: Avx) {}
| ^^^^^^^^^^^^^^^^^^^^^
error: cannot use `#[inline(always)]` with `#[target_feature]`
--> $DIR/struct-target-features.rs:40:1
|
LL | #[inline(always)]
| ^^^^^^^^^^^^^^^^^
error[E0133]: initializing type with `target_feature` attr is unsafe and requires unsafe function or block
--> $DIR/struct-target-features.rs:86:5
|
LL | Avx {};
| ^^^^^^ initializing type with `target_feature` attr
|
= note: this struct can only be constructed if the corresponding `target_feature`s are available
error: aborting due to 5 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0133`.