Skip to content

Commit aedc3a5

Browse files
committed
Declare some unconst operations as unsafe in const fn
1 parent 64afc6b commit aedc3a5

17 files changed

+187
-98
lines changed

src/librustc_mir/transform/check_unsafety.rs

+77-20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_data_structures::sync::Lrc;
44

55
use rustc::ty::query::Providers;
66
use rustc::ty::{self, TyCtxt};
7+
use rustc::ty::cast::CastTy;
78
use rustc::hir;
89
use rustc::hir::Node;
910
use rustc::hir::def_id::DefId;
@@ -20,6 +21,7 @@ use util;
2021

2122
pub struct UnsafetyChecker<'a, 'tcx: 'a> {
2223
mir: &'a Mir<'tcx>,
24+
const_context: bool,
2325
min_const_fn: bool,
2426
source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
2527
violations: Vec<UnsafetyViolation>,
@@ -33,14 +35,20 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
3335

3436
impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> {
3537
fn new(
38+
const_context: bool,
3639
min_const_fn: bool,
3740
mir: &'a Mir<'tcx>,
3841
source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
3942
tcx: TyCtxt<'a, 'tcx, 'tcx>,
4043
param_env: ty::ParamEnv<'tcx>,
4144
) -> Self {
45+
// sanity check
46+
if min_const_fn {
47+
assert!(const_context);
48+
}
4249
Self {
4350
mir,
51+
const_context,
4452
min_const_fn,
4553
source_scope_local_data,
4654
violations: vec![],
@@ -124,29 +132,70 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
124132
rvalue: &Rvalue<'tcx>,
125133
location: Location)
126134
{
127-
if let &Rvalue::Aggregate(box ref aggregate, _) = rvalue {
128-
match aggregate {
129-
&AggregateKind::Array(..) |
130-
&AggregateKind::Tuple => {}
131-
&AggregateKind::Adt(ref def, ..) => {
132-
match self.tcx.layout_scalar_valid_range(def.did) {
133-
(Bound::Unbounded, Bound::Unbounded) => {},
134-
_ => self.require_unsafe(
135-
"initializing type with `rustc_layout_scalar_valid_range` attr",
136-
"initializing a layout restricted type's field with a value outside \
137-
the valid range is undefined behavior",
138-
UnsafetyViolationKind::GeneralAndConstFn,
139-
),
135+
match rvalue {
136+
Rvalue::Aggregate(box ref aggregate, _) => {
137+
match aggregate {
138+
&AggregateKind::Array(..) |
139+
&AggregateKind::Tuple => {}
140+
&AggregateKind::Adt(ref def, ..) => {
141+
match self.tcx.layout_scalar_valid_range(def.did) {
142+
(Bound::Unbounded, Bound::Unbounded) => {},
143+
_ => self.require_unsafe(
144+
"initializing type with `rustc_layout_scalar_valid_range` attr",
145+
"initializing a layout restricted type's field with a value \
146+
outside the valid range is undefined behavior",
147+
UnsafetyViolationKind::GeneralAndConstFn,
148+
),
149+
}
150+
}
151+
&AggregateKind::Closure(def_id, _) |
152+
&AggregateKind::Generator(def_id, _, _) => {
153+
let UnsafetyCheckResult {
154+
violations, unsafe_blocks
155+
} = self.tcx.unsafety_check_result(def_id);
156+
self.register_violations(&violations, &unsafe_blocks);
140157
}
141158
}
142-
&AggregateKind::Closure(def_id, _) |
143-
&AggregateKind::Generator(def_id, _, _) => {
144-
let UnsafetyCheckResult {
145-
violations, unsafe_blocks
146-
} = self.tcx.unsafety_check_result(def_id);
147-
self.register_violations(&violations, &unsafe_blocks);
159+
},
160+
// casting pointers to ints is unsafe in const fn because the const evaluator cannot
161+
// possibly know what the result of various operations like `address / 2` would be
162+
// pointers during const evaluation have no integral address, only an abstract one
163+
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty)
164+
if self.const_context && self.tcx.features().const_raw_ptr_to_usize_cast => {
165+
let operand_ty = operand.ty(self.mir, self.tcx);
166+
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
167+
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
168+
match (cast_in, cast_out) {
169+
(CastTy::Ptr(_), CastTy::Int(_)) |
170+
(CastTy::FnPtr, CastTy::Int(_)) => {
171+
self.register_violations(&[UnsafetyViolation {
172+
source_info: self.source_info,
173+
description: Symbol::intern("cast of pointer to int").as_interned_str(),
174+
details: Symbol::intern("casting pointers to integers in constants")
175+
.as_interned_str(),
176+
kind: UnsafetyViolationKind::General,
177+
}], &[]);
178+
},
179+
_ => {},
148180
}
149181
}
182+
// raw pointer and fn pointer operations are unsafe as it is not clear whether one
183+
// pointer would be "less" or "equal" to another, because we cannot know where llvm
184+
// or the linker will place various statics in memory. Without this information the
185+
// result of a comparison of addresses would differ between runtime and compile-time.
186+
Rvalue::BinaryOp(_, ref lhs, _)
187+
if self.const_context && self.tcx.features().const_compare_raw_pointers => {
188+
if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
189+
self.register_violations(&[UnsafetyViolation {
190+
source_info: self.source_info,
191+
description: Symbol::intern("pointer operation").as_interned_str(),
192+
details: Symbol::intern("operations on pointers in constants")
193+
.as_interned_str(),
194+
kind: UnsafetyViolationKind::General,
195+
}], &[]);
196+
}
197+
}
198+
_ => {},
150199
}
151200
self.super_rvalue(rvalue, location);
152201
}
@@ -484,8 +533,16 @@ fn unsafety_check_result<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
484533
};
485534

486535
let param_env = tcx.param_env(def_id);
536+
537+
let id = tcx.hir().as_local_node_id(def_id).unwrap();
538+
let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
539+
hir::BodyOwnerKind::Closure => (false, false),
540+
hir::BodyOwnerKind::Fn => (tcx.is_const_fn(def_id), tcx.is_min_const_fn(def_id)),
541+
hir::BodyOwnerKind::Const |
542+
hir::BodyOwnerKind::Static(_) => (true, false),
543+
};
487544
let mut checker = UnsafetyChecker::new(
488-
tcx.is_min_const_fn(def_id),
545+
const_context, min_const_fn,
489546
mir, source_scope_local_data, tcx, param_env);
490547
checker.visit_mir(mir);
491548

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// gate-test-const_raw_ptr_to_usize_cast
22

33
fn main() {
4-
const X: u32 = main as u32; //~ ERROR casting pointers to integers in constants is unstable
4+
const X: u32 = unsafe {
5+
main as u32 //~ ERROR casting pointers to integers in constants is unstable
6+
};
57
const Y: u32 = 0;
6-
const Z: u32 = &Y as *const u32 as u32; //~ ERROR is unstable
8+
const Z: u32 = unsafe {
9+
&Y as *const u32 as u32 //~ ERROR is unstable
10+
};
711
}

src/test/ui/cast/cast-ptr-to-int-const.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
error[E0658]: casting pointers to integers in constants is unstable (see issue #51910)
2-
--> $DIR/cast-ptr-to-int-const.rs:4:20
2+
--> $DIR/cast-ptr-to-int-const.rs:5:9
33
|
4-
LL | const X: u32 = main as u32; //~ ERROR casting pointers to integers in constants is unstable
5-
| ^^^^^^^^^^^
4+
LL | main as u32 //~ ERROR casting pointers to integers in constants is unstable
5+
| ^^^^^^^^^^^
66
|
77
= help: add #![feature(const_raw_ptr_to_usize_cast)] to the crate attributes to enable
88

99
error[E0658]: casting pointers to integers in constants is unstable (see issue #51910)
10-
--> $DIR/cast-ptr-to-int-const.rs:6:20
10+
--> $DIR/cast-ptr-to-int-const.rs:9:9
1111
|
12-
LL | const Z: u32 = &Y as *const u32 as u32; //~ ERROR is unstable
13-
| ^^^^^^^^^^^^^^^^^^^^^^^
12+
LL | &Y as *const u32 as u32 //~ ERROR is unstable
13+
| ^^^^^^^^^^^^^^^^^^^^^^^
1414
|
1515
= help: add #![feature(const_raw_ptr_to_usize_cast)] to the crate attributes to enable
1616

src/test/ui/consts/const-eval/const_raw_ptr_ops.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
fn main() {}
44

55
// unconst and bad, will thus error in miri
6-
const X: bool = &1 as *const i32 == &2 as *const i32; //~ ERROR any use of this value will cause
6+
const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; //~ ERROR any use of this
77
// unconst and fine
8-
const X2: bool = 42 as *const i32 == 43 as *const i32;
8+
const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 };
99
// unconst and fine
10-
const Y: usize = 42usize as *const i32 as usize + 1;
10+
const Y: usize = unsafe { 42usize as *const i32 as usize + 1 };
1111
// unconst and bad, will thus error in miri
12-
const Y2: usize = &1 as *const i32 as usize + 1; //~ ERROR any use of this value will cause
12+
const Y2: usize = unsafe { &1 as *const i32 as usize + 1 }; //~ ERROR any use of this
1313
// unconst and fine
1414
const Z: i32 = unsafe { *(&1 as *const i32) };
1515
// unconst and bad, will thus error in miri

src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
error: any use of this value will cause an error
22
--> $DIR/const_raw_ptr_ops.rs:6:1
33
|
4-
LL | const X: bool = &1 as *const i32 == &2 as *const i32; //~ ERROR any use of this value will cause
5-
| ^^^^^^^^^^^^^^^^------------------------------------^
6-
| |
7-
| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
4+
LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; //~ ERROR any use of this
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^------------------------------------^^^
6+
| |
7+
| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
88
|
99
= note: #[deny(const_err)] on by default
1010

1111
error: any use of this value will cause an error
1212
--> $DIR/const_raw_ptr_ops.rs:12:1
1313
|
14-
LL | const Y2: usize = &1 as *const i32 as usize + 1; //~ ERROR any use of this value will cause
15-
| ^^^^^^^^^^^^^^^^^^-----------------------------^
16-
| |
17-
| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
14+
LL | const Y2: usize = unsafe { &1 as *const i32 as usize + 1 }; //~ ERROR any use of this
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------------------------^^^
16+
| |
17+
| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
1818

1919
error: any use of this value will cause an error
2020
--> $DIR/const_raw_ptr_ops.rs:16:1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const fn cmp(x: fn(), y: fn()) -> bool { //~ ERROR function pointers in const fn are unstable
2-
x == y
2+
unsafe { x == y }
33
}
44

55
fn main() {}

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

+32-20
Original file line numberDiff line numberDiff line change
@@ -95,97 +95,109 @@ LL | const fn foo30(x: *const u32) -> usize { x as usize }
9595
| ^^^^^^^^^^
9696

9797
error: casting pointers to ints is unstable in const fn
98-
--> $DIR/min_const_fn.rs:94:42
98+
--> $DIR/min_const_fn.rs:94:63
99+
|
100+
LL | const fn foo30_with_unsafe(x: *const u32) -> usize { unsafe { x as usize } }
101+
| ^^^^^^^^^^
102+
103+
error: casting pointers to ints is unstable in const fn
104+
--> $DIR/min_const_fn.rs:96:42
99105
|
100106
LL | const fn foo30_2(x: *mut u32) -> usize { x as usize }
101107
| ^^^^^^^^^^
102108

109+
error: casting pointers to ints is unstable in const fn
110+
--> $DIR/min_const_fn.rs:98:63
111+
|
112+
LL | const fn foo30_2_with_unsafe(x: *mut u32) -> usize { unsafe { x as usize } }
113+
| ^^^^^^^^^^
114+
103115
error: `if`, `match`, `&&` and `||` are not stable in const fn
104-
--> $DIR/min_const_fn.rs:96:38
116+
--> $DIR/min_const_fn.rs:100:38
105117
|
106118
LL | const fn foo30_4(b: bool) -> usize { if b { 1 } else { 42 } }
107119
| ^^^^^^^^^^^^^^^^^^^^^^
108120

109121
error: `if`, `match`, `&&` and `||` are not stable in const fn
110-
--> $DIR/min_const_fn.rs:98:29
122+
--> $DIR/min_const_fn.rs:102:29
111123
|
112124
LL | const fn foo30_5(b: bool) { while b { } } //~ ERROR not stable in const fn
113125
| ^^^^^^^^^^^
114126

115127
error: `if`, `match`, `&&` and `||` are not stable in const fn
116-
--> $DIR/min_const_fn.rs:100:44
128+
--> $DIR/min_const_fn.rs:104:44
117129
|
118130
LL | const fn foo36(a: bool, b: bool) -> bool { a && b }
119131
| ^^^^^^
120132

121133
error: `if`, `match`, `&&` and `||` are not stable in const fn
122-
--> $DIR/min_const_fn.rs:102:44
134+
--> $DIR/min_const_fn.rs:106:44
123135
|
124136
LL | const fn foo37(a: bool, b: bool) -> bool { a || b }
125137
| ^^^^^^
126138

127139
error: mutable references in const fn are unstable
128-
--> $DIR/min_const_fn.rs:104:14
140+
--> $DIR/min_const_fn.rs:108:14
129141
|
130142
LL | const fn inc(x: &mut i32) { *x += 1 }
131143
| ^
132144

133145
error: trait bounds other than `Sized` on const fn parameters are unstable
134-
--> $DIR/min_const_fn.rs:109:6
146+
--> $DIR/min_const_fn.rs:113:6
135147
|
136148
LL | impl<T: std::fmt::Debug> Foo<T> {
137149
| ^
138150

139151
error: trait bounds other than `Sized` on const fn parameters are unstable
140-
--> $DIR/min_const_fn.rs:114:6
152+
--> $DIR/min_const_fn.rs:118:6
141153
|
142154
LL | impl<T: std::fmt::Debug + Sized> Foo<T> {
143155
| ^
144156

145157
error: trait bounds other than `Sized` on const fn parameters are unstable
146-
--> $DIR/min_const_fn.rs:119:6
158+
--> $DIR/min_const_fn.rs:123:6
147159
|
148160
LL | impl<T: Sync + Sized> Foo<T> {
149161
| ^
150162

151163
error: `impl Trait` in const fn is unstable
152-
--> $DIR/min_const_fn.rs:125:24
164+
--> $DIR/min_const_fn.rs:129:24
153165
|
154166
LL | const fn no_rpit2() -> AlanTuring<impl std::fmt::Debug> { AlanTuring(0) }
155167
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
156168

157169
error: trait bounds other than `Sized` on const fn parameters are unstable
158-
--> $DIR/min_const_fn.rs:127:34
170+
--> $DIR/min_const_fn.rs:131:34
159171
|
160172
LL | const fn no_apit2(_x: AlanTuring<impl std::fmt::Debug>) {}
161173
| ^^^^^^^^^^^^^^^^^^^^
162174

163175
error: trait bounds other than `Sized` on const fn parameters are unstable
164-
--> $DIR/min_const_fn.rs:129:22
176+
--> $DIR/min_const_fn.rs:133:22
165177
|
166178
LL | const fn no_apit(_x: impl std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
167179
| ^^^^^^^^^^^^^^^^^^^^
168180

169181
error: `impl Trait` in const fn is unstable
170-
--> $DIR/min_const_fn.rs:130:23
182+
--> $DIR/min_const_fn.rs:134:23
171183
|
172184
LL | const fn no_rpit() -> impl std::fmt::Debug {} //~ ERROR `impl Trait` in const fn is unstable
173185
| ^^^^^^^^^^^^^^^^^^^^
174186

175187
error: trait bounds other than `Sized` on const fn parameters are unstable
176-
--> $DIR/min_const_fn.rs:131:23
188+
--> $DIR/min_const_fn.rs:135:23
177189
|
178190
LL | const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
179191
| ^^
180192

181193
error: trait bounds other than `Sized` on const fn parameters are unstable
182-
--> $DIR/min_const_fn.rs:132:32
194+
--> $DIR/min_const_fn.rs:136:32
183195
|
184196
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
185197
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
186198

187199
warning[E0515]: cannot return reference to temporary value
188-
--> $DIR/min_const_fn.rs:132:63
200+
--> $DIR/min_const_fn.rs:136:63
189201
|
190202
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
191203
| ^--
@@ -197,24 +209,24 @@ LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
197209
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
198210

199211
error: trait bounds other than `Sized` on const fn parameters are unstable
200-
--> $DIR/min_const_fn.rs:137:41
212+
--> $DIR/min_const_fn.rs:141:41
201213
|
202214
LL | const fn really_no_traits_i_mean_it() { (&() as &std::fmt::Debug, ()).1 }
203215
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
204216

205217
error: function pointers in const fn are unstable
206-
--> $DIR/min_const_fn.rs:140:21
218+
--> $DIR/min_const_fn.rs:144:21
207219
|
208220
LL | const fn no_fn_ptrs(_x: fn()) {}
209221
| ^^
210222

211223
error: function pointers in const fn are unstable
212-
--> $DIR/min_const_fn.rs:142:27
224+
--> $DIR/min_const_fn.rs:146:27
213225
|
214226
LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
215227
| ^^^^
216228

217-
error: aborting due to 34 previous errors
229+
error: aborting due to 36 previous errors
218230

219231
Some errors occurred: E0493, E0515.
220232
For more information about an error, try `rustc --explain E0493`.

0 commit comments

Comments
 (0)