Skip to content

Commit ab6ad4d

Browse files
committed
WIP: first try at implementing RFC 3525.
1 parent 3d68afc commit ab6ad4d

File tree

18 files changed

+267
-31
lines changed

18 files changed

+267
-31
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+85-14
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::*, struct_span_code_err, DiagMessage, SubdiagMessage};
45
use rustc_hir as hir;
56
use rustc_hir::def::DefKind;
@@ -13,8 +14,10 @@ use rustc_middle::query::Providers;
1314
use rustc_middle::ty::{self as ty, TyCtxt};
1415
use rustc_session::{lint, parse::feature_err};
1516
use rustc_span::symbol::Ident;
16-
use rustc_span::{sym, Span};
17+
use rustc_span::{sym, Span, Symbol};
18+
use rustc_target::abi::VariantIdx;
1719
use rustc_target::spec::{abi, SanitizerSet};
20+
use rustc_type_ir::inherent::*;
1821

1922
use crate::errors;
2023
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 {
7578
let mut link_ordinal_span = None;
7679
let mut no_sanitize_span = None;
7780

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

97103
let Some(Ident { name, .. }) = attr.ident() else {
@@ -610,6 +616,57 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
610616
}
611617
}
612618

619+
if tcx.features().struct_target_features
620+
&& let Some(sig) = fn_sig_outer()
621+
{
622+
// Collect target features from types reachable from arguments.
623+
// We define a type as "reachable" if:
624+
// - it is a function argument
625+
// - it is a field of a reachable struct
626+
// - there is a reachable reference to it
627+
// FIXME: we may want to cache the result of this computation.
628+
let mut visited_types = FxHashSet::default();
629+
let mut reachable_types: Vec<_> = sig.skip_binder().inputs().skip_binder().to_owned();
630+
let mut additional_tf = vec![];
631+
632+
while let Some(ty) = reachable_types.pop() {
633+
if visited_types.contains(&ty) {
634+
continue;
635+
}
636+
visited_types.insert(ty);
637+
if ty.is_ref() {
638+
reachable_types.push(ty.builtin_deref(false).unwrap());
639+
} else if matches!(ty.kind(), ty::Tuple(_)) {
640+
reachable_types.extend(ty.tuple_fields().iter());
641+
} else if let ty::Adt(adt_def, args) = ty.kind() {
642+
additional_tf.extend_from_slice(&adt_def.target_features());
643+
if adt_def.is_struct() {
644+
reachable_types.extend(
645+
adt_def
646+
.variant(VariantIdx::from_usize(0))
647+
.fields
648+
.iter()
649+
.map(|field| field.ty(tcx, args)),
650+
);
651+
}
652+
}
653+
}
654+
655+
if !additional_tf.is_empty() && !sig.skip_binder().abi().is_rust() {
656+
tcx.dcx().span_err(
657+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
658+
"cannot use a struct with target features in a function with non-Rust ABI",
659+
);
660+
}
661+
if !additional_tf.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always {
662+
tcx.dcx().span_err(
663+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
664+
"cannot use a struct with target features in a #[inline(always)] function",
665+
);
666+
}
667+
codegen_fn_attrs.target_features.extend_from_slice(&additional_tf);
668+
}
669+
613670
// If a function uses #[target_feature] it can't be inlined into general
614671
// purpose functions as they wouldn't have the right target features
615672
// enabled. For that reason we also forbid #[inline(always)] as it can't be
@@ -755,6 +812,20 @@ fn check_link_name_xor_ordinal(
755812
}
756813
}
757814

815+
fn struct_target_features(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<Symbol> {
816+
let mut features = vec![];
817+
let supported_features = tcx.supported_target_features(def_id.krate);
818+
for attr in tcx.get_attrs(def_id, sym::target_feature) {
819+
from_target_feature(tcx, attr, supported_features, &mut features);
820+
}
821+
features
822+
}
823+
758824
pub fn provide(providers: &mut Providers) {
759-
*providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
825+
*providers = Providers {
826+
codegen_fn_attrs,
827+
should_inherit_track_caller,
828+
struct_target_features,
829+
..*providers
830+
};
760831
}

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,8 @@ declare_features! (
602602
(unstable, strict_provenance, "1.61.0", Some(95228)),
603603
/// Allows string patterns to dereference values to match them.
604604
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
605+
/// Allows structs to carry target_feature information.
606+
(unstable, struct_target_features, "CURRENT_RUSTC_VERSION", Some(871212)) /*FIXME*/,
605607
/// Allows the use of `#[target_feature]` on safe functions.
606608
(unstable, target_feature_11, "1.45.0", Some(69098)),
607609
/// Allows using `#[thread_local]` on `static` items.

compiler/rustc_hir_analysis/src/collect.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
11471147
} else {
11481148
tcx.repr_options_of_def(def_id)
11491149
};
1150-
let (kind, variants) = match &item.kind {
1150+
let (kind, variants, features) = match &item.kind {
11511151
ItemKind::Enum(def, _) => {
11521152
let mut distance_from_explicit = 0;
11531153
let variants = def
@@ -1175,7 +1175,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
11751175
})
11761176
.collect();
11771177

1178-
(AdtKind::Enum, variants)
1178+
(AdtKind::Enum, variants, vec![])
11791179
}
11801180
ItemKind::Struct(def, _) | ItemKind::Union(def, _) => {
11811181
let adt_kind = match item.kind {
@@ -1194,11 +1194,11 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
11941194
))
11951195
.collect();
11961196

1197-
(adt_kind, variants)
1197+
(adt_kind, variants, tcx.struct_target_features(def_id).clone())
11981198
}
11991199
_ => bug!("{:?} is not an ADT", item.owner_id.def_id),
12001200
};
1201-
tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, is_anonymous)
1201+
tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, features, is_anonymous)
12021202
}
12031203

12041204
fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {

compiler/rustc_metadata/src/rmeta/decoder.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
11791179
adt_kind,
11801180
variants.into_iter().map(|(_, variant)| variant).collect(),
11811181
repr,
1182+
self.root
1183+
.tables
1184+
.adt_target_features
1185+
.get(self, item_id)
1186+
.expect("target features not encoded")
1187+
.decode(self)
1188+
.collect(),
11821189
false,
11831190
)
11841191
}

compiler/rustc_metadata/src/rmeta/encoder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
15681568
record!(self.tables.fn_sig[variant.def_id] <- fn_sig);
15691569
}
15701570
}
1571+
record_array!(self.tables.adt_target_features[def_id] <- adt_def.target_features());
15711572
}
15721573

15731574
#[instrument(level = "debug", skip(self))]

compiler/rustc_metadata/src/rmeta/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ define_tables! {
459459
def_keys: Table<DefIndex, LazyValue<DefKey>>,
460460
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
461461
variant_data: Table<DefIndex, LazyValue<VariantData>>,
462+
adt_target_features: Table<DefIndex, LazyArray<Symbol>>,
462463
assoc_container: Table<DefIndex, ty::AssocItemContainer>,
463464
macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>,
464465
proc_macro: Table<DefIndex, MacroKind>,

compiler/rustc_middle/src/query/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,11 @@ rustc_queries! {
12531253
feedable
12541254
}
12551255

1256+
query struct_target_features(def_id: DefId) -> &'tcx Vec<Symbol> {
1257+
arena_cache
1258+
desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) }
1259+
}
1260+
12561261
query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> {
12571262
desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) }
12581263
}

compiler/rustc_middle/src/ty/adt.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
1616
use rustc_query_system::ich::StableHashingContext;
1717
use rustc_session::DataTypeKind;
1818
use rustc_span::symbol::sym;
19+
use rustc_span::Symbol;
1920
use rustc_target::abi::{ReprOptions, VariantIdx, FIRST_VARIANT};
2021
use tracing::{debug, info, trace};
2122

@@ -103,6 +104,8 @@ pub struct AdtDefData {
103104
flags: AdtFlags,
104105
/// Repr options provided by the user.
105106
repr: ReprOptions,
107+
/// Target features that functions taking objects of this type by value will enable.
108+
target_features: Vec<Symbol>,
106109
}
107110

108111
impl PartialEq for AdtDefData {
@@ -115,8 +118,8 @@ impl PartialEq for AdtDefData {
115118
// definition of `AdtDefData` changes, a compile-error will be produced,
116119
// reminding us to revisit this assumption.
117120

118-
let Self { did: self_def_id, variants: _, flags: _, repr: _ } = self;
119-
let Self { did: other_def_id, variants: _, flags: _, repr: _ } = other;
121+
let Self { did: self_def_id, variants: _, flags: _, repr: _, target_features: _ } = self;
122+
let Self { did: other_def_id, variants: _, flags: _, repr: _, target_features: _ } = other;
120123

121124
let res = self_def_id == other_def_id;
122125

@@ -153,13 +156,15 @@ impl<'a> HashStable<StableHashingContext<'a>> for AdtDefData {
153156
let addr = self as *const AdtDefData as usize;
154157
let hashing_controls = hcx.hashing_controls();
155158
*cache.borrow_mut().entry((addr, hashing_controls)).or_insert_with(|| {
156-
let ty::AdtDefData { did, ref variants, ref flags, ref repr } = *self;
159+
let ty::AdtDefData { did, ref variants, ref flags, ref repr, ref target_features } =
160+
*self;
157161

158162
let mut hasher = StableHasher::new();
159163
did.hash_stable(hcx, &mut hasher);
160164
variants.hash_stable(hcx, &mut hasher);
161165
flags.hash_stable(hcx, &mut hasher);
162166
repr.hash_stable(hcx, &mut hasher);
167+
target_features.hash_stable(hcx, &mut hasher);
163168

164169
hasher.finish()
165170
})
@@ -198,6 +203,11 @@ impl<'tcx> AdtDef<'tcx> {
198203
pub fn repr(self) -> ReprOptions {
199204
self.0.0.repr
200205
}
206+
207+
#[inline]
208+
pub fn target_features(self) -> &'tcx Vec<Symbol> {
209+
&self.0.0.target_features
210+
}
201211
}
202212

203213
impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
@@ -260,6 +270,7 @@ impl AdtDefData {
260270
kind: AdtKind,
261271
variants: IndexVec<VariantIdx, VariantDef>,
262272
repr: ReprOptions,
273+
target_features: Vec<Symbol>,
263274
is_anonymous: bool,
264275
) -> Self {
265276
debug!(
@@ -302,7 +313,7 @@ impl AdtDefData {
302313
flags |= AdtFlags::IS_ANONYMOUS;
303314
}
304315

305-
AdtDefData { did, variants, flags, repr }
316+
AdtDefData { did, variants, flags, repr, target_features }
306317
}
307318
}
308319

compiler/rustc_middle/src/ty/context.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,7 @@ impl<'tcx> TyCtxt<'tcx> {
14291429
kind: AdtKind,
14301430
variants: IndexVec<VariantIdx, ty::VariantDef>,
14311431
repr: ReprOptions,
1432+
features: Vec<Symbol>,
14321433
is_anonymous: bool,
14331434
) -> ty::AdtDef<'tcx> {
14341435
self.mk_adt_def_from_data(ty::AdtDefData::new(
@@ -1437,6 +1438,7 @@ impl<'tcx> TyCtxt<'tcx> {
14371438
kind,
14381439
variants,
14391440
repr,
1441+
features,
14401442
is_anonymous,
14411443
))
14421444
}

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
@@ -377,6 +388,11 @@ mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe =
377388
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
378389
.label = initializing type with `rustc_layout_scalar_valid_range` attr
379390
391+
mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe =
392+
initializing type with `target_feature` attr is unsafe and requires unsafe block
393+
.note = this struct can only be constructed if the corresponding `target_feature`s are available
394+
.label = initializing type with `target_feature` attr
395+
380396
mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe =
381397
use of inline assembly is unsafe and requires unsafe block
382398
.note = inline assembly is entirely unchecked and can cause undefined behavior

0 commit comments

Comments
 (0)