Skip to content

Commit 341f603

Browse files
committed
Auto merge of rust-lang#134353 - oli-obk:safe-target-feature-unsafe-by-default, r=wesleywiser
Treat safe target_feature functions as unsafe by default [less invasive variant] This unblocks * rust-lang#134090 As I stated in rust-lang#134090 (comment) I think the previous impl was too easy to get wrong, as by default it treated safe target feature functions as safe and had to add additional checks for when they weren't. Now the logic is inverted. By default they are unsafe and you have to explicitly handle safe target feature functions. This is the less (imo) invasive variant of rust-lang#134317, as it doesn't require changing the Safety enum, so it only affects FnDefs and nothing else, as it should.
2 parents 2776bdf + 767d4fe commit 341f603

File tree

39 files changed

+319
-120
lines changed

39 files changed

+319
-120
lines changed

compiler/rustc_ast_lowering/src/delegation.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
188188
) -> hir::FnSig<'hir> {
189189
let header = if let Some(local_sig_id) = sig_id.as_local() {
190190
match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
191-
Some(sig) => self.lower_fn_header(sig.header, hir::Safety::Safe),
191+
Some(sig) => self.lower_fn_header(
192+
sig.header,
193+
// HACK: we override the default safety instead of generating attributes from the ether.
194+
// We are not forwarding the attributes, as the delegation fn sigs are collected on the ast,
195+
// and here we need the hir attributes.
196+
if sig.target_feature { hir::Safety::Unsafe } else { hir::Safety::Safe },
197+
&[],
198+
),
192199
None => self.generate_header_error(),
193200
}
194201
} else {
@@ -198,7 +205,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
198205
Asyncness::No => hir::IsAsync::NotAsync,
199206
};
200207
hir::FnHeader {
201-
safety: sig.safety,
208+
safety: if self.tcx.codegen_fn_attrs(sig_id).safe_target_features {
209+
hir::HeaderSafety::SafeTargetFeatures
210+
} else {
211+
hir::HeaderSafety::Normal(sig.safety)
212+
},
202213
constness: self.tcx.constness(sig_id),
203214
asyncness,
204215
abi: sig.abi,
@@ -384,7 +395,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
384395

385396
fn generate_header_error(&self) -> hir::FnHeader {
386397
hir::FnHeader {
387-
safety: hir::Safety::Safe,
398+
safety: hir::Safety::Safe.into(),
388399
constness: hir::Constness::NotConst,
389400
asyncness: hir::IsAsync::NotAsync,
390401
abi: abi::Abi::Rust,

compiler/rustc_ast_lowering/src/item.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
231231
});
232232
let sig = hir::FnSig {
233233
decl,
234-
header: this.lower_fn_header(*header, hir::Safety::Safe),
234+
header: this.lower_fn_header(*header, hir::Safety::Safe, attrs),
235235
span: this.lower_span(*fn_sig_span),
236236
};
237237
hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() }
@@ -610,7 +610,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
610610
fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
611611
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
612612
let owner_id = hir_id.expect_owner();
613-
self.lower_attrs(hir_id, &i.attrs);
613+
let attrs = self.lower_attrs(hir_id, &i.attrs);
614614
let item = hir::ForeignItem {
615615
owner_id,
616616
ident: self.lower_ident(i.ident),
@@ -634,7 +634,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
634634
});
635635

636636
// Unmarked safety in unsafe block defaults to unsafe.
637-
let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe);
637+
let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs);
638638

639639
hir::ForeignItemKind::Fn(
640640
hir::FnSig { header, decl, span: self.lower_span(sig.span) },
@@ -776,6 +776,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
776776
i.id,
777777
FnDeclKind::Trait,
778778
sig.header.coroutine_kind,
779+
attrs,
779780
);
780781
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
781782
}
@@ -795,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
795796
i.id,
796797
FnDeclKind::Trait,
797798
sig.header.coroutine_kind,
799+
attrs,
798800
);
799801
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
800802
}
@@ -911,6 +913,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
911913
i.id,
912914
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
913915
sig.header.coroutine_kind,
916+
attrs,
914917
);
915918

916919
(generics, hir::ImplItemKind::Fn(sig, body_id))
@@ -1339,8 +1342,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
13391342
id: NodeId,
13401343
kind: FnDeclKind,
13411344
coroutine_kind: Option<CoroutineKind>,
1345+
attrs: &[hir::Attribute],
13421346
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
1343-
let header = self.lower_fn_header(sig.header, hir::Safety::Safe);
1347+
let header = self.lower_fn_header(sig.header, hir::Safety::Safe, attrs);
13441348
let itctx = ImplTraitContext::Universal;
13451349
let (generics, decl) = self.lower_generics(generics, id, itctx, |this| {
13461350
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
@@ -1352,14 +1356,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
13521356
&mut self,
13531357
h: FnHeader,
13541358
default_safety: hir::Safety,
1359+
attrs: &[hir::Attribute],
13551360
) -> hir::FnHeader {
13561361
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
13571362
hir::IsAsync::Async(span)
13581363
} else {
13591364
hir::IsAsync::NotAsync
13601365
};
1366+
1367+
let safety = self.lower_safety(h.safety, default_safety);
1368+
1369+
// Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
1370+
let safety = if attrs.iter().any(|attr| attr.has_name(sym::target_feature))
1371+
&& safety.is_safe()
1372+
&& !self.tcx.sess.target.is_like_wasm
1373+
{
1374+
hir::HeaderSafety::SafeTargetFeatures
1375+
} else {
1376+
safety.into()
1377+
};
1378+
13611379
hir::FnHeader {
1362-
safety: self.lower_safety(h.safety, default_safety),
1380+
safety,
13631381
asyncness,
13641382
constness: self.lower_constness(h.constness),
13651383
abi: self.lower_extern(h.ext),

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
250250
}
251251
}
252252
sym::target_feature => {
253-
if !tcx.is_closure_like(did.to_def_id())
254-
&& let Some(fn_sig) = fn_sig()
255-
&& fn_sig.skip_binder().safety().is_safe()
256-
{
253+
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
254+
tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn");
255+
continue;
256+
};
257+
let safe_target_features =
258+
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
259+
codegen_fn_attrs.safe_target_features = safe_target_features;
260+
if safe_target_features {
257261
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
258262
// The `#[target_feature]` attribute is allowed on
259263
// WebAssembly targets on all functions, including safe

compiler/rustc_hir/src/hir.rs

+34-2
Original file line numberDiff line numberDiff line change
@@ -3762,9 +3762,30 @@ impl fmt::Display for Constness {
37623762
}
37633763
}
37643764

3765+
/// The actualy safety specified in syntax. We may treat
3766+
/// its safety different within the type system to create a
3767+
/// "sound by default" system that needs checking this enum
3768+
/// explicitly to allow unsafe operations.
3769+
#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)]
3770+
pub enum HeaderSafety {
3771+
/// A safe function annotated with `#[target_features]`.
3772+
/// The type system treats this function as an unsafe function,
3773+
/// but safety checking will check this enum to treat it as safe
3774+
/// and allowing calling other safe target feature functions with
3775+
/// the same features without requiring an additional unsafe block.
3776+
SafeTargetFeatures,
3777+
Normal(Safety),
3778+
}
3779+
3780+
impl From<Safety> for HeaderSafety {
3781+
fn from(v: Safety) -> Self {
3782+
Self::Normal(v)
3783+
}
3784+
}
3785+
37653786
#[derive(Copy, Clone, Debug, HashStable_Generic)]
37663787
pub struct FnHeader {
3767-
pub safety: Safety,
3788+
pub safety: HeaderSafety,
37683789
pub constness: Constness,
37693790
pub asyncness: IsAsync,
37703791
pub abi: ExternAbi,
@@ -3780,7 +3801,18 @@ impl FnHeader {
37803801
}
37813802

37823803
pub fn is_unsafe(&self) -> bool {
3783-
self.safety.is_unsafe()
3804+
self.safety().is_unsafe()
3805+
}
3806+
3807+
pub fn is_safe(&self) -> bool {
3808+
self.safety().is_safe()
3809+
}
3810+
3811+
pub fn safety(&self) -> Safety {
3812+
match self.safety {
3813+
HeaderSafety::SafeTargetFeatures => Safety::Unsafe,
3814+
HeaderSafety::Normal(safety) => safety,
3815+
}
37843816
}
37853817
}
37863818

compiler/rustc_hir_analysis/src/collect.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -1336,7 +1336,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
13361336
{
13371337
icx.lowerer().lower_fn_ty(
13381338
hir_id,
1339-
sig.header.safety,
1339+
sig.header.safety(),
13401340
sig.header.abi,
13411341
sig.decl,
13421342
Some(generics),
@@ -1351,13 +1351,18 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
13511351
kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
13521352
generics,
13531353
..
1354-
}) => {
1355-
icx.lowerer().lower_fn_ty(hir_id, header.safety, header.abi, decl, Some(generics), None)
1356-
}
1354+
}) => icx.lowerer().lower_fn_ty(
1355+
hir_id,
1356+
header.safety(),
1357+
header.abi,
1358+
decl,
1359+
Some(generics),
1360+
None,
1361+
),
13571362

13581363
ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(sig, _, _), .. }) => {
13591364
let abi = tcx.hir().get_foreign_abi(hir_id);
1360-
compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety)
1365+
compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety())
13611366
}
13621367

13631368
Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => {
@@ -1405,7 +1410,7 @@ fn lower_fn_sig_recovering_infer_ret_ty<'tcx>(
14051410

14061411
icx.lowerer().lower_fn_ty(
14071412
icx.tcx().local_def_id_to_hir_id(def_id),
1408-
sig.header.safety,
1413+
sig.header.safety(),
14091414
sig.header.abi,
14101415
sig.decl,
14111416
Some(generics),

compiler/rustc_hir_pretty/src/lib.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -2407,7 +2407,7 @@ impl<'a> State<'a> {
24072407
self.print_fn(
24082408
decl,
24092409
hir::FnHeader {
2410-
safety,
2410+
safety: safety.into(),
24112411
abi,
24122412
constness: hir::Constness::NotConst,
24132413
asyncness: hir::IsAsync::NotAsync,
@@ -2423,12 +2423,20 @@ impl<'a> State<'a> {
24232423
fn print_fn_header_info(&mut self, header: hir::FnHeader) {
24242424
self.print_constness(header.constness);
24252425

2426+
let safety = match header.safety {
2427+
hir::HeaderSafety::SafeTargetFeatures => {
2428+
self.word_nbsp("#[target_feature]");
2429+
hir::Safety::Safe
2430+
}
2431+
hir::HeaderSafety::Normal(safety) => safety,
2432+
};
2433+
24262434
match header.asyncness {
24272435
hir::IsAsync::NotAsync => {}
24282436
hir::IsAsync::Async(_) => self.word_nbsp("async"),
24292437
}
24302438

2431-
self.print_safety(header.safety);
2439+
self.print_safety(safety);
24322440

24332441
if header.abi != ExternAbi::Rust {
24342442
self.word_nbsp("extern");

compiler/rustc_hir_typeck/src/coercion.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -932,10 +932,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
932932
return Err(TypeError::ForceInlineCast);
933933
}
934934

935-
// Safe `#[target_feature]` functions are not assignable to safe fn pointers
936-
// (RFC 2396).
935+
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
936+
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
937+
return Err(TypeError::ForceInlineCast);
938+
}
939+
940+
// FIXME(target_feature): Safe `#[target_feature]` functions could be cast to safe fn pointers (RFC 2396),
941+
// as you can already write that "cast" in user code by wrapping a target_feature fn call in a closure,
942+
// which is safe. This is sound because you already need to be executing code that is satisfying the target
943+
// feature constraints..
937944
if b_hdr.safety.is_safe()
938-
&& !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
945+
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
939946
{
940947
return Err(TypeError::TargetFeatureCast(def_id));
941948
}

compiler/rustc_hir_typeck/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ fn typeck_with_fallback<'tcx>(
139139
// type that has an infer in it, lower the type directly so that it'll
140140
// be correctly filled with infer. We'll use this inference to provide
141141
// a suggestion later on.
142-
fcx.lowerer().lower_fn_ty(id, header.safety, header.abi, decl, None, None)
142+
fcx.lowerer().lower_fn_ty(id, header.safety(), header.abi, decl, None, None)
143143
} else {
144144
tcx.fn_sig(def_id).instantiate_identity()
145145
};

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub struct CodegenFnAttrs {
3030
/// features (only enabled features are supported right now).
3131
/// Implied target features have already been applied.
3232
pub target_features: Vec<TargetFeature>,
33+
/// Whether the function was declared safe, but has target features
34+
pub safe_target_features: bool,
3335
/// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
3436
pub linkage: Option<Linkage>,
3537
/// The `#[linkage = "..."]` attribute on foreign items and the value we found.
@@ -150,6 +152,7 @@ impl CodegenFnAttrs {
150152
link_name: None,
151153
link_ordinal: None,
152154
target_features: vec![],
155+
safe_target_features: false,
153156
linkage: None,
154157
import_linkage: None,
155158
link_section: None,

compiler/rustc_middle/src/ty/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ pub struct DelegationFnSig {
222222
pub param_count: usize,
223223
pub has_self: bool,
224224
pub c_variadic: bool,
225+
pub target_feature: bool,
225226
}
226227

227228
#[derive(Clone, Copy, Debug)]

compiler/rustc_middle/src/ty/print/pretty.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
690690
if with_reduced_queries() {
691691
p!(print_def_path(def_id, args));
692692
} else {
693-
let sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args);
693+
let mut sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args);
694+
if self.tcx().codegen_fn_attrs(def_id).safe_target_features {
695+
p!("#[target_features] ");
696+
sig = sig.map_bound(|mut sig| {
697+
sig.safety = hir::Safety::Safe;
698+
sig
699+
});
700+
}
694701
p!(print(sig), " {{", print_value_path(def_id, args), "}}");
695702
}
696703
}

0 commit comments

Comments
 (0)