about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Scherer <github35764891676564198441@oli-obk.de>2018-11-03 00:22:12 +0100
committerOliver Scherer <github35764891676564198441@oli-obk.de>2018-12-04 10:17:36 +0100
commitf2ae7b78d63695c1b9ff7bdf4079c7a02e77f73e (patch)
tree496ef923b5278d7f030c8a4a50d972f0d3755e34
parent91d5d56c00d8e2926ccf856f14a4e52ef480d039 (diff)
downloadrust-f2ae7b78d63695c1b9ff7bdf4079c7a02e77f73e.tar.gz
rust-f2ae7b78d63695c1b9ff7bdf4079c7a02e77f73e.zip
Allow calling `const unsafe fn` in `const fn` behind a feature gate
-rw-r--r--src/librustc/mir/mod.rs3
-rw-r--r--src/librustc/ty/constness.rs4
-rw-r--r--src/librustc_mir/build/mod.rs9
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs81
-rw-r--r--src/libsyntax/feature_gate.rs3
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs2
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr14
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.rs56
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.stderr59
9 files changed, 194 insertions, 37 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 368f83eb611..c0581121efa 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -2770,7 +2770,8 @@ impl Location {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum UnsafetyViolationKind {
     General,
-    /// unsafety is not allowed at all in min const fn
+    /// Right now function calls to `const unsafe fn` are the only permitted unsafe operation in
+    /// const fn. Also, even `const unsafe fn` need an `unsafe` block to do the allowed operations
     MinConstFn,
     ExternStatic(ast::NodeId),
     BorrowPacked(ast::NodeId),
diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs
index e32913b8905..f067a125c5d 100644
--- a/src/librustc/ty/constness.rs
+++ b/src/librustc/ty/constness.rs
@@ -55,7 +55,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
             }
             // in order for a libstd function to be considered min_const_fn
             // it needs to be stable and have no `rustc_const_unstable` attribute
-            match self.lookup_stability(def_id) {
+            self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) {
                 // stable functions with unstable const fn aren't `min_const_fn`
                 Some(&attr::Stability { const_stability: Some(_), .. }) => false,
                 // unstable functions don't need to conform
@@ -66,7 +66,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
             }
         } else {
             // users enabling the `const_fn` feature gate can do what they want
-            !self.features().const_fn
+            self.is_const_fn_raw(def_id) && !self.features().const_fn
         }
     }
 }
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index d95a74be776..cc927df6350 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -91,8 +91,9 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
             // types/lifetimes replaced)
             let fn_hir_id = tcx.hir.node_to_hir_id(id);
             let fn_sig = cx.tables().liberated_fn_sigs()[fn_hir_id].clone();
+            let fn_def_id = tcx.hir.local_def_id(id);
 
-            let ty = tcx.type_of(tcx.hir.local_def_id(id));
+            let ty = tcx.type_of(fn_def_id);
             let mut abi = fn_sig.abi;
             let implicit_argument = match ty.sty {
                 ty::Closure(..) => {
@@ -113,6 +114,12 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
                 hir::Unsafety::Normal => Safety::Safe,
                 hir::Unsafety::Unsafe => Safety::FnUnsafe,
             };
+            let safety = if implicit_argument.is_none() && tcx.is_min_const_fn(fn_def_id) {
+                // the body of `const unsafe fn`s is treated like the body of safe `const fn`s
+                Safety::Safe
+            } else {
+                safety
+            };
 
             let body = tcx.hir.body(body_id);
             let explicit_arguments =
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index 3404772f825..d096bb32d95 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -34,6 +34,7 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
     source_info: SourceInfo,
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
+    /// mark an `unsafe` block as used, so we don't lint it
     used_unsafe: FxHashSet<ast::NodeId>,
     inherited_blocks: Vec<(ast::NodeId, bool)>,
 }
@@ -93,7 +94,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                 if let hir::Unsafety::Unsafe = sig.unsafety() {
                     self.require_unsafe("call to unsafe function",
                         "consult the function's documentation for information on how to avoid \
-                         undefined behavior")
+                         undefined behavior", UnsafetyViolationKind::MinConstFn)
                 }
             }
         }
@@ -121,7 +122,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
 
             StatementKind::InlineAsm { .. } => {
                 self.require_unsafe("use of inline assembly",
-                    "inline assembly is entirely unchecked and can cause undefined behavior")
+                    "inline assembly is entirely unchecked and can cause undefined behavior",
+                    UnsafetyViolationKind::General)
             },
         }
         self.super_statement(block, statement, location);
@@ -189,7 +191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                         self.require_unsafe("dereference of raw pointer",
                             "raw pointers may be NULL, dangling or unaligned; they can violate \
                              aliasing rules and cause data races: all of these are undefined \
-                             behavior")
+                             behavior", UnsafetyViolationKind::General)
                     }
                     ty::Adt(adt, _) => {
                         if adt.is_union() {
@@ -212,14 +214,15 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                                         "assignment to non-`Copy` union field",
                                         "the previous content of the field will be dropped, which \
                                          causes undefined behavior if the field was not properly \
-                                         initialized")
+                                         initialized", UnsafetyViolationKind::General)
                                 } else {
                                     // write to non-move union, safe
                                 }
                             } else {
                                 self.require_unsafe("access to union field",
                                     "the field may not be properly initialized: using \
-                                     uninitialized data will cause undefined behavior")
+                                     uninitialized data will cause undefined behavior",
+                                     UnsafetyViolationKind::General)
                             }
                         }
                     }
@@ -237,7 +240,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                 if self.tcx.is_static(def_id) == Some(hir::Mutability::MutMutable) {
                     self.require_unsafe("use of mutable static",
                         "mutable statics can be mutated by multiple threads: aliasing violations \
-                         or data races will cause undefined behavior");
+                         or data races will cause undefined behavior",
+                         UnsafetyViolationKind::General);
                 } else if self.tcx.is_foreign_item(def_id) {
                     let source_info = self.source_info;
                     let lint_root =
@@ -260,45 +264,70 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
-    fn require_unsafe(&mut self,
-                      description: &'static str,
-                      details: &'static str)
-    {
+    fn require_unsafe(
+        &mut self,
+        description: &'static str,
+        details: &'static str,
+        kind: UnsafetyViolationKind,
+    ) {
         let source_info = self.source_info;
         self.register_violations(&[UnsafetyViolation {
             source_info,
             description: Symbol::intern(description).as_interned_str(),
             details: Symbol::intern(details).as_interned_str(),
-            kind: UnsafetyViolationKind::General,
+            kind,
         }], &[]);
     }
 
     fn register_violations(&mut self,
                            violations: &[UnsafetyViolation],
                            unsafe_blocks: &[(ast::NodeId, bool)]) {
-        if self.min_const_fn {
-            for violation in violations {
-                let mut violation = violation.clone();
-                violation.kind = UnsafetyViolationKind::MinConstFn;
-                if !self.violations.contains(&violation) {
-                    self.violations.push(violation)
-                }
-            }
-        }
-        let within_unsafe = match self.source_scope_local_data[self.source_info.scope].safety {
-            Safety::Safe => {
+        let safety = self.source_scope_local_data[self.source_info.scope].safety;
+        let within_unsafe = match (safety, self.min_const_fn) {
+            // FIXME: erring on the safe side here and disallowing builtin unsafety in const fn
+            (Safety::BuiltinUnsafe, true) |
+            // `unsafe` blocks are required even in `const unsafe fn`
+            (Safety::FnUnsafe, true) |
+            // `unsafe` blocks are required in safe code
+            (Safety::Safe, _) => {
                 for violation in violations {
-                    if !self.violations.contains(violation) {
-                        self.violations.push(violation.clone())
+                    let mut violation = violation.clone();
+                    if self.min_const_fn {
+                        // overwrite unsafety violation in const fn with a single hard error kind
+                        violation.kind = UnsafetyViolationKind::MinConstFn;
+                    } else if let UnsafetyViolationKind::MinConstFn = violation.kind {
+                        // outside of const fns we treat `MinConstFn` and `General` the same
+                        violation.kind = UnsafetyViolationKind::General;
+                    }
+                    if !self.violations.contains(&violation) {
+                        self.violations.push(violation)
                     }
                 }
                 false
             }
-            Safety::BuiltinUnsafe | Safety::FnUnsafe => true,
-            Safety::ExplicitUnsafe(node_id) => {
+            (Safety::BuiltinUnsafe, false) | (Safety::FnUnsafe, false) => true,
+            (Safety::ExplicitUnsafe(node_id), _) => {
                 if !violations.is_empty() {
                     self.used_unsafe.insert(node_id);
                 }
+                // only some unsafety is allowed in const fn
+                if self.min_const_fn {
+                    for violation in violations {
+                        match violation.kind {
+                            // these are allowed
+                            UnsafetyViolationKind::MinConstFn
+                                if self.tcx.sess.features_untracked().min_const_unsafe_fn => {},
+                            _ => {
+                                let mut violation = violation.clone();
+                                // overwrite unsafety violation in const fn with a hard error
+                                violation.kind = UnsafetyViolationKind::MinConstFn;
+                                if !self.violations.contains(&violation) {
+                                    self.violations.push(violation)
+                                }
+                            },
+                        }
+                    }
+                }
                 true
             }
         };
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index fac7ff2bf34..026b159f80f 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -492,6 +492,9 @@ declare_features! (
 
     // `extern crate self as foo;` puts local crate root into extern prelude under name `foo`.
     (active, extern_crate_self, "1.31.0", Some(56409), None),
+    
+    // Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions.
+    (active, min_const_unsafe_fn, "1.31.0", Some(55607), None),
 );
 
 declare_features! (
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs
index 67332c6d2cf..7a84992e14b 100644
--- a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs
+++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// gate-test-min_const_unsafe_fn
+
 // ok
 const unsafe fn foo4() -> i32 { 42 }
 const unsafe fn foo5<T>() -> *const T { 0 as *const T }
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr
index 8cff0d491d8..17cba8569c1 100644
--- a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr
+++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr
@@ -1,5 +1,5 @@
 error[E0658]: dereferencing raw pointers in constant functions is unstable (see issue #51911)
-  --> $DIR/min_const_fn_unsafe.rs:27:51
+  --> $DIR/min_const_fn_unsafe.rs:29:51
    |
 LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
    |                                                   ^^
@@ -7,7 +7,7 @@ LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowe
    = help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable
 
 error[E0658]: unions in const fn are unstable (see issue #51909)
-  --> $DIR/min_const_fn_unsafe.rs:34:5
+  --> $DIR/min_const_fn_unsafe.rs:36:5
    |
 LL |     Foo { x: () }.y //~ ERROR not allowed in const fn
    |     ^^^^^^^^^^^^^^^
@@ -15,7 +15,7 @@ LL |     Foo { x: () }.y //~ ERROR not allowed in const fn
    = help: add #![feature(const_fn_union)] to the crate attributes to enable
 
 error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
-  --> $DIR/min_const_fn_unsafe.rs:19:14
+  --> $DIR/min_const_fn_unsafe.rs:21:14
    |
 LL |     unsafe { foo4() } //~ ERROR unsafe operations are not allowed in const fn
    |              ^^^^^^ call to unsafe function
@@ -23,7 +23,7 @@ LL |     unsafe { foo4() } //~ ERROR unsafe operations are not allowed in const
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
-  --> $DIR/min_const_fn_unsafe.rs:22:14
+  --> $DIR/min_const_fn_unsafe.rs:24:14
    |
 LL |     unsafe { foo5::<String>() } //~ ERROR unsafe operations are not allowed in const fn
    |              ^^^^^^^^^^^^^^^^ call to unsafe function
@@ -31,7 +31,7 @@ LL |     unsafe { foo5::<String>() } //~ ERROR unsafe operations are not allowed
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
-  --> $DIR/min_const_fn_unsafe.rs:25:14
+  --> $DIR/min_const_fn_unsafe.rs:27:14
    |
 LL |     unsafe { foo6::<Vec<std::cell::Cell<u32>>>() } //~ ERROR not allowed in const fn
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -39,7 +39,7 @@ LL |     unsafe { foo6::<Vec<std::cell::Cell<u32>>>() } //~ ERROR not allowed in
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: dereference of raw pointer is unsafe and unsafe operations are not allowed in const fn
-  --> $DIR/min_const_fn_unsafe.rs:27:51
+  --> $DIR/min_const_fn_unsafe.rs:29:51
    |
 LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
    |                                                   ^^ dereference of raw pointer
@@ -47,7 +47,7 @@ LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowe
    = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
 error: access to union field is unsafe and unsafe operations are not allowed in const fn
-  --> $DIR/min_const_fn_unsafe.rs:34:5
+  --> $DIR/min_const_fn_unsafe.rs:36:5
    |
 LL |     Foo { x: () }.y //~ ERROR not allowed in const fn
    |     ^^^^^^^^^^^^^^^ access to union field
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.rs b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.rs
new file mode 100644
index 00000000000..f3e85ebe5f6
--- /dev/null
+++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.rs
@@ -0,0 +1,56 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(min_const_unsafe_fn)]
+
+// ok
+const unsafe fn foo4() -> i32 { 42 }
+const unsafe fn foo5<T>() -> *const T { 0 as *const T }
+const unsafe fn foo6<T>() -> *mut T { 0 as *mut T }
+const fn no_unsafe() { unsafe {} }
+
+const fn foo8() -> i32 {
+    unsafe { foo4() }
+}
+const fn foo9() -> *const String {
+    unsafe { foo5::<String>() }
+}
+const fn foo10() -> *const Vec<std::cell::Cell<u32>> {
+    unsafe { foo6::<Vec<std::cell::Cell<u32>>>() }
+}
+const unsafe fn foo8_3() -> i32 {
+    unsafe { foo4() }
+}
+const unsafe fn foo9_3() -> *const String {
+    unsafe { foo5::<String>() }
+}
+const unsafe fn foo10_3() -> *const Vec<std::cell::Cell<u32>> {
+    unsafe { foo6::<Vec<std::cell::Cell<u32>>>() }
+}
+// not ok
+const unsafe fn foo8_2() -> i32 {
+    foo4() //~ ERROR not allowed in const fn
+}
+const unsafe fn foo9_2() -> *const String {
+    foo5::<String>() //~ ERROR not allowed in const fn
+}
+const unsafe fn foo10_2() -> *const Vec<std::cell::Cell<u32>> {
+    foo6::<Vec<std::cell::Cell<u32>>>() //~ ERROR not allowed in const fn
+}
+const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
+//~^ dereferencing raw pointers in constant functions
+
+fn main() {}
+
+const unsafe fn no_union() {
+    union Foo { x: (), y: () }
+    Foo { x: () }.y //~ ERROR not allowed in const fn
+    //~^ unions in const fn
+}
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.stderr
new file mode 100644
index 00000000000..78bf99b0a4d
--- /dev/null
+++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.stderr
@@ -0,0 +1,59 @@
+error[E0658]: dereferencing raw pointers in constant functions is unstable (see issue #51911)
+  --> $DIR/min_const_fn_unsafe_feature_gate.rs:47:51
+   |
+LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
+   |                                                   ^^
+   |
+   = help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable
+
+error[E0658]: unions in const fn are unstable (see issue #51909)
+  --> $DIR/min_const_fn_unsafe_feature_gate.rs:54:5
+   |
+LL |     Foo { x: () }.y //~ ERROR not allowed in const fn
+   |     ^^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(const_fn_union)] to the crate attributes to enable
+
+error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
+  --> $DIR/min_const_fn_unsafe_feature_gate.rs:39:5
+   |
+LL |     foo4() //~ ERROR not allowed in const fn
+   |     ^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
+  --> $DIR/min_const_fn_unsafe_feature_gate.rs:42:5
+   |
+LL |     foo5::<String>() //~ ERROR not allowed in const fn
+   |     ^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
+  --> $DIR/min_const_fn_unsafe_feature_gate.rs:45:5
+   |
+LL |     foo6::<Vec<std::cell::Cell<u32>>>() //~ ERROR not allowed in const fn
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: dereference of raw pointer is unsafe and unsafe operations are not allowed in const fn
+  --> $DIR/min_const_fn_unsafe_feature_gate.rs:47:51
+   |
+LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
+   |                                                   ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error: access to union field is unsafe and unsafe operations are not allowed in const fn
+  --> $DIR/min_const_fn_unsafe_feature_gate.rs:54:5
+   |
+LL |     Foo { x: () }.y //~ ERROR not allowed in const fn
+   |     ^^^^^^^^^^^^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0658`.