Skip to content

Commit

Permalink
Implement RFC 3525.
Browse files Browse the repository at this point in the history
  • Loading branch information
veluca93 committed Aug 4, 2024
1 parent 64ebd39 commit 4c6b058
Show file tree
Hide file tree
Showing 15 changed files with 377 additions and 18 deletions.
89 changes: 80 additions & 9 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::codes::*;
use rustc_errors::{struct_span_code_err, DiagMessage, SubdiagMessage};
use rustc_hir as hir;
Expand All @@ -16,8 +17,10 @@ use rustc_middle::ty::{self as ty, TyCtxt};
use rustc_session::lint;
use rustc_session::parse::feature_err;
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span};
use rustc_span::{sym, Span, Symbol};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::{abi, SanitizerSet};
use rustc_type_ir::inherent::*;

use crate::errors;
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
Expand Down Expand Up @@ -78,23 +81,26 @@ 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.
// In these cases, we bail from performing further checks that are only meaningful for
// 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 {
Expand Down Expand Up @@ -613,6 +619,57 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
}

if tcx.features().struct_target_features
&& 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: 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);
if ty.is_ref() {
reachable_types.push(ty.builtin_deref(false).unwrap());
} else if matches!(ty.kind(), ty::Tuple(_)) {
reachable_types.extend(ty.tuple_fields().iter());
} else if let ty::Adt(adt_def, args) = ty.kind() {
additional_tf.extend_from_slice(tcx.struct_target_features(adt_def.did()));
if adt_def.is_struct() {
reachable_types.extend(
adt_def
.variant(VariantIdx::from_usize(0))
.fields
.iter()
.map(|field| field.ty(tcx, args)),
);
}
}
}

if !additional_tf.is_empty() && !sig.skip_binder().abi().is_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_from_slice(&additional_tf);
}

// If a function uses #[target_feature] 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
Expand Down Expand Up @@ -758,6 +815,20 @@ fn check_link_name_xor_ordinal(
}
}

fn struct_target_features(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<Symbol> {
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);
}
features
}

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,
..*providers
};
}
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,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.
(unstable, struct_target_features, "CURRENT_RUSTC_VERSION", Some(871212)) /*FIXME*/,
/// 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.
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,11 @@ rustc_queries! {
feedable
}

query struct_target_features(def_id: DefId) -> &'tcx Vec<Symbol> {
arena_cache
desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) }
}

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) }
}
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
.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 =
use of inline assembly is unsafe and requires unsafe block
.note = inline assembly is entirely unchecked and can cause undefined behavior
Expand Down Expand Up @@ -384,6 +395,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
Expand Down
38 changes: 34 additions & 4 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,10 +510,16 @@ 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),
}
if !self.tcx.struct_target_features(adt_def.did()).is_empty() {
self.requires_unsafe(expr.span, ConstructingTargetFeaturesType)
}
}

ExprKind::Closure(box ClosureExpr {
closure_id,
args: _,
Expand Down Expand Up @@ -615,6 +621,7 @@ enum UnsafeOpKind {
CallToUnsafeFunction(Option<DefId>),
UseOfInlineAssembly,
InitializingTypeWith,
ConstructingTargetFeaturesType,
UseOfMutableStatic,
UseOfExternStatic,
DerefOfRawPointer,
Expand Down Expand Up @@ -696,6 +703,15 @@ impl UnsafeOpKind {
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(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
Expand Down Expand Up @@ -853,6 +869,20 @@ impl UnsafeOpKind {
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 => {
dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span,
Expand Down
35 changes: 35 additions & 0 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
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)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)]
#[note]
Expand Down Expand Up @@ -251,6 +261,17 @@ pub(crate) struct InitializingTypeWithRequiresUnsafe {
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)]
#[diag(
mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
Expand All @@ -265,6 +286,20 @@ pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
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)]
#[diag(mir_build_mutable_static_requires_unsafe, code = E0133)]
#[note]
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,10 @@ passes_should_be_applied_to_fn =
*[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 =
attribute should be applied to a static
.label = not a static
Expand Down
33 changes: 28 additions & 5 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,12 +737,35 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Target::Field | Target::Arm | Target::MacroDef => {
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 {
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::AttrShouldBeAppliedToFnOrUnitStruct {
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,
});
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ pub struct AttrShouldBeAppliedToFn {
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)]
#[diag(passes_should_be_applied_to_fn, code = E0739)]
pub struct TrackedCallerWrongLocation {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1845,6 +1845,7 @@ symbols! {
stringify,
struct_field_attributes,
struct_inherit,
struct_target_features,
struct_variant,
structural_match,
structural_peq,
Expand Down
Loading

0 comments on commit 4c6b058

Please sign in to comment.