Skip to content

Commit 39e9516

Browse files
committed
Auto merge of rust-lang#51990 - oli-obk:unstable_union, r=nikomatsakis
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
2 parents 18925de + 4b731a9 commit 39e9516

35 files changed

+317
-200
lines changed

src/libcore/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
#![feature(concat_idents)]
8383
#![feature(const_fn)]
8484
#![feature(const_int_ops)]
85+
#![feature(const_fn_union)]
8586
#![feature(custom_attribute)]
8687
#![feature(doc_cfg)]
8788
#![feature(doc_spotlight)]

src/librustc_mir/diagnostics.rs

-85
Original file line numberDiff line numberDiff line change
@@ -619,38 +619,6 @@ If you really want global mutable state, try using `static mut` or a global
619619
`UnsafeCell`.
620620
"##,
621621

622-
E0018: r##"
623-
624-
The value of static and constant integers must be known at compile time. You
625-
can't cast a pointer to an integer because the address of a pointer can
626-
vary.
627-
628-
For example, if you write:
629-
630-
```compile_fail,E0018
631-
static MY_STATIC: u32 = 42;
632-
static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
633-
static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
634-
```
635-
636-
Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However,
637-
the address can change when the program is linked, as well as change
638-
between different executions due to ASLR, and many linkers would
639-
not be able to calculate the value of `WHAT`.
640-
641-
On the other hand, static and constant pointers can point either to
642-
a known numeric address or to the address of a symbol.
643-
644-
```
645-
static MY_STATIC: u32 = 42;
646-
static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
647-
const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
648-
```
649-
650-
This does not pose a problem by itself because they can't be
651-
accessed directly.
652-
"##,
653-
654622
E0019: r##"
655623
A function call isn't allowed in the const's initialization expression
656624
because the expression's value must be known at compile-time. Erroneous code
@@ -1145,36 +1113,6 @@ fn main() {
11451113
```
11461114
"##,
11471115

1148-
E0395: r##"
1149-
The value assigned to a constant scalar must be known at compile time,
1150-
which is not the case when comparing raw pointers.
1151-
1152-
Erroneous code example:
1153-
1154-
```compile_fail,E0395
1155-
static FOO: i32 = 42;
1156-
static BAR: i32 = 42;
1157-
1158-
static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
1159-
// error: raw pointers cannot be compared in statics!
1160-
```
1161-
1162-
The address assigned by the linker to `FOO` and `BAR` may or may not
1163-
be identical, so the value of `BAZ` can't be determined.
1164-
1165-
If you want to do the comparison, please do it at run-time.
1166-
1167-
For example:
1168-
1169-
```
1170-
static FOO: i32 = 42;
1171-
static BAR: i32 = 42;
1172-
1173-
let baz: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
1174-
// baz isn't a constant expression so it's ok
1175-
```
1176-
"##,
1177-
11781116
E0161: r##"
11791117
A value was moved. However, its size was not known at compile time, and only
11801118
values of a known size can be moved.
@@ -1208,29 +1146,6 @@ fn main() {
12081146
```
12091147
"##,
12101148

1211-
E0396: r##"
1212-
The value behind a raw pointer can't be determined at compile-time
1213-
(or even link-time), which means it can't be used in a constant
1214-
expression. Erroneous code example:
1215-
1216-
```compile_fail,E0396
1217-
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
1218-
1219-
const VALUE: u8 = unsafe { *REG_ADDR };
1220-
// error: raw pointers cannot be dereferenced in constants
1221-
```
1222-
1223-
A possible fix is to dereference your pointer at some point in run-time.
1224-
1225-
For example:
1226-
1227-
```
1228-
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
1229-
1230-
let reg_value = unsafe { *REG_ADDR };
1231-
```
1232-
"##,
1233-
12341149
E0492: r##"
12351150
A borrow of a constant containing interior mutability was attempted. Erroneous
12361151
code example:

src/librustc_mir/transform/qualify_consts.rs

+51-73
Original file line numberDiff line numberDiff line change
@@ -491,41 +491,46 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
491491
this.super_place(place, context, location);
492492
match proj.elem {
493493
ProjectionElem::Deref => {
494-
this.add(Qualif::NOT_CONST);
495-
496-
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
497-
if let ty::TyRawPtr(_) = base_ty.sty {
498-
if this.mode != Mode::Fn {
499-
let mut err = struct_span_err!(
500-
this.tcx.sess,
501-
this.span,
502-
E0396,
503-
"raw pointers cannot be dereferenced in {}s",
504-
this.mode
505-
);
506-
err.span_label(this.span,
507-
"dereference of raw pointer in constant");
508-
if this.tcx.sess.teach(&err.get_code().unwrap()) {
509-
err.note(
510-
"The value behind a raw pointer can't be determined \
511-
at compile-time (or even link-time), which means it \
512-
can't be used in a constant expression."
494+
if let Mode::Fn = this.mode {
495+
this.add(Qualif::NOT_CONST);
496+
} else {
497+
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
498+
if let ty::TyRawPtr(_) = base_ty.sty {
499+
if !this.tcx.sess.features_untracked().const_raw_ptr_deref {
500+
emit_feature_err(
501+
&this.tcx.sess.parse_sess, "const_raw_ptr_deref",
502+
this.span, GateIssue::Language,
503+
&format!(
504+
"dereferencing raw pointers in {}s is unstable",
505+
this.mode,
506+
),
513507
);
514-
err.help("A possible fix is to dereference your pointer \
515-
at some point in run-time.");
516508
}
517-
err.emit();
518509
}
519510
}
520511
}
521512

522513
ProjectionElem::Field(..) |
523514
ProjectionElem::Index(_) => {
524-
if this.mode == Mode::Fn {
525-
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
526-
if let Some(def) = base_ty.ty_adt_def() {
527-
if def.is_union() {
528-
this.not_const();
515+
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
516+
if let Some(def) = base_ty.ty_adt_def() {
517+
if def.is_union() {
518+
match this.mode {
519+
Mode::Fn => this.not_const(),
520+
Mode::ConstFn => {
521+
if !this.tcx.sess.features_untracked().const_fn_union {
522+
emit_feature_err(
523+
&this.tcx.sess.parse_sess, "const_fn_union",
524+
this.span, GateIssue::Language,
525+
"unions in const fn are unstable",
526+
);
527+
}
528+
},
529+
530+
| Mode::Static
531+
| Mode::StaticMut
532+
| Mode::Const
533+
=> {},
529534
}
530535
}
531536
}
@@ -722,44 +727,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
722727
match (cast_in, cast_out) {
723728
(CastTy::Ptr(_), CastTy::Int(_)) |
724729
(CastTy::FnPtr, CastTy::Int(_)) => {
725-
self.add(Qualif::NOT_CONST);
726-
if self.mode != Mode::Fn {
727-
let mut err = struct_span_err!(
728-
self.tcx.sess,
729-
self.span,
730-
E0018,
731-
"raw pointers cannot be cast to integers in {}s",
732-
self.mode
730+
if let Mode::Fn = self.mode {
731+
self.add(Qualif::NOT_CONST);
732+
} else if !self.tcx.sess.features_untracked().const_raw_ptr_to_usize_cast {
733+
emit_feature_err(
734+
&self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
735+
self.span, GateIssue::Language,
736+
&format!(
737+
"casting pointers to integers in {}s is unstable",
738+
self.mode,
739+
),
733740
);
734-
if self.tcx.sess.teach(&err.get_code().unwrap()) {
735-
err.note("\
736-
The value of static and constant integers must be known at compile time. You can't cast a pointer \
737-
to an integer because the address of a pointer can vary.
738-
739-
For example, if you write:
740-
741-
```
742-
static MY_STATIC: u32 = 42;
743-
static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
744-
static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
745-
```
746-
747-
Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However, the address can change \
748-
when the program is linked, as well as change between different executions due to ASLR, and many \
749-
linkers would not be able to calculate the value of `WHAT`.
750-
751-
On the other hand, static and constant pointers can point either to a known numeric address or to \
752-
the address of a symbol.
753-
754-
```
755-
static MY_STATIC: u32 = 42;
756-
static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
757-
const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
758-
```
759-
760-
This does not pose a problem by itself because they can't be accessed directly.");
761-
}
762-
err.emit();
763741
}
764742
}
765743
_ => {}
@@ -773,16 +751,16 @@ This does not pose a problem by itself because they can't be accessed directly."
773751
op == BinOp::Ge || op == BinOp::Gt ||
774752
op == BinOp::Offset);
775753

776-
self.add(Qualif::NOT_CONST);
777-
if self.mode != Mode::Fn {
778-
struct_span_err!(
779-
self.tcx.sess, self.span, E0395,
780-
"raw pointers cannot be compared in {}s",
781-
self.mode)
782-
.span_label(
754+
if let Mode::Fn = self.mode {
755+
self.add(Qualif::NOT_CONST);
756+
} else if !self.tcx.sess.features_untracked().const_compare_raw_pointers {
757+
emit_feature_err(
758+
&self.tcx.sess.parse_sess,
759+
"const_compare_raw_pointers",
783760
self.span,
784-
"comparing raw pointers in static")
785-
.emit();
761+
GateIssue::Language,
762+
&format!("comparing raw pointers inside {}", self.mode),
763+
);
786764
}
787765
}
788766
}

src/libsyntax/feature_gate.rs

+12
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,18 @@ declare_features! (
216216
// Allows let bindings and destructuring in `const fn` functions and constants.
217217
(active, const_let, "1.22.1", Some(48821), None),
218218

219+
// Allows accessing fields of unions inside const fn
220+
(active, const_fn_union, "1.27.0", Some(51909), None),
221+
222+
// Allows casting raw pointers to `usize` during const eval
223+
(active, const_raw_ptr_to_usize_cast, "1.27.0", Some(51910), None),
224+
225+
// Allows dereferencing raw pointers during const eval
226+
(active, const_raw_ptr_deref, "1.27.0", Some(51911), None),
227+
228+
// Allows comparing raw pointers during const eval
229+
(active, const_compare_raw_pointers, "1.27.0", Some(53020), None),
230+
219231
// Allows using #[prelude_import] on glob `use` items.
220232
//
221233
// rustc internal

src/test/compile-fail/cast-ptr-to-int-const.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// gate-test-const_raw_ptr_to_usize_cast
12+
1113
fn main() {
12-
const X: u32 = main as u32; //~ ERROR E0018
14+
const X: u32 = main as u32; //~ ERROR casting pointers to integers in constants is unstable
1315
const Y: u32 = 0;
14-
const Z: u32 = &Y as *const u32 as u32; //~ ERROR E0018
16+
const Z: u32 = &Y as *const u32 as u32; //~ ERROR is unstable
1517
}

src/test/ui/const-deref-ptr.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// Check that you can't dereference raw pointers in constants.
1212

1313
fn main() {
14-
static C: u64 = unsafe {*(0xdeadbeef as *const u64)}; //~ ERROR E0396
14+
static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
15+
//~^ ERROR dereferencing raw pointers in statics is unstable
1516
println!("{}", C);
1617
}

src/test/ui/const-deref-ptr.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
error[E0396]: raw pointers cannot be dereferenced in statics
1+
error[E0658]: dereferencing raw pointers in statics is unstable (see issue #51911)
22
--> $DIR/const-deref-ptr.rs:14:29
33
|
4-
LL | static C: u64 = unsafe {*(0xdeadbeef as *const u64)}; //~ ERROR E0396
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer in constant
4+
LL | static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable
68

79
error: aborting due to previous error
810

9-
For more information about this error, try `rustc --explain E0396`.
11+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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(const_raw_ptr_to_usize_cast, const_compare_raw_pointers, const_raw_ptr_deref)]
12+
13+
fn main() {}
14+
15+
// unconst and bad, will thus error in miri
16+
const X: bool = &1 as *const i32 == &2 as *const i32; //~ ERROR cannot be used
17+
// unconst and fine
18+
const X2: bool = 42 as *const i32 == 43 as *const i32;
19+
// unconst and fine
20+
const Y: usize = 42usize as *const i32 as usize + 1;
21+
// unconst and bad, will thus error in miri
22+
const Y2: usize = &1 as *const i32 as usize + 1; //~ ERROR cannot be used
23+
// unconst and fine
24+
const Z: i32 = unsafe { *(&1 as *const i32) };
25+
// unconst and bad, will thus error in miri
26+
const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR cannot be used
27+
const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR cannot be used
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error: this constant cannot be used
2+
--> $DIR/const_raw_ptr_ops.rs:16:1
3+
|
4+
LL | const X: bool = &1 as *const i32 == &2 as *const i32; //~ ERROR cannot be used
5+
| ^^^^^^^^^^^^^^^^------------------------------------^
6+
| |
7+
| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
8+
|
9+
= note: #[deny(const_err)] on by default
10+
11+
error: this constant cannot be used
12+
--> $DIR/const_raw_ptr_ops.rs:22:1
13+
|
14+
LL | const Y2: usize = &1 as *const i32 as usize + 1; //~ ERROR cannot be used
15+
| ^^^^^^^^^^^^^^^^^^-----------------------------^
16+
| |
17+
| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
18+
19+
error: this constant cannot be used
20+
--> $DIR/const_raw_ptr_ops.rs:26:1
21+
|
22+
LL | const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR cannot be used
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^-------------------^^^
24+
| |
25+
| tried to access memory with alignment 2, but alignment 4 is required
26+
27+
error: this constant cannot be used
28+
--> $DIR/const_raw_ptr_ops.rs:27:1
29+
|
30+
LL | const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR cannot be used
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^-------------------^^^
32+
| |
33+
| a memory access tried to interpret some bytes as a pointer
34+
35+
error: aborting due to 4 previous errors
36+

src/test/ui/const-eval/const_transmute.rs

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// compile-pass
1212
// run-pass
1313

14+
#![feature(const_fn_union)]
15+
1416
union Transmute<T: Copy, U: Copy> {
1517
t: T,
1618
u: U,

0 commit comments

Comments
 (0)