Skip to content

Commit 56178dd

Browse files
committed
Treat safe target_feature functions as unsafe by default
1 parent a907c56 commit 56178dd

File tree

20 files changed

+159
-56
lines changed

20 files changed

+159
-56
lines changed

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 13 additions & 2 deletions
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.into(),
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,

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 19 additions & 5 deletions
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,6 +1356,7 @@ 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)
@@ -1360,7 +1365,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
13601365
};
13611366

13621367
let safety = self.lower_safety(h.safety, default_safety);
1363-
let safety = safety.into();
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+
};
13641378

13651379
hir::FnHeader {
13661380
safety,

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 8 additions & 4 deletions
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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3762,8 +3762,18 @@ 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.
37653769
#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)]
37663770
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,
37673777
Normal(Safety),
37683778
}
37693779

@@ -3800,6 +3810,7 @@ impl FnHeader {
38003810

38013811
pub fn safety(&self) -> Safety {
38023812
match self.safety {
3813+
HeaderSafety::SafeTargetFeatures => Safety::Unsafe,
38033814
HeaderSafety::Normal(safety) => safety,
38043815
}
38053816
}

compiler/rustc_hir_pretty/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,6 +2424,10 @@ impl<'a> State<'a> {
24242424
self.print_constness(header.constness);
24252425

24262426
let safety = match header.safety {
2427+
hir::HeaderSafety::SafeTargetFeatures => {
2428+
self.word_nbsp("#[target_feature]");
2429+
hir::Safety::Safe
2430+
}
24272431
hir::HeaderSafety::Normal(safety) => safety,
24282432
};
24292433

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -932,9 +932,15 @@ 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 (RFC 2396),
936-
// report a better error than a safety mismatch.
937-
// FIXME(target_feature): do this inside `coerce_from_safe_fn`.
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..
938944
if b_hdr.safety.is_safe()
939945
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
940946
{

compiler/rustc_mir_build/src/check_unsafety.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,10 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
11201120
let hir_id = tcx.local_def_id_to_hir_id(def);
11211121
let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
11221122
match fn_sig.header.safety {
1123+
// We typeck the body as safe, but otherwise treat it as unsafe everywhere else.
1124+
// Call sites to other SafeTargetFeatures functions are checked explicitly and don't need
1125+
// to care about safety of the body.
1126+
hir::HeaderSafety::SafeTargetFeatures => SafetyContext::Safe,
11231127
hir::HeaderSafety::Normal(safety) => match safety {
11241128
hir::Safety::Unsafe => SafetyContext::UnsafeFn,
11251129
hir::Safety::Safe => SafetyContext::Safe,

src/librustdoc/clean/types.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,14 +668,25 @@ impl Item {
668668
ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP),
669669
ty::Asyncness::No => hir::IsAsync::NotAsync,
670670
};
671-
hir::FnHeader { safety: sig.safety().into(), abi: sig.abi(), constness, asyncness }
671+
hir::FnHeader {
672+
safety: if tcx.codegen_fn_attrs(def_id).safe_target_features {
673+
hir::HeaderSafety::SafeTargetFeatures
674+
} else {
675+
sig.safety().into()
676+
},
677+
abi: sig.abi(),
678+
constness,
679+
asyncness,
680+
}
672681
}
673682
let header = match self.kind {
674683
ItemKind::ForeignFunctionItem(_, safety) => {
675684
let def_id = self.def_id().unwrap();
676685
let abi = tcx.fn_sig(def_id).skip_binder().abi();
677686
hir::FnHeader {
678-
safety: if abi == ExternAbi::RustIntrinsic {
687+
safety: if tcx.codegen_fn_attrs(def_id).safe_target_features {
688+
hir::HeaderSafety::SafeTargetFeatures
689+
} else if abi == ExternAbi::RustIntrinsic {
679690
intrinsic_operation_unsafety(tcx, def_id.expect_local()).into()
680691
} else {
681692
safety.into()

src/librustdoc/html/format.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,6 +1640,7 @@ impl PrintWithSpace for hir::Safety {
16401640
impl PrintWithSpace for hir::HeaderSafety {
16411641
fn print_with_space(&self) -> &str {
16421642
match self {
1643+
hir::HeaderSafety::SafeTargetFeatures => "",
16431644
hir::HeaderSafety::Normal(safety) => safety.print_with_space(),
16441645
}
16451646
}

tests/ui/async-await/async-closures/fn-exception-target-features.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0277]: the trait bound `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn()` is not satisfied
1+
error[E0277]: the trait bound `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn()` is not satisfied
22
--> $DIR/fn-exception-target-features.rs:16:10
33
|
44
LL | test(target_feature);
5-
| ---- ^^^^^^^^^^^^^^ the trait `AsyncFn()` is not implemented for fn item `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
5+
| ---- ^^^^^^^^^^^^^^ the trait `AsyncFn()` is not implemented for fn item `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
66
| |
77
| required by a bound introduced by this call
88
|

tests/ui/macros/issue-68060.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ fn main() {
33
.map(
44
#[target_feature(enable = "")]
55
//~^ ERROR: attribute should be applied to a function
6-
//~| ERROR: feature named `` is not valid
7-
//~| NOTE: `` is not valid for this target
86
#[track_caller]
97
//~^ ERROR: `#[track_caller]` on closures is currently unstable
108
//~| NOTE: see issue #87417

tests/ui/macros/issue-68060.stderr

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,8 @@ LL | #[target_feature(enable = "")]
77
LL | |_| (),
88
| ------ not a function definition
99

10-
error: the feature named `` is not valid for this target
11-
--> $DIR/issue-68060.rs:4:30
12-
|
13-
LL | #[target_feature(enable = "")]
14-
| ^^^^^^^^^^^ `` is not valid for this target
15-
1610
error[E0658]: `#[track_caller]` on closures is currently unstable
17-
--> $DIR/issue-68060.rs:8:13
11+
--> $DIR/issue-68060.rs:6:13
1812
|
1913
LL | #[track_caller]
2014
| ^^^^^^^^^^^^^^^
@@ -23,6 +17,6 @@ LL | #[track_caller]
2317
= help: add `#![feature(closure_track_caller)]` to the crate attributes to enable
2418
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
2519

26-
error: aborting due to 3 previous errors
20+
error: aborting due to 2 previous errors
2721

2822
For more information about this error, try `rustc --explain E0658`.

tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,9 @@ LL | let foo: fn() = foo;
1010
| expected due to this
1111
|
1212
= note: expected fn pointer `fn()`
13-
found fn item `fn() {foo}`
14-
= note: fn items are distinct from fn pointers
13+
found fn item `unsafe fn() {foo}`
1514
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
16-
help: consider casting to a fn pointer
17-
|
18-
LL | let foo: fn() = foo as fn();
19-
| ~~~~~~~~~~~
15+
= note: unsafe functions cannot be coerced into safe function pointers
2016

2117
error: aborting due to 1 previous error
2218

tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ fn call_once(f: impl FnOnce()) {
2121
}
2222

2323
fn main() {
24-
call(foo); //~ ERROR expected a `Fn()` closure, found `fn() {foo}`
25-
call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `fn() {foo}`
26-
call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `fn() {foo}`
24+
call(foo); //~ ERROR expected a `Fn()` closure, found `unsafe fn() {foo}`
25+
call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `unsafe fn() {foo}`
26+
call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `unsafe fn() {foo}`
2727

2828
call(foo_unsafe);
2929
//~^ ERROR expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}`

tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,50 @@
1-
error[E0277]: expected a `Fn()` closure, found `fn() {foo}`
1+
error[E0277]: expected a `Fn()` closure, found `unsafe fn() {foo}`
22
--> $DIR/fn-traits.rs:24:10
33
|
44
LL | call(foo);
5-
| ---- ^^^ expected an `Fn()` closure, found `fn() {foo}`
5+
| ---- ^^^ call the function in a closure: `|| unsafe { /* code */ }`
66
| |
77
| required by a bound introduced by this call
88
|
9-
= help: the trait `Fn()` is not implemented for fn item `fn() {foo}`
10-
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
9+
= help: the trait `Fn()` is not implemented for fn item `unsafe fn() {foo}`
10+
= note: unsafe function cannot be called generically without an unsafe block
11+
= note: wrap the `unsafe fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
1112
= note: `#[target_feature]` functions do not implement the `Fn` traits
1213
note: required by a bound in `call`
1314
--> $DIR/fn-traits.rs:11:17
1415
|
1516
LL | fn call(f: impl Fn()) {
1617
| ^^^^ required by this bound in `call`
1718

18-
error[E0277]: expected a `FnMut()` closure, found `fn() {foo}`
19+
error[E0277]: expected a `FnMut()` closure, found `unsafe fn() {foo}`
1920
--> $DIR/fn-traits.rs:25:14
2021
|
2122
LL | call_mut(foo);
22-
| -------- ^^^ expected an `FnMut()` closure, found `fn() {foo}`
23+
| -------- ^^^ call the function in a closure: `|| unsafe { /* code */ }`
2324
| |
2425
| required by a bound introduced by this call
2526
|
26-
= help: the trait `FnMut()` is not implemented for fn item `fn() {foo}`
27-
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
27+
= help: the trait `FnMut()` is not implemented for fn item `unsafe fn() {foo}`
28+
= note: unsafe function cannot be called generically without an unsafe block
29+
= note: wrap the `unsafe fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
2830
= note: `#[target_feature]` functions do not implement the `Fn` traits
2931
note: required by a bound in `call_mut`
3032
--> $DIR/fn-traits.rs:15:25
3133
|
3234
LL | fn call_mut(mut f: impl FnMut()) {
3335
| ^^^^^^^ required by this bound in `call_mut`
3436

35-
error[E0277]: expected a `FnOnce()` closure, found `fn() {foo}`
37+
error[E0277]: expected a `FnOnce()` closure, found `unsafe fn() {foo}`
3638
--> $DIR/fn-traits.rs:26:15
3739
|
3840
LL | call_once(foo);
39-
| --------- ^^^ expected an `FnOnce()` closure, found `fn() {foo}`
41+
| --------- ^^^ call the function in a closure: `|| unsafe { /* code */ }`
4042
| |
4143
| required by a bound introduced by this call
4244
|
43-
= help: the trait `FnOnce()` is not implemented for fn item `fn() {foo}`
44-
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
45+
= help: the trait `FnOnce()` is not implemented for fn item `unsafe fn() {foo}`
46+
= note: unsafe function cannot be called generically without an unsafe block
47+
= note: wrap the `unsafe fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
4548
= note: `#[target_feature]` functions do not implement the `Fn` traits
4649
note: required by a bound in `call_once`
4750
--> $DIR/fn-traits.rs:19:22

0 commit comments

Comments
 (0)