about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_mir/src/transform/check_unsafety.rs6
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs17
-rw-r--r--compiler/rustc_typeck/src/collect.rs16
-rw-r--r--src/test/ui/target-feature/wasm-safe.rs44
4 files changed, 75 insertions, 8 deletions
diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs
index 955be8cc81e..9a4e51203d2 100644
--- a/compiler/rustc_mir/src/transform/check_unsafety.rs
+++ b/compiler/rustc_mir/src/transform/check_unsafety.rs
@@ -376,6 +376,12 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
     /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
     /// the called function has target features the calling function hasn't.
     fn check_target_features(&mut self, func_did: DefId) {
+        // Unsafety isn't required on wasm targets. For more information see
+        // the corresponding check in typeck/src/collect.rs
+        if self.tcx.sess.target.options.is_like_wasm {
+            return;
+        }
+
         let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
         let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
 
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index d1aaabe92ed..2d52577829c 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -166,13 +166,16 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                     self.requires_unsafe(expr.span, CallToUnsafeFunction);
                 } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
                     // If the called function has target features the calling function hasn't,
-                    // the call requires `unsafe`.
-                    if !self
-                        .tcx
-                        .codegen_fn_attrs(func_did)
-                        .target_features
-                        .iter()
-                        .all(|feature| self.body_target_features.contains(feature))
+                    // the call requires `unsafe`. Don't check this on wasm
+                    // targets, though. For more information on wasm see the
+                    // is_like_wasm check in typeck/src/collect.rs
+                    if !self.tcx.sess.target.options.is_like_wasm
+                        && !self
+                            .tcx
+                            .codegen_fn_attrs(func_did)
+                            .target_features
+                            .iter()
+                            .all(|feature| self.body_target_features.contains(feature))
                     {
                         self.requires_unsafe(expr.span, CallToFunctionWith);
                     }
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 0528f8812f9..5d83375e5a1 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -2770,7 +2770,21 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
             }
         } else if tcx.sess.check_name(attr, sym::target_feature) {
             if !tcx.is_closure(id) && tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
-                if !tcx.features().target_feature_11 {
+                if tcx.sess.target.is_like_wasm {
+                    // The `#[target_feature]` attribute is allowed on
+                    // WebAssembly targets on all functions, including safe
+                    // ones. Other targets require that `#[target_feature]` is
+                    // only applied to unsafe funtions (pending the
+                    // `target_feature_11` feature) because on most targets
+                    // execution of instructions that are not supported is
+                    // considered undefined behavior. For WebAssembly which is a
+                    // 100% safe target at execution time it's not possible to
+                    // execute undefined instructions, and even if a future
+                    // feature was added in some form for this it would be a
+                    // deterministic trap. There is no undefined behavior when
+                    // executing WebAssembly so `#[target_feature]` is allowed
+                    // on safe functions (but again, only for WebAssembly)
+                } else if !tcx.features().target_feature_11 {
                     let mut err = feature_err(
                         &tcx.sess.parse_sess,
                         sym::target_feature_11,
diff --git a/src/test/ui/target-feature/wasm-safe.rs b/src/test/ui/target-feature/wasm-safe.rs
new file mode 100644
index 00000000000..4b868684a52
--- /dev/null
+++ b/src/test/ui/target-feature/wasm-safe.rs
@@ -0,0 +1,44 @@
+// only-wasm32
+// check-pass
+
+#![feature(wasm_target_feature)]
+#![allow(dead_code)]
+
+#[target_feature(enable = "nontrapping-fptoint")]
+fn foo() {}
+
+#[target_feature(enable = "nontrapping-fptoint")]
+extern "C" fn bar() {}
+
+trait A {
+    fn foo();
+    fn bar(&self);
+}
+
+struct B;
+
+impl B {
+    #[target_feature(enable = "nontrapping-fptoint")]
+    fn foo() {}
+    #[target_feature(enable = "nontrapping-fptoint")]
+    fn bar(&self) {}
+}
+
+impl A for B {
+    #[target_feature(enable = "nontrapping-fptoint")]
+    fn foo() {}
+    #[target_feature(enable = "nontrapping-fptoint")]
+    fn bar(&self) {}
+}
+
+fn no_features_enabled_on_this_function() {
+    bar();
+    foo();
+    B.bar();
+    B::foo();
+    <B as A>::foo();
+    <B as A>::bar(&B);
+}
+
+#[target_feature(enable = "nontrapping-fptoint")]
+fn main() {}