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).
This commit is contained in:
parent
74a8f7451e
commit
d6638e9bd2
5 changed files with 69 additions and 11 deletions
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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::{
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue