Skip to content

Commit fcab4fb

Browse files
Make #[target_feature] safe always on WASM
Even when the feature isn't enabled, as it's not UB to invoke an undefined feature in WASM (just a trap).
1 parent 6315e31 commit fcab4fb

File tree

5 files changed

+69
-11
lines changed

5 files changed

+69
-11
lines changed

crates/hir-ty/src/diagnostics/unsafe_check.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ use hir_def::{
1515
use span::Edition;
1616

1717
use crate::{
18-
InferenceResult, Interner, TargetFeatures, TyExt, TyKind, db::HirDatabase,
19-
utils::is_fn_unsafe_to_call,
18+
InferenceResult, Interner, TargetFeatures, TyExt, TyKind,
19+
db::HirDatabase,
20+
utils::{is_fn_unsafe_to_call, target_feature_is_safe_in_target},
2021
};
2122

2223
#[derive(Debug, Default)]
@@ -144,6 +145,9 @@ struct UnsafeVisitor<'db> {
144145
def_target_features: TargetFeatures,
145146
// FIXME: This needs to be the edition of the span of each call.
146147
edition: Edition,
148+
/// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when
149+
/// the target feature is not enabled. This flag encodes that.
150+
target_feature_is_safe: bool,
147151
}
148152

149153
impl<'db> UnsafeVisitor<'db> {
@@ -159,7 +163,12 @@ impl<'db> UnsafeVisitor<'db> {
159163
DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())),
160164
_ => TargetFeatures::default(),
161165
};
162-
let edition = resolver.module().krate().data(db).edition;
166+
let krate = resolver.module().krate();
167+
let edition = krate.data(db).edition;
168+
let target_feature_is_safe = match &krate.workspace_data(db).target {
169+
Ok(target) => target_feature_is_safe_in_target(target),
170+
Err(_) => false,
171+
};
163172
Self {
164173
db,
165174
infer,
@@ -172,6 +181,7 @@ impl<'db> UnsafeVisitor<'db> {
172181
callback: unsafe_expr_cb,
173182
def_target_features,
174183
edition,
184+
target_feature_is_safe,
175185
}
176186
}
177187

@@ -184,7 +194,13 @@ impl<'db> UnsafeVisitor<'db> {
184194
}
185195

186196
fn check_call(&mut self, node: ExprId, func: FunctionId) {
187-
let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition);
197+
let unsafety = is_fn_unsafe_to_call(
198+
self.db,
199+
func,
200+
&self.def_target_features,
201+
self.edition,
202+
self.target_feature_is_safe,
203+
);
188204
match unsafety {
189205
crate::utils::Unsafety::Safe => {}
190206
crate::utils::Unsafety::Unsafe => {

crates/hir-ty/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,10 @@ pub use mapping::{
129129
pub use method_resolution::check_orphan_rules;
130130
pub use target_feature::TargetFeatures;
131131
pub use traits::TraitEnvironment;
132-
pub use utils::{Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call};
132+
pub use utils::{
133+
Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call,
134+
target_feature_is_safe_in_target,
135+
};
133136
pub use variance::Variance;
134137

135138
pub use chalk_ir::{

crates/hir-ty/src/utils.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
44
use std::{cell::LazyCell, iter};
55

6-
use base_db::Crate;
6+
use base_db::{
7+
Crate,
8+
target::{self, TargetData},
9+
};
710
use chalk_ir::{DebruijnIndex, fold::FallibleTypeFolder};
811
use hir_def::{
912
EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId,
@@ -275,18 +278,23 @@ pub enum Unsafety {
275278
DeprecatedSafe2024,
276279
}
277280

281+
pub fn target_feature_is_safe_in_target(target: &TargetData) -> bool {
282+
matches!(target.arch, target::Arch::Wasm32 | target::Arch::Wasm64)
283+
}
284+
278285
pub fn is_fn_unsafe_to_call(
279286
db: &dyn HirDatabase,
280287
func: FunctionId,
281288
caller_target_features: &TargetFeatures,
282289
call_edition: Edition,
290+
target_feature_is_safe: bool,
283291
) -> Unsafety {
284292
let data = db.function_signature(func);
285293
if data.is_unsafe() {
286294
return Unsafety::Unsafe;
287295
}
288296

289-
if data.has_target_feature() {
297+
if data.has_target_feature() && !target_feature_is_safe {
290298
// RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>.
291299
let callee_target_features =
292300
TargetFeatures::from_attrs_no_implications(&db.attrs(func.into()));

crates/hir/src/lib.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,11 +2542,26 @@ impl Function {
25422542
caller: Option<Function>,
25432543
call_edition: Edition,
25442544
) -> bool {
2545-
let target_features = caller
2546-
.map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into())))
2547-
.unwrap_or_default();
2545+
let (target_features, target_feature_is_safe_in_target) = caller
2546+
.map(|caller| {
2547+
let target_features =
2548+
hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()));
2549+
let target_feature_is_safe_in_target =
2550+
match &caller.krate(db).id.workspace_data(db).target {
2551+
Ok(target) => hir_ty::target_feature_is_safe_in_target(target),
2552+
Err(_) => false,
2553+
};
2554+
(target_features, target_feature_is_safe_in_target)
2555+
})
2556+
.unwrap_or_else(|| (hir_ty::TargetFeatures::default(), false));
25482557
matches!(
2549-
hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition),
2558+
hir_ty::is_fn_unsafe_to_call(
2559+
db,
2560+
self.id,
2561+
&target_features,
2562+
call_edition,
2563+
target_feature_is_safe_in_target
2564+
),
25502565
hir_ty::Unsafety::Unsafe
25512566
)
25522567
}

crates/ide-diagnostics/src/handlers/missing_unsafe.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,4 +998,20 @@ extern "C" fn naked() {
998998
"#,
999999
);
10001000
}
1001+
1002+
#[test]
1003+
fn target_feature_safe_on_wasm() {
1004+
check_diagnostics(
1005+
r#"
1006+
//- target_arch: wasm32
1007+
1008+
#[target_feature(enable = "simd128")]
1009+
fn requires_target_feature() {}
1010+
1011+
fn main() {
1012+
requires_target_feature();
1013+
}
1014+
"#,
1015+
);
1016+
}
10011017
}

0 commit comments

Comments
 (0)