Skip to content

Commit d898105

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

File tree

16 files changed

+378
-19
lines changed

16 files changed

+378
-19
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+80-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,57 @@ 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+
if ty.is_ref() {
641+
reachable_types.push(ty.builtin_deref(false).unwrap());
642+
} else if matches!(ty.kind(), ty::Tuple(_)) {
643+
reachable_types.extend(ty.tuple_fields().iter());
644+
} else if let ty::Adt(adt_def, args) = ty.kind() {
645+
additional_tf.extend_from_slice(tcx.struct_target_features(adt_def.did()));
646+
if adt_def.is_struct() {
647+
reachable_types.extend(
648+
adt_def
649+
.variant(VariantIdx::from_usize(0))
650+
.fields
651+
.iter()
652+
.map(|field| field.ty(tcx, args)),
653+
);
654+
}
655+
}
656+
}
657+
658+
if !additional_tf.is_empty() && !sig.skip_binder().abi().is_rust() {
659+
tcx.dcx().span_err(
660+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
661+
"cannot use a struct with target features in a function with non-Rust ABI",
662+
);
663+
}
664+
if !additional_tf.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always {
665+
tcx.dcx().span_err(
666+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
667+
"cannot use a struct with target features in a #[inline(always)] function",
668+
);
669+
}
670+
codegen_fn_attrs.target_features.extend_from_slice(&additional_tf);
671+
}
672+
616673
// If a function uses #[target_feature] it can't be inlined into general
617674
// purpose functions as they wouldn't have the right target features
618675
// enabled. For that reason we also forbid #[inline(always)] as it can't be
@@ -758,6 +815,20 @@ fn check_link_name_xor_ordinal(
758815
}
759816
}
760817

818+
fn struct_target_features(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<Symbol> {
819+
let mut features = vec![];
820+
let supported_features = tcx.supported_target_features(LOCAL_CRATE);
821+
for attr in tcx.get_attrs(def_id, sym::target_feature) {
822+
from_target_feature(tcx, attr, supported_features, &mut features);
823+
}
824+
features
825+
}
826+
761827
pub fn provide(providers: &mut Providers) {
762-
*providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
828+
*providers = Providers {
829+
codegen_fn_attrs,
830+
should_inherit_track_caller,
831+
struct_target_features,
832+
..*providers
833+
};
763834
}

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_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 Vec<Symbol> {
1250+
arena_cache
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,

compiler/rustc_mir_build/src/errors.rs

+35
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
8787
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
8888
}
8989

90+
#[derive(LintDiagnostic)]
91+
#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe, code = E0133)]
92+
#[note]
93+
pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithTargetFeatureRequiresUnsafe {
94+
#[label]
95+
pub(crate) span: Span,
96+
#[subdiagnostic]
97+
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
98+
}
99+
90100
#[derive(LintDiagnostic)]
91101
#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)]
92102
#[note]
@@ -251,6 +261,17 @@ pub(crate) struct InitializingTypeWithRequiresUnsafe {
251261
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
252262
}
253263

264+
#[derive(Diagnostic)]
265+
#[diag(mir_build_initializing_type_with_target_feature_requires_unsafe, code = E0133)]
266+
#[note]
267+
pub(crate) struct InitializingTypeWithTargetFeatureRequiresUnsafe {
268+
#[primary_span]
269+
#[label]
270+
pub(crate) span: Span,
271+
#[subdiagnostic]
272+
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
273+
}
274+
254275
#[derive(Diagnostic)]
255276
#[diag(
256277
mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
@@ -265,6 +286,20 @@ pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
265286
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
266287
}
267288

289+
#[derive(Diagnostic)]
290+
#[diag(
291+
mir_build_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
292+
code = E0133
293+
)]
294+
#[note]
295+
pub(crate) struct InitializingTypeWithTargetFeatureRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
296+
#[primary_span]
297+
#[label]
298+
pub(crate) span: Span,
299+
#[subdiagnostic]
300+
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
301+
}
302+
268303
#[derive(Diagnostic)]
269304
#[diag(mir_build_mutable_static_requires_unsafe, code = E0133)]
270305
#[note]

compiler/rustc_passes/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ passes_should_be_applied_to_fn =
659659
*[false] not a function definition
660660
}
661661
662+
passes_should_be_applied_to_fn_or_unit_struct =
663+
attribute should be applied to a function definition or unit struct
664+
.label = not a function definition or a unit struct
665+
662666
passes_should_be_applied_to_static =
663667
attribute should be applied to a static
664668
.label = not a static

compiler/rustc_passes/src/check_attr.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -737,12 +737,35 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
737737
Target::Field | Target::Arm | Target::MacroDef => {
738738
self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
739739
}
740+
Target::Struct if self.tcx.features().struct_target_features => {
741+
let ty = self.tcx.hir_node(hir_id).expect_item();
742+
match ty.kind {
743+
ItemKind::Struct(data, _) => {
744+
if data.fields().len() != 0 {
745+
self.dcx().emit_err(errors::AttrShouldBeAppliedToFnOrUnitStruct {
746+
attr_span: attr.span,
747+
defn_span: span,
748+
});
749+
}
750+
}
751+
_ => {
752+
panic!("Target::Struct for a non-struct");
753+
}
754+
}
755+
}
740756
_ => {
741-
self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
742-
attr_span: attr.span,
743-
defn_span: span,
744-
on_crate: hir_id == CRATE_HIR_ID,
745-
});
757+
if self.tcx.features().struct_target_features {
758+
self.dcx().emit_err(errors::AttrShouldBeAppliedToFnOrUnitStruct {
759+
attr_span: attr.span,
760+
defn_span: span,
761+
});
762+
} else {
763+
self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
764+
attr_span: attr.span,
765+
defn_span: span,
766+
on_crate: hir_id == CRATE_HIR_ID,
767+
});
768+
}
746769
}
747770
}
748771
}

compiler/rustc_passes/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ pub struct AttrShouldBeAppliedToFn {
8282
pub on_crate: bool,
8383
}
8484

85+
#[derive(Diagnostic)]
86+
#[diag(passes_should_be_applied_to_fn_or_unit_struct)]
87+
pub struct AttrShouldBeAppliedToFnOrUnitStruct {
88+
#[primary_span]
89+
pub attr_span: Span,
90+
#[label]
91+
pub defn_span: Span,
92+
}
93+
8594
#[derive(Diagnostic)]
8695
#[diag(passes_should_be_applied_to_fn, code = E0739)]
8796
pub struct TrackedCallerWrongLocation {

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,7 @@ symbols! {
18451845
stringify,
18461846
struct_field_attributes,
18471847
struct_inherit,
1848+
struct_target_features,
18481849
struct_variant,
18491850
structural_match,
18501851
structural_peq,

0 commit comments

Comments
 (0)