Skip to content

Commit f2ae7b7

Browse files
committed
Allow calling const unsafe fn in const fn behind a feature gate
1 parent 91d5d56 commit f2ae7b7

File tree

9 files changed

+194
-37
lines changed

9 files changed

+194
-37
lines changed

src/librustc/mir/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2770,7 +2770,8 @@ impl Location {
27702770
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
27712771
pub enum UnsafetyViolationKind {
27722772
General,
2773-
/// unsafety is not allowed at all in min const fn
2773+
/// Right now function calls to `const unsafe fn` are the only permitted unsafe operation in
2774+
/// const fn. Also, even `const unsafe fn` need an `unsafe` block to do the allowed operations
27742775
MinConstFn,
27752776
ExternStatic(ast::NodeId),
27762777
BorrowPacked(ast::NodeId),

src/librustc/ty/constness.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
5555
}
5656
// in order for a libstd function to be considered min_const_fn
5757
// it needs to be stable and have no `rustc_const_unstable` attribute
58-
match self.lookup_stability(def_id) {
58+
self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) {
5959
// stable functions with unstable const fn aren't `min_const_fn`
6060
Some(&attr::Stability { const_stability: Some(_), .. }) => false,
6161
// unstable functions don't need to conform
@@ -66,7 +66,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
6666
}
6767
} else {
6868
// users enabling the `const_fn` feature gate can do what they want
69-
!self.features().const_fn
69+
self.is_const_fn_raw(def_id) && !self.features().const_fn
7070
}
7171
}
7272
}

src/librustc_mir/build/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,9 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
9191
// types/lifetimes replaced)
9292
let fn_hir_id = tcx.hir.node_to_hir_id(id);
9393
let fn_sig = cx.tables().liberated_fn_sigs()[fn_hir_id].clone();
94+
let fn_def_id = tcx.hir.local_def_id(id);
9495

95-
let ty = tcx.type_of(tcx.hir.local_def_id(id));
96+
let ty = tcx.type_of(fn_def_id);
9697
let mut abi = fn_sig.abi;
9798
let implicit_argument = match ty.sty {
9899
ty::Closure(..) => {
@@ -113,6 +114,12 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
113114
hir::Unsafety::Normal => Safety::Safe,
114115
hir::Unsafety::Unsafe => Safety::FnUnsafe,
115116
};
117+
let safety = if implicit_argument.is_none() && tcx.is_min_const_fn(fn_def_id) {
118+
// the body of `const unsafe fn`s is treated like the body of safe `const fn`s
119+
Safety::Safe
120+
} else {
121+
safety
122+
};
116123

117124
let body = tcx.hir.body(body_id);
118125
let explicit_arguments =

src/librustc_mir/transform/check_unsafety.rs

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
3434
source_info: SourceInfo,
3535
tcx: TyCtxt<'a, 'tcx, 'tcx>,
3636
param_env: ty::ParamEnv<'tcx>,
37+
/// mark an `unsafe` block as used, so we don't lint it
3738
used_unsafe: FxHashSet<ast::NodeId>,
3839
inherited_blocks: Vec<(ast::NodeId, bool)>,
3940
}
@@ -93,7 +94,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
9394
if let hir::Unsafety::Unsafe = sig.unsafety() {
9495
self.require_unsafe("call to unsafe function",
9596
"consult the function's documentation for information on how to avoid \
96-
undefined behavior")
97+
undefined behavior", UnsafetyViolationKind::MinConstFn)
9798
}
9899
}
99100
}
@@ -121,7 +122,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
121122

122123
StatementKind::InlineAsm { .. } => {
123124
self.require_unsafe("use of inline assembly",
124-
"inline assembly is entirely unchecked and can cause undefined behavior")
125+
"inline assembly is entirely unchecked and can cause undefined behavior",
126+
UnsafetyViolationKind::General)
125127
},
126128
}
127129
self.super_statement(block, statement, location);
@@ -189,7 +191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
189191
self.require_unsafe("dereference of raw pointer",
190192
"raw pointers may be NULL, dangling or unaligned; they can violate \
191193
aliasing rules and cause data races: all of these are undefined \
192-
behavior")
194+
behavior", UnsafetyViolationKind::General)
193195
}
194196
ty::Adt(adt, _) => {
195197
if adt.is_union() {
@@ -212,14 +214,15 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
212214
"assignment to non-`Copy` union field",
213215
"the previous content of the field will be dropped, which \
214216
causes undefined behavior if the field was not properly \
215-
initialized")
217+
initialized", UnsafetyViolationKind::General)
216218
} else {
217219
// write to non-move union, safe
218220
}
219221
} else {
220222
self.require_unsafe("access to union field",
221223
"the field may not be properly initialized: using \
222-
uninitialized data will cause undefined behavior")
224+
uninitialized data will cause undefined behavior",
225+
UnsafetyViolationKind::General)
223226
}
224227
}
225228
}
@@ -237,7 +240,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
237240
if self.tcx.is_static(def_id) == Some(hir::Mutability::MutMutable) {
238241
self.require_unsafe("use of mutable static",
239242
"mutable statics can be mutated by multiple threads: aliasing violations \
240-
or data races will cause undefined behavior");
243+
or data races will cause undefined behavior",
244+
UnsafetyViolationKind::General);
241245
} else if self.tcx.is_foreign_item(def_id) {
242246
let source_info = self.source_info;
243247
let lint_root =
@@ -260,45 +264,70 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
260264
}
261265

262266
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
263-
fn require_unsafe(&mut self,
264-
description: &'static str,
265-
details: &'static str)
266-
{
267+
fn require_unsafe(
268+
&mut self,
269+
description: &'static str,
270+
details: &'static str,
271+
kind: UnsafetyViolationKind,
272+
) {
267273
let source_info = self.source_info;
268274
self.register_violations(&[UnsafetyViolation {
269275
source_info,
270276
description: Symbol::intern(description).as_interned_str(),
271277
details: Symbol::intern(details).as_interned_str(),
272-
kind: UnsafetyViolationKind::General,
278+
kind,
273279
}], &[]);
274280
}
275281

276282
fn register_violations(&mut self,
277283
violations: &[UnsafetyViolation],
278284
unsafe_blocks: &[(ast::NodeId, bool)]) {
279-
if self.min_const_fn {
280-
for violation in violations {
281-
let mut violation = violation.clone();
282-
violation.kind = UnsafetyViolationKind::MinConstFn;
283-
if !self.violations.contains(&violation) {
284-
self.violations.push(violation)
285-
}
286-
}
287-
}
288-
let within_unsafe = match self.source_scope_local_data[self.source_info.scope].safety {
289-
Safety::Safe => {
285+
let safety = self.source_scope_local_data[self.source_info.scope].safety;
286+
let within_unsafe = match (safety, self.min_const_fn) {
287+
// FIXME: erring on the safe side here and disallowing builtin unsafety in const fn
288+
(Safety::BuiltinUnsafe, true) |
289+
// `unsafe` blocks are required even in `const unsafe fn`
290+
(Safety::FnUnsafe, true) |
291+
// `unsafe` blocks are required in safe code
292+
(Safety::Safe, _) => {
290293
for violation in violations {
291-
if !self.violations.contains(violation) {
292-
self.violations.push(violation.clone())
294+
let mut violation = violation.clone();
295+
if self.min_const_fn {
296+
// overwrite unsafety violation in const fn with a single hard error kind
297+
violation.kind = UnsafetyViolationKind::MinConstFn;
298+
} else if let UnsafetyViolationKind::MinConstFn = violation.kind {
299+
// outside of const fns we treat `MinConstFn` and `General` the same
300+
violation.kind = UnsafetyViolationKind::General;
301+
}
302+
if !self.violations.contains(&violation) {
303+
self.violations.push(violation)
293304
}
294305
}
295306
false
296307
}
297-
Safety::BuiltinUnsafe | Safety::FnUnsafe => true,
298-
Safety::ExplicitUnsafe(node_id) => {
308+
(Safety::BuiltinUnsafe, false) | (Safety::FnUnsafe, false) => true,
309+
(Safety::ExplicitUnsafe(node_id), _) => {
299310
if !violations.is_empty() {
300311
self.used_unsafe.insert(node_id);
301312
}
313+
// only some unsafety is allowed in const fn
314+
if self.min_const_fn {
315+
for violation in violations {
316+
match violation.kind {
317+
// these are allowed
318+
UnsafetyViolationKind::MinConstFn
319+
if self.tcx.sess.features_untracked().min_const_unsafe_fn => {},
320+
_ => {
321+
let mut violation = violation.clone();
322+
// overwrite unsafety violation in const fn with a hard error
323+
violation.kind = UnsafetyViolationKind::MinConstFn;
324+
if !self.violations.contains(&violation) {
325+
self.violations.push(violation)
326+
}
327+
},
328+
}
329+
}
330+
}
302331
true
303332
}
304333
};

src/libsyntax/feature_gate.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,9 @@ declare_features! (
492492

493493
// `extern crate self as foo;` puts local crate root into extern prelude under name `foo`.
494494
(active, extern_crate_self, "1.31.0", Some(56409), None),
495+
496+
// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions.
497+
(active, min_const_unsafe_fn, "1.31.0", Some(55607), None),
495498
);
496499

497500
declare_features! (

src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// gate-test-min_const_unsafe_fn
12+
1113
// ok
1214
const unsafe fn foo4() -> i32 { 42 }
1315
const unsafe fn foo5<T>() -> *const T { 0 as *const T }

src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,53 @@
11
error[E0658]: dereferencing raw pointers in constant functions is unstable (see issue #51911)
2-
--> $DIR/min_const_fn_unsafe.rs:27:51
2+
--> $DIR/min_const_fn_unsafe.rs:29:51
33
|
44
LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
55
| ^^
66
|
77
= help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable
88

99
error[E0658]: unions in const fn are unstable (see issue #51909)
10-
--> $DIR/min_const_fn_unsafe.rs:34:5
10+
--> $DIR/min_const_fn_unsafe.rs:36:5
1111
|
1212
LL | Foo { x: () }.y //~ ERROR not allowed in const fn
1313
| ^^^^^^^^^^^^^^^
1414
|
1515
= help: add #![feature(const_fn_union)] to the crate attributes to enable
1616

1717
error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
18-
--> $DIR/min_const_fn_unsafe.rs:19:14
18+
--> $DIR/min_const_fn_unsafe.rs:21:14
1919
|
2020
LL | unsafe { foo4() } //~ ERROR unsafe operations are not allowed in const fn
2121
| ^^^^^^ call to unsafe function
2222
|
2323
= note: consult the function's documentation for information on how to avoid undefined behavior
2424

2525
error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
26-
--> $DIR/min_const_fn_unsafe.rs:22:14
26+
--> $DIR/min_const_fn_unsafe.rs:24:14
2727
|
2828
LL | unsafe { foo5::<String>() } //~ ERROR unsafe operations are not allowed in const fn
2929
| ^^^^^^^^^^^^^^^^ call to unsafe function
3030
|
3131
= note: consult the function's documentation for information on how to avoid undefined behavior
3232

3333
error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn
34-
--> $DIR/min_const_fn_unsafe.rs:25:14
34+
--> $DIR/min_const_fn_unsafe.rs:27:14
3535
|
3636
LL | unsafe { foo6::<Vec<std::cell::Cell<u32>>>() } //~ ERROR not allowed in const fn
3737
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
3838
|
3939
= note: consult the function's documentation for information on how to avoid undefined behavior
4040

4141
error: dereference of raw pointer is unsafe and unsafe operations are not allowed in const fn
42-
--> $DIR/min_const_fn_unsafe.rs:27:51
42+
--> $DIR/min_const_fn_unsafe.rs:29:51
4343
|
4444
LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
4545
| ^^ dereference of raw pointer
4646
|
4747
= note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
4848

4949
error: access to union field is unsafe and unsafe operations are not allowed in const fn
50-
--> $DIR/min_const_fn_unsafe.rs:34:5
50+
--> $DIR/min_const_fn_unsafe.rs:36:5
5151
|
5252
LL | Foo { x: () }.y //~ ERROR not allowed in const fn
5353
| ^^^^^^^^^^^^^^^ access to union field
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(min_const_unsafe_fn)]
12+
13+
// ok
14+
const unsafe fn foo4() -> i32 { 42 }
15+
const unsafe fn foo5<T>() -> *const T { 0 as *const T }
16+
const unsafe fn foo6<T>() -> *mut T { 0 as *mut T }
17+
const fn no_unsafe() { unsafe {} }
18+
19+
const fn foo8() -> i32 {
20+
unsafe { foo4() }
21+
}
22+
const fn foo9() -> *const String {
23+
unsafe { foo5::<String>() }
24+
}
25+
const fn foo10() -> *const Vec<std::cell::Cell<u32>> {
26+
unsafe { foo6::<Vec<std::cell::Cell<u32>>>() }
27+
}
28+
const unsafe fn foo8_3() -> i32 {
29+
unsafe { foo4() }
30+
}
31+
const unsafe fn foo9_3() -> *const String {
32+
unsafe { foo5::<String>() }
33+
}
34+
const unsafe fn foo10_3() -> *const Vec<std::cell::Cell<u32>> {
35+
unsafe { foo6::<Vec<std::cell::Cell<u32>>>() }
36+
}
37+
// not ok
38+
const unsafe fn foo8_2() -> i32 {
39+
foo4() //~ ERROR not allowed in const fn
40+
}
41+
const unsafe fn foo9_2() -> *const String {
42+
foo5::<String>() //~ ERROR not allowed in const fn
43+
}
44+
const unsafe fn foo10_2() -> *const Vec<std::cell::Cell<u32>> {
45+
foo6::<Vec<std::cell::Cell<u32>>>() //~ ERROR not allowed in const fn
46+
}
47+
const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn
48+
//~^ dereferencing raw pointers in constant functions
49+
50+
fn main() {}
51+
52+
const unsafe fn no_union() {
53+
union Foo { x: (), y: () }
54+
Foo { x: () }.y //~ ERROR not allowed in const fn
55+
//~^ unions in const fn
56+
}

0 commit comments

Comments
 (0)