Skip to content

Commit

Permalink
Auto merge of #51990 - oli-obk:unstable_union, r=nikomatsakis
Browse files Browse the repository at this point in the history
Place unions, pointer casts and pointer derefs behind extra feature gates

To ensure we don't stabilize these things together with const fn stabilization (or any other stabilization)

This PR moves union field accesses inside `const fn` behind a feature gate. It was possible without a feature gate before, but since `const fn` was behind a feature gate we can do this change.

While "dereferencing raw pointers" and "casting raw pointers to usize" were hard errors before this PR, one could work around them by abusing unions:

```rust
// deref
union Foo<T> {
    x: &'static T,
    y: *const T,
}
const FOO: u32 = unsafe { *Foo { y: 42 as *const T }.x };

// as usize cast
union Bar<T> {
    x: usize,
    y: *const T,
}
const BAR: usize = unsafe { Bar { y: &1u8 }.x };
```

r? @eddyb

cc @nikomatsakis
  • Loading branch information
bors committed Aug 7, 2018
2 parents 18925de + 4b731a9 commit 39e9516
Show file tree
Hide file tree
Showing 35 changed files with 317 additions and 200 deletions.
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
#![feature(concat_idents)]
#![feature(const_fn)]
#![feature(const_int_ops)]
#![feature(const_fn_union)]
#![feature(custom_attribute)]
#![feature(doc_cfg)]
#![feature(doc_spotlight)]
Expand Down
85 changes: 0 additions & 85 deletions src/librustc_mir/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,38 +619,6 @@ If you really want global mutable state, try using `static mut` or a global
`UnsafeCell`.
"##,

E0018: r##"
The value of static and constant integers must be known at compile time. You
can't cast a pointer to an integer because the address of a pointer can
vary.
For example, if you write:
```compile_fail,E0018
static MY_STATIC: u32 = 42;
static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
```
Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However,
the address can change when the program is linked, as well as change
between different executions due to ASLR, and many linkers would
not be able to calculate the value of `WHAT`.
On the other hand, static and constant pointers can point either to
a known numeric address or to the address of a symbol.
```
static MY_STATIC: u32 = 42;
static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
```
This does not pose a problem by itself because they can't be
accessed directly.
"##,

E0019: r##"
A function call isn't allowed in the const's initialization expression
because the expression's value must be known at compile-time. Erroneous code
Expand Down Expand Up @@ -1145,36 +1113,6 @@ fn main() {
```
"##,

E0395: r##"
The value assigned to a constant scalar must be known at compile time,
which is not the case when comparing raw pointers.
Erroneous code example:
```compile_fail,E0395
static FOO: i32 = 42;
static BAR: i32 = 42;
static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
// error: raw pointers cannot be compared in statics!
```
The address assigned by the linker to `FOO` and `BAR` may or may not
be identical, so the value of `BAZ` can't be determined.
If you want to do the comparison, please do it at run-time.
For example:
```
static FOO: i32 = 42;
static BAR: i32 = 42;
let baz: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
// baz isn't a constant expression so it's ok
```
"##,

E0161: r##"
A value was moved. However, its size was not known at compile time, and only
values of a known size can be moved.
Expand Down Expand Up @@ -1208,29 +1146,6 @@ fn main() {
```
"##,

E0396: r##"
The value behind a raw pointer can't be determined at compile-time
(or even link-time), which means it can't be used in a constant
expression. Erroneous code example:
```compile_fail,E0396
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
const VALUE: u8 = unsafe { *REG_ADDR };
// error: raw pointers cannot be dereferenced in constants
```
A possible fix is to dereference your pointer at some point in run-time.
For example:
```
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
let reg_value = unsafe { *REG_ADDR };
```
"##,

E0492: r##"
A borrow of a constant containing interior mutability was attempted. Erroneous
code example:
Expand Down
124 changes: 51 additions & 73 deletions src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,41 +491,46 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
this.super_place(place, context, location);
match proj.elem {
ProjectionElem::Deref => {
this.add(Qualif::NOT_CONST);

let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
if let ty::TyRawPtr(_) = base_ty.sty {
if this.mode != Mode::Fn {
let mut err = struct_span_err!(
this.tcx.sess,
this.span,
E0396,
"raw pointers cannot be dereferenced in {}s",
this.mode
);
err.span_label(this.span,
"dereference of raw pointer in constant");
if this.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"The value behind a raw pointer can't be determined \
at compile-time (or even link-time), which means it \
can't be used in a constant expression."
if let Mode::Fn = this.mode {
this.add(Qualif::NOT_CONST);
} else {
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
if let ty::TyRawPtr(_) = base_ty.sty {
if !this.tcx.sess.features_untracked().const_raw_ptr_deref {
emit_feature_err(
&this.tcx.sess.parse_sess, "const_raw_ptr_deref",
this.span, GateIssue::Language,
&format!(
"dereferencing raw pointers in {}s is unstable",
this.mode,
),
);
err.help("A possible fix is to dereference your pointer \
at some point in run-time.");
}
err.emit();
}
}
}

ProjectionElem::Field(..) |
ProjectionElem::Index(_) => {
if this.mode == Mode::Fn {
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
if let Some(def) = base_ty.ty_adt_def() {
if def.is_union() {
this.not_const();
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
if let Some(def) = base_ty.ty_adt_def() {
if def.is_union() {
match this.mode {
Mode::Fn => this.not_const(),
Mode::ConstFn => {
if !this.tcx.sess.features_untracked().const_fn_union {
emit_feature_err(
&this.tcx.sess.parse_sess, "const_fn_union",
this.span, GateIssue::Language,
"unions in const fn are unstable",
);
}
},

| Mode::Static
| Mode::StaticMut
| Mode::Const
=> {},
}
}
}
Expand Down Expand Up @@ -722,44 +727,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
match (cast_in, cast_out) {
(CastTy::Ptr(_), CastTy::Int(_)) |
(CastTy::FnPtr, CastTy::Int(_)) => {
self.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
let mut err = struct_span_err!(
self.tcx.sess,
self.span,
E0018,
"raw pointers cannot be cast to integers in {}s",
self.mode
if let Mode::Fn = self.mode {
self.add(Qualif::NOT_CONST);
} else if !self.tcx.sess.features_untracked().const_raw_ptr_to_usize_cast {
emit_feature_err(
&self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
self.span, GateIssue::Language,
&format!(
"casting pointers to integers in {}s is unstable",
self.mode,
),
);
if self.tcx.sess.teach(&err.get_code().unwrap()) {
err.note("\
The value of static and constant integers must be known at compile time. You can't cast a pointer \
to an integer because the address of a pointer can vary.
For example, if you write:
```
static MY_STATIC: u32 = 42;
static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
```
Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However, the address can change \
when the program is linked, as well as change between different executions due to ASLR, and many \
linkers would not be able to calculate the value of `WHAT`.
On the other hand, static and constant pointers can point either to a known numeric address or to \
the address of a symbol.
```
static MY_STATIC: u32 = 42;
static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
```
This does not pose a problem by itself because they can't be accessed directly.");
}
err.emit();
}
}
_ => {}
Expand All @@ -773,16 +751,16 @@ This does not pose a problem by itself because they can't be accessed directly."
op == BinOp::Ge || op == BinOp::Gt ||
op == BinOp::Offset);

self.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
struct_span_err!(
self.tcx.sess, self.span, E0395,
"raw pointers cannot be compared in {}s",
self.mode)
.span_label(
if let Mode::Fn = self.mode {
self.add(Qualif::NOT_CONST);
} else if !self.tcx.sess.features_untracked().const_compare_raw_pointers {
emit_feature_err(
&self.tcx.sess.parse_sess,
"const_compare_raw_pointers",
self.span,
"comparing raw pointers in static")
.emit();
GateIssue::Language,
&format!("comparing raw pointers inside {}", self.mode),
);
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,18 @@ declare_features! (
// Allows let bindings and destructuring in `const fn` functions and constants.
(active, const_let, "1.22.1", Some(48821), None),

// Allows accessing fields of unions inside const fn
(active, const_fn_union, "1.27.0", Some(51909), None),

// Allows casting raw pointers to `usize` during const eval
(active, const_raw_ptr_to_usize_cast, "1.27.0", Some(51910), None),

// Allows dereferencing raw pointers during const eval
(active, const_raw_ptr_deref, "1.27.0", Some(51911), None),

// Allows comparing raw pointers during const eval
(active, const_compare_raw_pointers, "1.27.0", Some(53020), None),

// Allows using #[prelude_import] on glob `use` items.
//
// rustc internal
Expand Down
6 changes: 4 additions & 2 deletions src/test/compile-fail/cast-ptr-to-int-const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// gate-test-const_raw_ptr_to_usize_cast

fn main() {
const X: u32 = main as u32; //~ ERROR E0018
const X: u32 = main as u32; //~ ERROR casting pointers to integers in constants is unstable
const Y: u32 = 0;
const Z: u32 = &Y as *const u32 as u32; //~ ERROR E0018
const Z: u32 = &Y as *const u32 as u32; //~ ERROR is unstable
}
3 changes: 2 additions & 1 deletion src/test/ui/const-deref-ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// Check that you can't dereference raw pointers in constants.

fn main() {
static C: u64 = unsafe {*(0xdeadbeef as *const u64)}; //~ ERROR E0396
static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
//~^ ERROR dereferencing raw pointers in statics is unstable
println!("{}", C);
}
10 changes: 6 additions & 4 deletions src/test/ui/const-deref-ptr.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
error[E0396]: raw pointers cannot be dereferenced in statics
error[E0658]: dereferencing raw pointers in statics is unstable (see issue #51911)
--> $DIR/const-deref-ptr.rs:14:29
|
LL | static C: u64 = unsafe {*(0xdeadbeef as *const u64)}; //~ ERROR E0396
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer in constant
LL | static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0396`.
For more information about this error, try `rustc --explain E0658`.
27 changes: 27 additions & 0 deletions src/test/ui/const-eval/const_raw_ptr_ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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(const_raw_ptr_to_usize_cast, const_compare_raw_pointers, const_raw_ptr_deref)]

fn main() {}

// unconst and bad, will thus error in miri
const X: bool = &1 as *const i32 == &2 as *const i32; //~ ERROR cannot be used
// unconst and fine
const X2: bool = 42 as *const i32 == 43 as *const i32;
// unconst and fine
const Y: usize = 42usize as *const i32 as usize + 1;
// unconst and bad, will thus error in miri
const Y2: usize = &1 as *const i32 as usize + 1; //~ ERROR cannot be used
// unconst and fine
const Z: i32 = unsafe { *(&1 as *const i32) };
// unconst and bad, will thus error in miri
const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR cannot be used
const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR cannot be used
36 changes: 36 additions & 0 deletions src/test/ui/const-eval/const_raw_ptr_ops.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
error: this constant cannot be used
--> $DIR/const_raw_ptr_ops.rs:16:1
|
LL | const X: bool = &1 as *const i32 == &2 as *const i32; //~ ERROR cannot be used
| ^^^^^^^^^^^^^^^^------------------------------------^
| |
| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
|
= note: #[deny(const_err)] on by default

error: this constant cannot be used
--> $DIR/const_raw_ptr_ops.rs:22:1
|
LL | const Y2: usize = &1 as *const i32 as usize + 1; //~ ERROR cannot be used
| ^^^^^^^^^^^^^^^^^^-----------------------------^
| |
| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants

error: this constant cannot be used
--> $DIR/const_raw_ptr_ops.rs:26:1
|
LL | const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR cannot be used
| ^^^^^^^^^^^^^^^^^^^^^^^^^-------------------^^^
| |
| tried to access memory with alignment 2, but alignment 4 is required

error: this constant cannot be used
--> $DIR/const_raw_ptr_ops.rs:27:1
|
LL | const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR cannot be used
| ^^^^^^^^^^^^^^^^^^^^^^^^^-------------------^^^
| |
| a memory access tried to interpret some bytes as a pointer

error: aborting due to 4 previous errors

2 changes: 2 additions & 0 deletions src/test/ui/const-eval/const_transmute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// compile-pass
// run-pass

#![feature(const_fn_union)]

union Transmute<T: Copy, U: Copy> {
t: T,
u: U,
Expand Down
Loading

0 comments on commit 39e9516

Please sign in to comment.