]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_consts/ops.rs
Add note to src/ci/docker/README.md about multiple docker images
[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::feature_err;
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     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
57         Some(tcx.features().const_if_match)
58     }
59 }
60
61 /// A function call where the callee is a pointer.
62 #[derive(Debug)]
63 pub struct FnCallIndirect;
64 impl NonConstOp for FnCallIndirect {
65     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
66         let mut err = item.tcx.sess.struct_span_err(
67             span,
68             &format!("function pointers are not allowed in const fn"));
69         err.emit();
70     }
71 }
72
73 /// A function call where the callee is not marked as `const`.
74 #[derive(Debug)]
75 pub struct FnCallNonConst(pub DefId);
76 impl NonConstOp for FnCallNonConst {
77     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
78         let mut err = struct_span_err!(
79             item.tcx.sess,
80             span,
81             E0015,
82             "calls in {}s are limited to constant functions, \
83              tuple structs and tuple variants",
84             item.const_kind(),
85         );
86         err.emit();
87     }
88 }
89
90 /// A function call where the callee is not a function definition or function pointer, e.g. a
91 /// closure.
92 ///
93 /// This can be subdivided in the future to produce a better error message.
94 #[derive(Debug)]
95 pub struct FnCallOther;
96 impl NonConstOp for FnCallOther {
97     const IS_SUPPORTED_IN_MIRI: bool = false;
98 }
99
100 /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
101 ///
102 /// Contains the name of the feature that would allow the use of this function.
103 #[derive(Debug)]
104 pub struct FnCallUnstable(pub DefId, pub Symbol);
105 impl NonConstOp for FnCallUnstable {
106     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
107         let FnCallUnstable(def_id, feature) = *self;
108
109         let mut err = item.tcx.sess.struct_span_err(span,
110             &format!("`{}` is not yet stable as a const fn",
111                     item.tcx.def_path_str(def_id)));
112         if nightly_options::is_nightly_build() {
113             help!(&mut err,
114                   "add `#![feature({})]` to the \
115                    crate attributes to enable",
116                   feature);
117         }
118         err.emit();
119     }
120 }
121
122 #[derive(Debug)]
123 pub struct HeapAllocation;
124 impl NonConstOp for HeapAllocation {
125     const IS_SUPPORTED_IN_MIRI: bool = false;
126
127     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
128         let mut err = struct_span_err!(item.tcx.sess, span, E0010,
129                                        "allocations are not allowed in {}s", item.const_kind());
130         err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
131         if item.tcx.sess.teach(&err.get_code().unwrap()) {
132             err.note(
133                 "The value of statics and constants must be known at compile time, \
134                  and they live for the entire lifetime of a program. Creating a boxed \
135                  value allocates memory on the heap at runtime, and therefore cannot \
136                  be done at compile time."
137             );
138         }
139         err.emit();
140     }
141 }
142
143 #[derive(Debug)]
144 pub struct IfOrMatch;
145 impl NonConstOp for IfOrMatch {
146     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
147         Some(tcx.features().const_if_match)
148     }
149
150     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
151         // This should be caught by the HIR const-checker.
152         item.tcx.sess.delay_span_bug(
153             span,
154             "complex control flow is forbidden in a const context",
155         );
156     }
157 }
158
159 #[derive(Debug)]
160 pub struct LiveDrop;
161 impl NonConstOp for LiveDrop {
162     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
163         struct_span_err!(item.tcx.sess, span, E0493,
164                          "destructors cannot be evaluated at compile-time")
165             .span_label(span, format!("{}s cannot evaluate destructors",
166                                       item.const_kind()))
167             .emit();
168     }
169 }
170
171 #[derive(Debug)]
172 pub struct Loop;
173 impl NonConstOp for Loop {
174     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
175         // This should be caught by the HIR const-checker.
176         item.tcx.sess.delay_span_bug(
177             span,
178             "complex control flow is forbidden in a const context",
179         );
180     }
181 }
182
183 #[derive(Debug)]
184 pub struct MutBorrow(pub BorrowKind);
185 impl NonConstOp for MutBorrow {
186     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
187         let kind = self.0;
188         if let BorrowKind::Mut { .. } = kind {
189             let mut err = struct_span_err!(item.tcx.sess, span, E0017,
190                                            "references in {}s may only refer \
191                                             to immutable values", item.const_kind());
192             err.span_label(span, format!("{}s require immutable values",
193                                                 item.const_kind()));
194             if item.tcx.sess.teach(&err.get_code().unwrap()) {
195                 err.note("References in statics and constants may only refer \
196                           to immutable values.\n\n\
197                           Statics are shared everywhere, and if they refer to \
198                           mutable data one might violate memory safety since \
199                           holding multiple mutable references to shared data \
200                           is not allowed.\n\n\
201                           If you really want global mutable state, try using \
202                           static mut or a global UnsafeCell.");
203             }
204             err.emit();
205         } else {
206             span_err!(item.tcx.sess, span, E0492,
207                       "cannot borrow a constant which may contain \
208                        interior mutability, create a static instead");
209         }
210     }
211 }
212
213 #[derive(Debug)]
214 pub struct MutDeref;
215 impl NonConstOp for MutDeref {}
216
217 #[derive(Debug)]
218 pub struct Panic;
219 impl NonConstOp for Panic {
220     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
221         Some(tcx.features().const_panic)
222     }
223
224     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
225         feature_err(
226             &item.tcx.sess.parse_sess,
227             sym::const_panic,
228             span,
229             &format!("panicking in {}s is unstable", item.const_kind()),
230         )
231         .emit();
232     }
233 }
234
235 #[derive(Debug)]
236 pub struct RawPtrComparison;
237 impl NonConstOp for RawPtrComparison {
238     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
239         Some(tcx.features().const_compare_raw_pointers)
240     }
241
242     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
243         feature_err(
244             &item.tcx.sess.parse_sess,
245             sym::const_compare_raw_pointers,
246             span,
247             &format!("comparing raw pointers inside {}", item.const_kind()),
248         )
249         .emit();
250     }
251 }
252
253 #[derive(Debug)]
254 pub struct RawPtrDeref;
255 impl NonConstOp for RawPtrDeref {
256     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
257         Some(tcx.features().const_raw_ptr_deref)
258     }
259
260     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
261         feature_err(
262             &item.tcx.sess.parse_sess, sym::const_raw_ptr_deref, span,
263             &format!(
264                 "dereferencing raw pointers in {}s is unstable",
265                 item.const_kind(),
266             ),
267         )
268         .emit();
269     }
270 }
271
272 #[derive(Debug)]
273 pub struct RawPtrToIntCast;
274 impl NonConstOp for RawPtrToIntCast {
275     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
276         Some(tcx.features().const_raw_ptr_to_usize_cast)
277     }
278
279     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
280         feature_err(
281             &item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast, span,
282             &format!(
283                 "casting pointers to integers in {}s is unstable",
284                 item.const_kind(),
285             ),
286         )
287         .emit();
288     }
289 }
290
291 /// An access to a (non-thread-local) `static`.
292 #[derive(Debug)]
293 pub struct StaticAccess;
294 impl NonConstOp for StaticAccess {
295     fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
296         item.const_kind().is_static()
297     }
298
299     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
300         let mut err = struct_span_err!(item.tcx.sess, span, E0013,
301                                         "{}s cannot refer to statics, use \
302                                         a constant instead", item.const_kind());
303         if item.tcx.sess.teach(&err.get_code().unwrap()) {
304             err.note(
305                 "Static and const variables can refer to other const variables. \
306                     But a const variable cannot refer to a static variable."
307             );
308             err.help(
309                 "To fix this, the value can be extracted as a const and then used."
310             );
311         }
312         err.emit();
313     }
314 }
315
316 /// An access to a thread-local `static`.
317 #[derive(Debug)]
318 pub struct ThreadLocalAccess;
319 impl NonConstOp for ThreadLocalAccess {
320     const IS_SUPPORTED_IN_MIRI: bool = false;
321
322     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
323         span_err!(item.tcx.sess, span, E0625,
324             "thread-local statics cannot be \
325             accessed at compile-time");
326     }
327 }
328
329 #[derive(Debug)]
330 pub struct UnionAccess;
331 impl NonConstOp for UnionAccess {
332     fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
333         // Union accesses are stable in all contexts except `const fn`.
334         item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap()
335     }
336
337     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
338         Some(tcx.features().const_fn_union)
339     }
340
341     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
342         feature_err(
343             &item.tcx.sess.parse_sess, sym::const_fn_union, span,
344             "unions in const fn are unstable",
345         )
346         .emit();
347     }
348 }