Skip to content

Commit

Permalink
WIP: first try at implementing RFC 3525.
Browse files Browse the repository at this point in the history
  • Loading branch information
veluca93 committed Jul 15, 2024
1 parent 9dcaa7f commit 1966290
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 29 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4056,6 +4056,7 @@ dependencies = [
"rustc_arena",
"rustc_ast",
"rustc_attr",
"rustc_codegen_ssa",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
Expand Down
81 changes: 69 additions & 12 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::*, struct_span_code_err, DiagMessage, SubdiagMessage};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
Expand All @@ -14,7 +15,9 @@ use rustc_middle::ty::{self as ty, TyCtxt};
use rustc_session::{lint, parse::feature_err};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::{abi, SanitizerSet};
use rustc_type_ir::inherent::Abi;

use crate::errors;
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
Expand Down Expand Up @@ -75,23 +78,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;

// 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_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 @@ -610,6 +616,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
// TODO: presumably we want to cache the result of this computation.

Check failure on line 627 in compiler/rustc_codegen_ssa/src/codegen_attrs.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
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(&adt_def.target_features());
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
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,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. TODO

Check failure on line 605 in compiler/rustc_feature/src/unstable.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
(unstable, struct_target_features, "CURRENT_RUSTC_VERSION", None),

Check failure on line 606 in compiler/rustc_feature/src/unstable.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

no tracking issue for feature struct_target_features

Check failure on line 606 in compiler/rustc_feature/src/unstable.rs

View workflow job for this annotation

GitHub Actions / PR - x86_64-gnu-llvm-17

no tracking issue for feature struct_target_features
/// 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
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ itertools = "0.12"
rustc_arena = { path = "../rustc_arena" }
rustc_ast = { path = "../rustc_ast" }
rustc_attr = { path = "../rustc_attr" }
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
Expand Down
19 changes: 15 additions & 4 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
} else {
tcx.repr_options_of_def(def_id)
};
let (kind, variants) = match &item.kind {
let (kind, variants, features) = match &item.kind {
ItemKind::Enum(def, _) => {
let mut distance_from_explicit = 0;
let variants = def
Expand Down Expand Up @@ -1157,7 +1157,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
})
.collect();

(AdtKind::Enum, variants)
(AdtKind::Enum, variants, vec![])
}
ItemKind::Struct(def, _) | ItemKind::Union(def, _) => {
let adt_kind = match item.kind {
Expand All @@ -1176,11 +1176,22 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
))
.collect();

(adt_kind, variants)
let mut features = vec![];
let supported_features = tcx.supported_target_features(rustc_span::def_id::LOCAL_CRATE);
for attr in tcx.get_attrs(def_id, sym::target_feature) {
rustc_codegen_ssa::target_features::from_target_feature(
tcx,
attr,
supported_features,
&mut features,
);
}

(adt_kind, variants, features)
}
_ => bug!("{:?} is not an ADT", item.owner_id.def_id),
};
tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, is_anonymous)
tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, features, is_anonymous)
}

fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
adt_kind,
variants.into_iter().map(|(_, variant)| variant).collect(),
repr,
self.root
.tables
.adt_target_features
.get(self, item_id)
.expect("target features not encoded")
.decode(self)
.collect(),
false,
)
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
record!(self.tables.fn_sig[variant.def_id] <- fn_sig);
}
}
record_array!(self.tables.adt_target_features[def_id] <- adt_def.target_features());
}

#[instrument(level = "debug", skip(self))]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ define_tables! {
def_keys: Table<DefIndex, LazyValue<DefKey>>,
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
variant_data: Table<DefIndex, LazyValue<VariantData>>,
adt_target_features: Table<DefIndex, LazyArray<Symbol>>,
assoc_container: Table<DefIndex, ty::AssocItemContainer>,
macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>,
proc_macro: Table<DefIndex, MacroKind>,
Expand Down
19 changes: 15 additions & 4 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_query_system::ich::StableHashingContext;
use rustc_session::DataTypeKind;
use rustc_span::symbol::sym;
use rustc_span::Symbol;
use rustc_target::abi::{ReprOptions, VariantIdx, FIRST_VARIANT};
use tracing::{debug, info, trace};

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

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

let Self { did: self_def_id, variants: _, flags: _, repr: _ } = self;
let Self { did: other_def_id, variants: _, flags: _, repr: _ } = other;
let Self { did: self_def_id, variants: _, flags: _, repr: _, target_features: _ } = self;
let Self { did: other_def_id, variants: _, flags: _, repr: _, target_features: _ } = other;

let res = self_def_id == other_def_id;

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

let mut hasher = StableHasher::new();
did.hash_stable(hcx, &mut hasher);
variants.hash_stable(hcx, &mut hasher);
flags.hash_stable(hcx, &mut hasher);
repr.hash_stable(hcx, &mut hasher);
target_features.hash_stable(hcx, &mut hasher);

hasher.finish()
})
Expand Down Expand Up @@ -198,6 +203,11 @@ impl<'tcx> AdtDef<'tcx> {
pub fn repr(self) -> ReprOptions {
self.0.0.repr
}

#[inline]
pub fn target_features(self) -> &'tcx Vec<Symbol> {
&self.0.0.target_features
}
}

impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
Expand Down Expand Up @@ -260,6 +270,7 @@ impl AdtDefData {
kind: AdtKind,
variants: IndexVec<VariantIdx, VariantDef>,
repr: ReprOptions,
target_features: Vec<Symbol>,
is_anonymous: bool,
) -> Self {
debug!(
Expand Down Expand Up @@ -302,7 +313,7 @@ impl AdtDefData {
flags |= AdtFlags::IS_ANONYMOUS;
}

AdtDefData { did, variants, flags, repr }
AdtDefData { did, variants, flags, repr, target_features }
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,7 @@ impl<'tcx> TyCtxt<'tcx> {
kind: AdtKind,
variants: IndexVec<VariantIdx, ty::VariantDef>,
repr: ReprOptions,
features: Vec<Symbol>,
is_anonymous: bool,
) -> ty::AdtDef<'tcx> {
self.mk_adt_def_from_data(ty::AdtDefData::new(
Expand All @@ -1422,6 +1423,7 @@ impl<'tcx> TyCtxt<'tcx> {
kind,
variants,
repr,
features,
is_anonymous,
))
}
Expand Down
37 changes: 33 additions & 4 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,10 +492,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 !adt_def.target_features().is_empty() {
self.requires_unsafe(expr.span, ConstructingTargetFeaturesType)
}
}

ExprKind::Closure(box ClosureExpr {
closure_id,
args: _,
Expand Down Expand Up @@ -597,6 +603,7 @@ enum UnsafeOpKind {
CallToUnsafeFunction(Option<DefId>),
UseOfInlineAssembly,
InitializingTypeWith,
ConstructingTargetFeaturesType,
UseOfMutableStatic,
UseOfExternStatic,
DerefOfRawPointer,
Expand Down Expand Up @@ -678,6 +685,16 @@ impl UnsafeOpKind {
unsafe_not_inherited_note,
},
),
ConstructingTargetFeaturesType => tcx.emit_node_span_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
// TODO: better message

Check failure on line 692 in compiler/rustc_mir_build/src/check_unsafety.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
span,
unsafe_not_inherited_note,
},
),
UseOfMutableStatic => tcx.emit_node_span_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
Expand Down Expand Up @@ -835,6 +852,18 @@ impl UnsafeOpKind {
unsafe_not_inherited_note,
});
}
ConstructingTargetFeaturesType if unsafe_op_in_unsafe_fn_allowed => {
dcx.emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span,
unsafe_not_inherited_note,
});
}
ConstructingTargetFeaturesType => {
dcx.emit_err(InitializingTypeWithRequiresUnsafe {
span,
unsafe_not_inherited_note,
});
}
UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span,
Expand Down
Loading

0 comments on commit 1966290

Please sign in to comment.