Skip to content

Commit 7aba3ad

Browse files
committed
Implement RFC 3525.
1 parent 64ebd39 commit 7aba3ad

File tree

20 files changed

+442
-19
lines changed

20 files changed

+442
-19
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+105-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
22
use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
3+
use rustc_data_structures::fx::FxHashSet;
34
use rustc_errors::codes::*;
45
use rustc_errors::{struct_span_code_err, DiagMessage, SubdiagMessage};
56
use rustc_hir as hir;
@@ -16,8 +17,10 @@ use rustc_middle::ty::{self as ty, TyCtxt};
1617
use rustc_session::lint;
1718
use rustc_session::parse::feature_err;
1819
use rustc_span::symbol::Ident;
19-
use rustc_span::{sym, Span};
20+
use rustc_span::{sym, Span, Symbol};
21+
use rustc_target::abi::VariantIdx;
2022
use rustc_target::spec::{abi, SanitizerSet};
23+
use rustc_type_ir::inherent::*;
2124

2225
use crate::errors;
2326
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
@@ -78,23 +81,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
7881
let mut link_ordinal_span = None;
7982
let mut no_sanitize_span = None;
8083

84+
let fn_sig_outer = || {
85+
use DefKind::*;
86+
87+
let def_kind = tcx.def_kind(did);
88+
if let Fn | AssocFn | Variant | Ctor(..) = def_kind { Some(tcx.fn_sig(did)) } else { None }
89+
};
90+
8191
for attr in attrs.iter() {
8292
// In some cases, attribute are only valid on functions, but it's the `check_attr`
8393
// pass that check that they aren't used anywhere else, rather this module.
8494
// In these cases, we bail from performing further checks that are only meaningful for
8595
// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
8696
// report a delayed bug, just in case `check_attr` isn't doing its job.
8797
let fn_sig = || {
88-
use DefKind::*;
89-
90-
let def_kind = tcx.def_kind(did);
91-
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
92-
Some(tcx.fn_sig(did))
93-
} else {
98+
let sig = fn_sig_outer();
99+
if sig.is_none() {
94100
tcx.dcx()
95101
.span_delayed_bug(attr.span, "this attribute can only be applied to functions");
96-
None
97102
}
103+
sig
98104
};
99105

100106
let Some(Ident { name, .. }) = attr.ident() else {
@@ -613,6 +619,82 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
613619
}
614620
}
615621

622+
if tcx.features().struct_target_features
623+
&& let Some(sig) = fn_sig_outer()
624+
{
625+
// Collect target features from types reachable from arguments.
626+
// We define a type as "reachable" if:
627+
// - it is a function argument
628+
// - it is a field of a reachable struct
629+
// - there is a reachable reference to it
630+
// FIXME: we may want to cache the result of this computation.
631+
let mut visited_types = FxHashSet::default();
632+
let mut reachable_types: Vec<_> = sig.skip_binder().inputs().skip_binder().to_owned();
633+
let mut additional_tf = vec![];
634+
635+
while let Some(ty) = reachable_types.pop() {
636+
if visited_types.contains(&ty) {
637+
continue;
638+
}
639+
visited_types.insert(ty);
640+
match ty.kind() {
641+
ty::Ref(..) => reachable_types.push(ty.builtin_deref(false).unwrap()),
642+
ty::Tuple(..) => reachable_types.extend(ty.tuple_fields().iter()),
643+
ty::Adt(adt_def, args) => {
644+
additional_tf.extend_from_slice(tcx.struct_target_features(adt_def.did()));
645+
if adt_def.is_struct() {
646+
reachable_types.extend(
647+
adt_def
648+
.variant(VariantIdx::from_usize(0))
649+
.fields
650+
.iter()
651+
.map(|field| field.ty(tcx, args)),
652+
);
653+
}
654+
}
655+
ty::Bool
656+
| ty::Char
657+
| ty::Int(..)
658+
| ty::Uint(..)
659+
| ty::Float(..)
660+
| ty::Foreign(..)
661+
| ty::Str
662+
| ty::Array(..)
663+
| ty::Pat(..)
664+
| ty::Slice(..)
665+
| ty::RawPtr(..)
666+
| ty::FnDef(..)
667+
| ty::FnPtr(..)
668+
| ty::Dynamic(..)
669+
| ty::Closure(..)
670+
| ty::CoroutineClosure(..)
671+
| ty::Coroutine(..)
672+
| ty::CoroutineWitness(..)
673+
| ty::Never
674+
| ty::Alias(..)
675+
| ty::Param(..)
676+
| ty::Bound(..)
677+
| ty::Placeholder(..)
678+
| ty::Infer(..)
679+
| ty::Error(..) => (),
680+
}
681+
}
682+
683+
if !additional_tf.is_empty() && !sig.skip_binder().abi().is_rust() {
684+
tcx.dcx().span_err(
685+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
686+
"cannot use a struct with target features in a function with non-Rust ABI",
687+
);
688+
}
689+
if !additional_tf.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always {
690+
tcx.dcx().span_err(
691+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
692+
"cannot use a struct with target features in a #[inline(always)] function",
693+
);
694+
}
695+
codegen_fn_attrs.target_features.extend_from_slice(&additional_tf);
696+
}
697+
616698
// If a function uses #[target_feature] it can't be inlined into general
617699
// purpose functions as they wouldn't have the right target features
618700
// enabled. For that reason we also forbid #[inline(always)] as it can't be
@@ -758,6 +840,20 @@ fn check_link_name_xor_ordinal(
758840
}
759841
}
760842

843+
fn struct_target_features(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[Symbol] {
844+
let mut features = vec![];
845+
let supported_features = tcx.supported_target_features(LOCAL_CRATE);
846+
for attr in tcx.get_attrs(def_id, sym::target_feature) {
847+
from_target_feature(tcx, attr, supported_features, &mut features);
848+
}
849+
tcx.arena.alloc_slice(&features)
850+
}
851+
761852
pub fn provide(providers: &mut Providers) {
762-
*providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
853+
*providers = Providers {
854+
codegen_fn_attrs,
855+
should_inherit_track_caller,
856+
struct_target_features,
857+
..*providers
858+
};
763859
}

compiler/rustc_feature/src/builtin_attrs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
471471
ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::Yes),
472472
ungated!(
473473
target_feature, Normal, template!(List: r#"enable = "name""#),
474-
DuplicatesOk, EncodeCrossCrate::No,
474+
DuplicatesOk, EncodeCrossCrate::Yes,
475475
),
476476
ungated!(track_caller, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes),
477477
ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding, EncodeCrossCrate::No),

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,8 @@ declare_features! (
605605
(unstable, strict_provenance, "1.61.0", Some(95228)),
606606
/// Allows string patterns to dereference values to match them.
607607
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
608+
/// Allows structs to carry target_feature information.
609+
(unstable, struct_target_features, "CURRENT_RUSTC_VERSION", Some(871212)) /*FIXME*/,
608610
/// Allows the use of `#[target_feature]` on safe functions.
609611
(unstable, target_feature_11, "1.45.0", Some(69098)),
610612
/// Allows using `#[thread_local]` on `static` items.

compiler/rustc_hir/src/def.rs

+34
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,40 @@ impl DefKind {
319319
| DefKind::ExternCrate => false,
320320
}
321321
}
322+
323+
/// Whether `query struct_target_features` should be used with this definition.
324+
pub fn has_struct_target_features(self) -> bool {
325+
match self {
326+
DefKind::Struct | DefKind::Union | DefKind::Enum => true,
327+
DefKind::Fn
328+
| DefKind::AssocFn
329+
| DefKind::Ctor(..)
330+
| DefKind::Closure
331+
| DefKind::Static { .. }
332+
| DefKind::Mod
333+
| DefKind::Variant
334+
| DefKind::Trait
335+
| DefKind::TyAlias
336+
| DefKind::ForeignTy
337+
| DefKind::TraitAlias
338+
| DefKind::AssocTy
339+
| DefKind::Const
340+
| DefKind::AssocConst
341+
| DefKind::Macro(..)
342+
| DefKind::Use
343+
| DefKind::ForeignMod
344+
| DefKind::OpaqueTy
345+
| DefKind::Impl { .. }
346+
| DefKind::Field
347+
| DefKind::TyParam
348+
| DefKind::ConstParam
349+
| DefKind::LifetimeParam
350+
| DefKind::AnonConst
351+
| DefKind::InlineConst
352+
| DefKind::GlobalAsm
353+
| DefKind::ExternCrate => false,
354+
}
355+
}
322356
}
323357

324358
/// The resolution of a path or export.

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ provide! { tcx, def_id, other, cdata,
224224
variances_of => { table }
225225
fn_sig => { table }
226226
codegen_fn_attrs => { table }
227+
struct_target_features => { table }
227228
impl_trait_header => { table }
228229
const_param_default => { table }
229230
object_lifetime_default => { table }

compiler/rustc_metadata/src/rmeta/encoder.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
13801380
if def_kind.has_codegen_attrs() {
13811381
record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id));
13821382
}
1383+
if def_kind.has_struct_target_features() {
1384+
record_array!(self.tables.struct_target_features[def_id] <- self.tcx.struct_target_features(def_id));
1385+
}
13831386
if should_encode_visibility(def_kind) {
13841387
let vis =
13851388
self.tcx.local_visibility(local_id).map_id(|def_id| def_id.local_def_index);

compiler/rustc_metadata/src/rmeta/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ define_tables! {
427427
variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
428428
fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::PolyFnSig<'static>>>>,
429429
codegen_fn_attrs: Table<DefIndex, LazyValue<CodegenFnAttrs>>,
430+
struct_target_features: Table<DefIndex, LazyArray<Symbol>>,
430431
impl_trait_header: Table<DefIndex, LazyValue<ty::ImplTraitHeader<'static>>>,
431432
const_param_default: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, rustc_middle::ty::Const<'static>>>>,
432433
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,

compiler/rustc_middle/src/query/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,11 @@ rustc_queries! {
12461246
feedable
12471247
}
12481248

1249+
query struct_target_features(def_id: DefId) -> &'tcx [Symbol] {
1250+
separate_provide_extern
1251+
desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) }
1252+
}
1253+
12491254
query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> {
12501255
desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) }
12511256
}

compiler/rustc_mir_build/messages.ftl

+16
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,17 @@ mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed
125125
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
126126
.label = initializing type with `rustc_layout_scalar_valid_range` attr
127127
128+
mir_build_initializing_type_with_target_feature_requires_unsafe =
129+
initializing type with `target_feature` attr is unsafe and requires unsafe block
130+
.note = this struct can only be constructed if the corresponding `target_feature`s are available
131+
.label = initializing type with `target_feature` attr
132+
133+
mir_build_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
134+
initializing type with `target_feature` attr is unsafe and requires unsafe function or block
135+
.note = this struct can only be constructed if the corresponding `target_feature`s are available
136+
.label = initializing type with `target_feature` attr
137+
138+
128139
mir_build_inline_assembly_requires_unsafe =
129140
use of inline assembly is unsafe and requires unsafe block
130141
.note = inline assembly is entirely unchecked and can cause undefined behavior
@@ -384,6 +395,11 @@ mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe =
384395
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
385396
.label = initializing type with `rustc_layout_scalar_valid_range` attr
386397
398+
mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe =
399+
initializing type with `target_feature` attr is unsafe and requires unsafe block
400+
.note = this struct can only be constructed if the corresponding `target_feature`s are available
401+
.label = initializing type with `target_feature` attr
402+
387403
mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe =
388404
use of inline assembly is unsafe and requires unsafe block
389405
.note = inline assembly is entirely unchecked and can cause undefined behavior

compiler/rustc_mir_build/src/check_unsafety.rs

+34-4
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,16 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
510510
user_ty: _,
511511
fields: _,
512512
base: _,
513-
}) => match self.tcx.layout_scalar_valid_range(adt_def.did()) {
514-
(Bound::Unbounded, Bound::Unbounded) => {}
515-
_ => self.requires_unsafe(expr.span, InitializingTypeWith),
516-
},
513+
}) => {
514+
match self.tcx.layout_scalar_valid_range(adt_def.did()) {
515+
(Bound::Unbounded, Bound::Unbounded) => {}
516+
_ => self.requires_unsafe(expr.span, InitializingTypeWith),
517+
}
518+
if !self.tcx.struct_target_features(adt_def.did()).is_empty() {
519+
self.requires_unsafe(expr.span, ConstructingTargetFeaturesType)
520+
}
521+
}
522+
517523
ExprKind::Closure(box ClosureExpr {
518524
closure_id,
519525
args: _,
@@ -615,6 +621,7 @@ enum UnsafeOpKind {
615621
CallToUnsafeFunction(Option<DefId>),
616622
UseOfInlineAssembly,
617623
InitializingTypeWith,
624+
ConstructingTargetFeaturesType,
618625
UseOfMutableStatic,
619626
UseOfExternStatic,
620627
DerefOfRawPointer,
@@ -696,6 +703,15 @@ impl UnsafeOpKind {
696703
unsafe_not_inherited_note,
697704
},
698705
),
706+
ConstructingTargetFeaturesType => tcx.emit_node_span_lint(
707+
UNSAFE_OP_IN_UNSAFE_FN,
708+
hir_id,
709+
span,
710+
UnsafeOpInUnsafeFnInitializingTypeWithTargetFeatureRequiresUnsafe {
711+
span,
712+
unsafe_not_inherited_note,
713+
},
714+
),
699715
UseOfMutableStatic => tcx.emit_node_span_lint(
700716
UNSAFE_OP_IN_UNSAFE_FN,
701717
hir_id,
@@ -853,6 +869,20 @@ impl UnsafeOpKind {
853869
unsafe_not_inherited_note,
854870
});
855871
}
872+
ConstructingTargetFeaturesType if unsafe_op_in_unsafe_fn_allowed => {
873+
dcx.emit_err(
874+
InitializingTypeWithTargetFeatureRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
875+
span,
876+
unsafe_not_inherited_note,
877+
},
878+
);
879+
}
880+
ConstructingTargetFeaturesType => {
881+
dcx.emit_err(InitializingTypeWithTargetFeatureRequiresUnsafe {
882+
span,
883+
unsafe_not_inherited_note,
884+
});
885+
}
856886
UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
857887
dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
858888
span,

0 commit comments

Comments
 (0)