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