Skip to content

Commit 51cc3cd

Browse files
committed
Auto merge of #55009 - oli-obk:const_safety, r=RalfJung
Make raw ptr ops unsafe in const contexts r? @RalfJung cc @Centril
2 parents 7164a9f + aedc3a5 commit 51cc3cd

29 files changed

+225
-115
lines changed

src/librustc/hir/map/mod.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -455,11 +455,20 @@ impl<'hir> Map<'hir> {
455455
Node::AnonConst(_) => {
456456
BodyOwnerKind::Const
457457
}
458+
Node::Variant(&Spanned { node: VariantKind { data: VariantData::Tuple(..), .. }, .. }) |
459+
Node::StructCtor(..) |
460+
Node::Item(&Item { node: ItemKind::Fn(..), .. }) |
461+
Node::TraitItem(&TraitItem { node: TraitItemKind::Method(..), .. }) |
462+
Node::ImplItem(&ImplItem { node: ImplItemKind::Method(..), .. }) => {
463+
BodyOwnerKind::Fn
464+
}
458465
Node::Item(&Item { node: ItemKind::Static(_, m, _), .. }) => {
459466
BodyOwnerKind::Static(m)
460467
}
461-
// Default to function if it's not a constant or static.
462-
_ => BodyOwnerKind::Fn
468+
Node::Expr(&Expr { node: ExprKind::Closure(..), .. }) => {
469+
BodyOwnerKind::Closure
470+
}
471+
node => bug!("{:#?} is not a body node", node),
463472
}
464473
}
465474

src/librustc/hir/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1270,13 +1270,25 @@ pub enum BodyOwnerKind {
12701270
/// Functions and methods.
12711271
Fn,
12721272

1273+
/// Closures
1274+
Closure,
1275+
12731276
/// Constants and associated constants.
12741277
Const,
12751278

12761279
/// Initializer of a `static` item.
12771280
Static(Mutability),
12781281
}
12791282

1283+
impl BodyOwnerKind {
1284+
pub fn is_fn_or_closure(self) -> bool {
1285+
match self {
1286+
BodyOwnerKind::Fn | BodyOwnerKind::Closure => true,
1287+
BodyOwnerKind::Const | BodyOwnerKind::Static(_) => false,
1288+
}
1289+
}
1290+
}
1291+
12801292
/// A constant (expression) that's not an item or associated item,
12811293
/// but needs its own `DefId` for type-checking, const-eval, etc.
12821294
/// These are usually found nested inside types (e.g., array lengths)

src/librustc/middle/region.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1268,8 +1268,8 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
12681268

12691269
// The body of the every fn is a root scope.
12701270
self.cx.parent = self.cx.var_parent;
1271-
if let hir::BodyOwnerKind::Fn = self.tcx.hir().body_owner_kind(owner_id) {
1272-
self.visit_expr(&body.value);
1271+
if self.tcx.hir().body_owner_kind(owner_id).is_fn_or_closure() {
1272+
self.visit_expr(&body.value)
12731273
} else {
12741274
// Only functions have an outer terminating (drop) scope, while
12751275
// temporaries in constant initializers may be 'static, but only

src/librustc_mir/borrow_check/mod.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
163163
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
164164
));
165165

166-
let locals_are_invalidated_at_exit = match tcx.hir().body_owner_kind(id) {
167-
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false,
168-
hir::BodyOwnerKind::Fn => true,
169-
};
166+
let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(id).is_fn_or_closure();
170167
let borrow_set = Rc::new(BorrowSet::build(
171168
tcx, mir, locals_are_invalidated_at_exit, &mdpe.move_data));
172169

src/librustc_mir/borrow_check/nll/universal_regions.rs

+1
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
476476
let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
477477

478478
match tcx.hir().body_owner_kind(self.mir_node_id) {
479+
BodyOwnerKind::Closure |
479480
BodyOwnerKind::Fn => {
480481
let defining_ty = if self.mir_def_id == closure_base_def_id {
481482
tcx.type_of(closure_base_def_id)

src/librustc_mir/build/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
7575
let cx = Cx::new(&infcx, id);
7676
let mut mir = if cx.tables().tainted_by_errors {
7777
build::construct_error(cx, body_id)
78-
} else if let hir::BodyOwnerKind::Fn = cx.body_owner_kind {
78+
} else if cx.body_owner_kind.is_fn_or_closure() {
7979
// fetch the fully liberated fn signature (that is, all bound
8080
// types/lifetimes replaced)
8181
let fn_hir_id = tcx.hir().node_to_hir_id(id);

src/librustc_mir/build/scope.rs

+1
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
613613
hir::BodyOwnerKind::Static(_) =>
614614
// No need to free storage in this context.
615615
None,
616+
hir::BodyOwnerKind::Closure |
616617
hir::BodyOwnerKind::Fn =>
617618
Some(self.topmost_scope()),
618619
}

src/librustc_mir/hair/cx/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
6161
let constness = match body_owner_kind {
6262
hir::BodyOwnerKind::Const |
6363
hir::BodyOwnerKind::Static(_) => hir::Constness::Const,
64+
hir::BodyOwnerKind::Closure |
6465
hir::BodyOwnerKind::Fn => hir::Constness::NotConst,
6566
};
6667

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

src/librustc_mir/transform/inline.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Inlining pass for MIR functions
22
3-
use rustc::hir;
43
use rustc::hir::CodegenFnAttrFlags;
54
use rustc::hir::def_id::DefId;
65

@@ -74,15 +73,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
7473

7574
// Only do inlining into fn bodies.
7675
let id = self.tcx.hir().as_local_node_id(self.source.def_id).unwrap();
77-
let body_owner_kind = self.tcx.hir().body_owner_kind(id);
78-
79-
if let (hir::BodyOwnerKind::Fn, None) = (body_owner_kind, self.source.promoted) {
80-
76+
if self.tcx.hir().body_owner_kind(id).is_fn_or_closure() && self.source.promoted.is_none() {
8177
for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated() {
8278
if let Some(callsite) = self.get_valid_function_call(bb,
83-
bb_data,
84-
caller_mir,
85-
param_env) {
79+
bb_data,
80+
caller_mir,
81+
param_env) {
8682
callsites.push_back(callsite);
8783
}
8884
}

src/librustc_mir/transform/qualify_consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,7 @@ impl MirPass for QualifyAndPromoteConstants {
11521152
let id = tcx.hir().as_local_node_id(def_id).unwrap();
11531153
let mut const_promoted_temps = None;
11541154
let mode = match tcx.hir().body_owner_kind(id) {
1155+
hir::BodyOwnerKind::Closure => Mode::Fn,
11551156
hir::BodyOwnerKind::Fn => {
11561157
if tcx.is_const_fn(def_id) {
11571158
Mode::ConstFn

src/librustc_mir/util/pretty.rs

+2
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut dyn Write) -> i
573573
let body_owner_kind = tcx.hir().body_owner_kind(id);
574574
match (body_owner_kind, src.promoted) {
575575
(_, Some(i)) => write!(w, "{:?} in", i)?,
576+
(hir::BodyOwnerKind::Closure, _) |
576577
(hir::BodyOwnerKind::Fn, _) => write!(w, "fn")?,
577578
(hir::BodyOwnerKind::Const, _) => write!(w, "const")?,
578579
(hir::BodyOwnerKind::Static(hir::MutImmutable), _) => write!(w, "static")?,
@@ -585,6 +586,7 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut dyn Write) -> i
585586
})?;
586587

587588
match (body_owner_kind, src.promoted) {
589+
(hir::BodyOwnerKind::Closure, None) |
588590
(hir::BodyOwnerKind::Fn, None) => {
589591
write!(w, "(")?;
590592

src/librustc_passes/rvalue_promotion.rs

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
191191
self.in_static = false;
192192

193193
match self.tcx.hir().body_owner_kind(item_id) {
194+
hir::BodyOwnerKind::Closure |
194195
hir::BodyOwnerKind::Fn => self.in_fn = true,
195196
hir::BodyOwnerKind::Static(_) => self.in_static = true,
196197
_ => {}
+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() {}

0 commit comments

Comments
 (0)