Unnamed repository; edit this file 'description' to name the repository.
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).
Chayim Refael Friedman 7 months ago
parent 6315e31 · commit fcab4fb
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs24
-rw-r--r--crates/hir-ty/src/lib.rs5
-rw-r--r--crates/hir-ty/src/utils.rs12
-rw-r--r--crates/hir/src/lib.rs23
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs16
5 files changed, 69 insertions, 11 deletions
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index d6d669258c..64c4cdeadd 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -15,8 +15,9 @@ use hir_def::{
use span::Edition;
use crate::{
- InferenceResult, Interner, TargetFeatures, TyExt, TyKind, db::HirDatabase,
- utils::is_fn_unsafe_to_call,
+ InferenceResult, Interner, TargetFeatures, TyExt, TyKind,
+ db::HirDatabase,
+ utils::{is_fn_unsafe_to_call, target_feature_is_safe_in_target},
};
#[derive(Debug, Default)]
@@ -144,6 +145,9 @@ struct UnsafeVisitor<'db> {
def_target_features: TargetFeatures,
// FIXME: This needs to be the edition of the span of each call.
edition: Edition,
+ /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when
+ /// the target feature is not enabled. This flag encodes that.
+ target_feature_is_safe: bool,
}
impl<'db> UnsafeVisitor<'db> {
@@ -159,7 +163,12 @@ impl<'db> UnsafeVisitor<'db> {
DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())),
_ => TargetFeatures::default(),
};
- let edition = resolver.module().krate().data(db).edition;
+ let krate = resolver.module().krate();
+ let edition = krate.data(db).edition;
+ let target_feature_is_safe = match &krate.workspace_data(db).target {
+ Ok(target) => target_feature_is_safe_in_target(target),
+ Err(_) => false,
+ };
Self {
db,
infer,
@@ -172,6 +181,7 @@ impl<'db> UnsafeVisitor<'db> {
callback: unsafe_expr_cb,
def_target_features,
edition,
+ target_feature_is_safe,
}
}
@@ -184,7 +194,13 @@ impl<'db> UnsafeVisitor<'db> {
}
fn check_call(&mut self, node: ExprId, func: FunctionId) {
- let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition);
+ let unsafety = is_fn_unsafe_to_call(
+ self.db,
+ func,
+ &self.def_target_features,
+ self.edition,
+ self.target_feature_is_safe,
+ );
match unsafety {
crate::utils::Unsafety::Safe => {}
crate::utils::Unsafety::Unsafe => {
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index c16bbb7b99..ee35261948 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -129,7 +129,10 @@ pub use mapping::{
pub use method_resolution::check_orphan_rules;
pub use target_feature::TargetFeatures;
pub use traits::TraitEnvironment;
-pub use utils::{Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call};
+pub use utils::{
+ Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call,
+ target_feature_is_safe_in_target,
+};
pub use variance::Variance;
pub use chalk_ir::{
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 881b1c1a9d..07679d2a11 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -3,7 +3,10 @@
use std::{cell::LazyCell, iter};
-use base_db::Crate;
+use base_db::{
+ Crate,
+ target::{self, TargetData},
+};
use chalk_ir::{DebruijnIndex, fold::FallibleTypeFolder};
use hir_def::{
EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId,
@@ -275,18 +278,23 @@ pub enum Unsafety {
DeprecatedSafe2024,
}
+pub fn target_feature_is_safe_in_target(target: &TargetData) -> bool {
+ matches!(target.arch, target::Arch::Wasm32 | target::Arch::Wasm64)
+}
+
pub fn is_fn_unsafe_to_call(
db: &dyn HirDatabase,
func: FunctionId,
caller_target_features: &TargetFeatures,
call_edition: Edition,
+ target_feature_is_safe: bool,
) -> Unsafety {
let data = db.function_signature(func);
if data.is_unsafe() {
return Unsafety::Unsafe;
}
- if data.has_target_feature() {
+ if data.has_target_feature() && !target_feature_is_safe {
// RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>.
let callee_target_features =
TargetFeatures::from_attrs_no_implications(&db.attrs(func.into()));
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 6eb8a8bf60..3f78a04df1 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2542,11 +2542,26 @@ impl Function {
caller: Option<Function>,
call_edition: Edition,
) -> bool {
- let target_features = caller
- .map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into())))
- .unwrap_or_default();
+ let (target_features, target_feature_is_safe_in_target) = caller
+ .map(|caller| {
+ let target_features =
+ hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()));
+ let target_feature_is_safe_in_target =
+ match &caller.krate(db).id.workspace_data(db).target {
+ Ok(target) => hir_ty::target_feature_is_safe_in_target(target),
+ Err(_) => false,
+ };
+ (target_features, target_feature_is_safe_in_target)
+ })
+ .unwrap_or_else(|| (hir_ty::TargetFeatures::default(), false));
matches!(
- hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition),
+ hir_ty::is_fn_unsafe_to_call(
+ db,
+ self.id,
+ &target_features,
+ call_edition,
+ target_feature_is_safe_in_target
+ ),
hir_ty::Unsafety::Unsafe
)
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 17caf63018..4bb64747f5 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -998,4 +998,20 @@ extern "C" fn naked() {
"#,
);
}
+
+ #[test]
+ fn target_feature_safe_on_wasm() {
+ check_diagnostics(
+ r#"
+//- target_arch: wasm32
+
+#[target_feature(enable = "simd128")]
+fn requires_target_feature() {}
+
+fn main() {
+ requires_target_feature();
+}
+ "#,
+ );
+ }
}