Skip to content

Commit b3e59bb

Browse files
Move non-const ops into their own module
1 parent e296436 commit b3e59bb

File tree

4 files changed

+342
-337
lines changed

4 files changed

+342
-337
lines changed

src/librustc_mir/transform/check_consts/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use rustc::ty::{self, TyCtxt};
44

55
pub use self::qualifs::Qualif;
66

7-
mod resolver;
7+
pub mod ops;
88
mod qualifs;
9+
mod resolver;
910
pub mod validation;
1011

1112
/// Information about the item currently being validated, as well as a reference to the global
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
use rustc::hir::def_id::DefId;
2+
use rustc::mir::BorrowKind;
3+
use rustc::session::config::nightly_options;
4+
use rustc::ty::TyCtxt;
5+
use syntax::feature_gate::{emit_feature_err, GateIssue};
6+
use syntax::symbol::sym;
7+
use syntax_pos::{Span, Symbol};
8+
9+
use super::Item;
10+
use super::validation::Mode;
11+
12+
/// An operation that is not *always* allowed in a const context.
13+
pub trait NonConstOp {
14+
/// Whether this operation can be evaluated by miri.
15+
const IS_SUPPORTED_IN_MIRI: bool = true;
16+
17+
/// Returns a boolean indicating whether the feature gate that would allow this operation is
18+
/// enabled, or `None` if such a feature gate does not exist.
19+
fn feature_gate(_tcx: TyCtxt<'tcx>) -> Option<bool> {
20+
None
21+
}
22+
23+
/// Returns `true` if this operation is allowed in the given item.
24+
///
25+
/// This check should assume that we are not in a non-const `fn`, where all operations are
26+
/// legal.
27+
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
28+
Self::feature_gate(item.tcx).unwrap_or(false)
29+
}
30+
31+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
32+
let mut err = struct_span_err!(
33+
item.tcx.sess,
34+
span,
35+
E0019,
36+
"{} contains unimplemented expression type",
37+
item.mode
38+
);
39+
if item.tcx.sess.teach(&err.get_code().unwrap()) {
40+
err.note("A function call isn't allowed in the const's initialization expression \
41+
because the expression's value must be known at compile-time.");
42+
err.note("Remember: you can't use a function call inside a const's initialization \
43+
expression! However, you can use it anywhere else.");
44+
}
45+
err.emit();
46+
}
47+
}
48+
49+
/// A `Downcast` projection.
50+
#[derive(Debug)]
51+
pub struct Downcast;
52+
impl NonConstOp for Downcast {}
53+
54+
/// A function call where the callee is a pointer.
55+
#[derive(Debug)]
56+
pub struct FnCallIndirect;
57+
impl NonConstOp for FnCallIndirect {
58+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
59+
let mut err = item.tcx.sess.struct_span_err(
60+
span,
61+
&format!("function pointers are not allowed in const fn"));
62+
err.emit();
63+
}
64+
}
65+
66+
/// A function call where the callee is not marked as `const`.
67+
#[derive(Debug)]
68+
pub struct FnCallNonConst(pub DefId);
69+
impl NonConstOp for FnCallNonConst {
70+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
71+
let mut err = struct_span_err!(
72+
item.tcx.sess,
73+
span,
74+
E0015,
75+
"calls in {}s are limited to constant functions, \
76+
tuple structs and tuple variants",
77+
item.mode,
78+
);
79+
err.emit();
80+
}
81+
}
82+
83+
/// A function call where the callee is not a function definition or function pointer, e.g. a
84+
/// closure.
85+
///
86+
/// This can be subdivided in the future to produce a better error message.
87+
#[derive(Debug)]
88+
pub struct FnCallOther;
89+
impl NonConstOp for FnCallOther {
90+
const IS_SUPPORTED_IN_MIRI: bool = false;
91+
}
92+
93+
/// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
94+
///
95+
/// Contains the name of the feature that would allow the use of this function.
96+
#[derive(Debug)]
97+
pub struct FnCallUnstable(pub DefId, pub Symbol);
98+
impl NonConstOp for FnCallUnstable {
99+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
100+
let FnCallUnstable(def_id, feature) = *self;
101+
102+
let mut err = item.tcx.sess.struct_span_err(span,
103+
&format!("`{}` is not yet stable as a const fn",
104+
item.tcx.def_path_str(def_id)));
105+
if nightly_options::is_nightly_build() {
106+
help!(&mut err,
107+
"add `#![feature({})]` to the \
108+
crate attributes to enable",
109+
feature);
110+
}
111+
err.emit();
112+
}
113+
}
114+
115+
#[derive(Debug)]
116+
pub struct HeapAllocation;
117+
impl NonConstOp for HeapAllocation {
118+
const IS_SUPPORTED_IN_MIRI: bool = false;
119+
120+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
121+
let mut err = struct_span_err!(item.tcx.sess, span, E0010,
122+
"allocations are not allowed in {}s", item.mode);
123+
err.span_label(span, format!("allocation not allowed in {}s", item.mode));
124+
if item.tcx.sess.teach(&err.get_code().unwrap()) {
125+
err.note(
126+
"The value of statics and constants must be known at compile time, \
127+
and they live for the entire lifetime of a program. Creating a boxed \
128+
value allocates memory on the heap at runtime, and therefore cannot \
129+
be done at compile time."
130+
);
131+
}
132+
err.emit();
133+
}
134+
}
135+
136+
#[derive(Debug)]
137+
pub struct IfOrMatch;
138+
impl NonConstOp for IfOrMatch {}
139+
140+
#[derive(Debug)]
141+
pub struct LiveDrop;
142+
impl NonConstOp for LiveDrop {
143+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
144+
struct_span_err!(item.tcx.sess, span, E0493,
145+
"destructors cannot be evaluated at compile-time")
146+
.span_label(span, format!("{}s cannot evaluate destructors",
147+
item.mode))
148+
.emit();
149+
}
150+
}
151+
152+
#[derive(Debug)]
153+
pub struct Loop;
154+
impl NonConstOp for Loop {}
155+
156+
#[derive(Debug)]
157+
pub struct MutBorrow(pub BorrowKind);
158+
impl NonConstOp for MutBorrow {
159+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
160+
let kind = self.0;
161+
if let BorrowKind::Mut { .. } = kind {
162+
let mut err = struct_span_err!(item.tcx.sess, span, E0017,
163+
"references in {}s may only refer \
164+
to immutable values", item.mode);
165+
err.span_label(span, format!("{}s require immutable values",
166+
item.mode));
167+
if item.tcx.sess.teach(&err.get_code().unwrap()) {
168+
err.note("References in statics and constants may only refer \
169+
to immutable values.\n\n\
170+
Statics are shared everywhere, and if they refer to \
171+
mutable data one might violate memory safety since \
172+
holding multiple mutable references to shared data \
173+
is not allowed.\n\n\
174+
If you really want global mutable state, try using \
175+
static mut or a global UnsafeCell.");
176+
}
177+
err.emit();
178+
} else {
179+
span_err!(item.tcx.sess, span, E0492,
180+
"cannot borrow a constant which may contain \
181+
interior mutability, create a static instead");
182+
}
183+
}
184+
}
185+
186+
#[derive(Debug)]
187+
pub struct MutDeref;
188+
impl NonConstOp for MutDeref {}
189+
190+
#[derive(Debug)]
191+
pub struct Panic;
192+
impl NonConstOp for Panic {
193+
fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
194+
Some(tcx.features().const_panic)
195+
}
196+
197+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
198+
emit_feature_err(
199+
&item.tcx.sess.parse_sess,
200+
sym::const_panic,
201+
span,
202+
GateIssue::Language,
203+
&format!("panicking in {}s is unstable", item.mode),
204+
);
205+
}
206+
}
207+
208+
#[derive(Debug)]
209+
pub struct RawPtrComparison;
210+
impl NonConstOp for RawPtrComparison {
211+
fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
212+
Some(tcx.features().const_compare_raw_pointers)
213+
}
214+
215+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
216+
emit_feature_err(
217+
&item.tcx.sess.parse_sess,
218+
sym::const_compare_raw_pointers,
219+
span,
220+
GateIssue::Language,
221+
&format!("comparing raw pointers inside {}", item.mode),
222+
);
223+
}
224+
}
225+
226+
#[derive(Debug)]
227+
pub struct RawPtrDeref;
228+
impl NonConstOp for RawPtrDeref {
229+
fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
230+
Some(tcx.features().const_raw_ptr_deref)
231+
}
232+
233+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
234+
emit_feature_err(
235+
&item.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
236+
span, GateIssue::Language,
237+
&format!(
238+
"dereferencing raw pointers in {}s is unstable",
239+
item.mode,
240+
),
241+
);
242+
}
243+
}
244+
245+
#[derive(Debug)]
246+
pub struct RawPtrToIntCast;
247+
impl NonConstOp for RawPtrToIntCast {
248+
fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
249+
Some(tcx.features().const_raw_ptr_to_usize_cast)
250+
}
251+
252+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
253+
emit_feature_err(
254+
&item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
255+
span, GateIssue::Language,
256+
&format!(
257+
"casting pointers to integers in {}s is unstable",
258+
item.mode,
259+
),
260+
);
261+
}
262+
}
263+
264+
/// An access to a (non-thread-local) `static`.
265+
#[derive(Debug)]
266+
pub struct StaticAccess;
267+
impl NonConstOp for StaticAccess {
268+
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
269+
item.mode.is_static()
270+
}
271+
272+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
273+
let mut err = struct_span_err!(item.tcx.sess, span, E0013,
274+
"{}s cannot refer to statics, use \
275+
a constant instead", item.mode);
276+
if item.tcx.sess.teach(&err.get_code().unwrap()) {
277+
err.note(
278+
"Static and const variables can refer to other const variables. \
279+
But a const variable cannot refer to a static variable."
280+
);
281+
err.help(
282+
"To fix this, the value can be extracted as a const and then used."
283+
);
284+
}
285+
err.emit();
286+
}
287+
}
288+
289+
/// An access to a thread-local `static`.
290+
#[derive(Debug)]
291+
pub struct ThreadLocalAccess;
292+
impl NonConstOp for ThreadLocalAccess {
293+
const IS_SUPPORTED_IN_MIRI: bool = false;
294+
295+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
296+
span_err!(item.tcx.sess, span, E0625,
297+
"thread-local statics cannot be \
298+
accessed at compile-time");
299+
}
300+
}
301+
302+
#[derive(Debug)]
303+
pub struct Transmute;
304+
impl NonConstOp for Transmute {
305+
fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
306+
Some(tcx.features().const_transmute)
307+
}
308+
309+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
310+
emit_feature_err(
311+
&item.tcx.sess.parse_sess, sym::const_transmute,
312+
span, GateIssue::Language,
313+
&format!("The use of std::mem::transmute() \
314+
is gated in {}s", item.mode));
315+
}
316+
}
317+
318+
#[derive(Debug)]
319+
pub struct UnionAccess;
320+
impl NonConstOp for UnionAccess {
321+
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
322+
// Union accesses are stable in all contexts except `const fn`.
323+
item.mode != Mode::ConstFn || Self::feature_gate(item.tcx).unwrap()
324+
}
325+
326+
fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
327+
Some(tcx.features().const_fn_union)
328+
}
329+
330+
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
331+
emit_feature_err(
332+
&item.tcx.sess.parse_sess, sym::const_fn_union,
333+
span, GateIssue::Language,
334+
"unions in const fn are unstable",
335+
);
336+
}
337+
}

0 commit comments

Comments
 (0)