]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_consts/ops.rs
303c3984f7c0f66dd6e9bc5ee17b2cdce53f723b
[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
143 #[derive(Debug)]
144 pub struct LiveDrop;
145 impl NonConstOp for LiveDrop {
146     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
147         struct_span_err!(item.tcx.sess, span, E0493,
148                          "destructors cannot be evaluated at compile-time")
149             .span_label(span, format!("{}s cannot evaluate destructors",
150                                       item.const_kind()))
151             .emit();
152     }
153 }
154
155 #[derive(Debug)]
156 pub struct Loop;
157 impl NonConstOp for Loop {}
158
159 #[derive(Debug)]
160 pub struct MutBorrow(pub BorrowKind);
161 impl NonConstOp for MutBorrow {
162     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
163         let kind = self.0;
164         if let BorrowKind::Mut { .. } = kind {
165             let mut err = struct_span_err!(item.tcx.sess, span, E0017,
166                                            "references in {}s may only refer \
167                                             to immutable values", item.const_kind());
168             err.span_label(span, format!("{}s require immutable values",
169                                                 item.const_kind()));
170             if item.tcx.sess.teach(&err.get_code().unwrap()) {
171                 err.note("References in statics and constants may only refer \
172                           to immutable values.\n\n\
173                           Statics are shared everywhere, and if they refer to \
174                           mutable data one might violate memory safety since \
175                           holding multiple mutable references to shared data \
176                           is not allowed.\n\n\
177                           If you really want global mutable state, try using \
178                           static mut or a global UnsafeCell.");
179             }
180             err.emit();
181         } else {
182             span_err!(item.tcx.sess, span, E0492,
183                       "cannot borrow a constant which may contain \
184                        interior mutability, create a static instead");
185         }
186     }
187 }
188
189 #[derive(Debug)]
190 pub struct MutDeref;
191 impl NonConstOp for MutDeref {}
192
193 #[derive(Debug)]
194 pub struct Panic;
195 impl NonConstOp for Panic {
196     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
197         Some(tcx.features().const_panic)
198     }
199
200     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
201         emit_feature_err(
202             &item.tcx.sess.parse_sess,
203             sym::const_panic,
204             span,
205             GateIssue::Language,
206             &format!("panicking in {}s is unstable", item.const_kind()),
207         );
208     }
209 }
210
211 #[derive(Debug)]
212 pub struct RawPtrComparison;
213 impl NonConstOp for RawPtrComparison {
214     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
215         Some(tcx.features().const_compare_raw_pointers)
216     }
217
218     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
219         emit_feature_err(
220             &item.tcx.sess.parse_sess,
221             sym::const_compare_raw_pointers,
222             span,
223             GateIssue::Language,
224             &format!("comparing raw pointers inside {}", item.const_kind()),
225         );
226     }
227 }
228
229 #[derive(Debug)]
230 pub struct RawPtrDeref;
231 impl NonConstOp for RawPtrDeref {
232     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
233         Some(tcx.features().const_raw_ptr_deref)
234     }
235
236     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
237         emit_feature_err(
238             &item.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
239             span, GateIssue::Language,
240             &format!(
241                 "dereferencing raw pointers in {}s is unstable",
242                 item.const_kind(),
243             ),
244         );
245     }
246 }
247
248 #[derive(Debug)]
249 pub struct RawPtrToIntCast;
250 impl NonConstOp for RawPtrToIntCast {
251     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
252         Some(tcx.features().const_raw_ptr_to_usize_cast)
253     }
254
255     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
256         emit_feature_err(
257             &item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
258             span, GateIssue::Language,
259             &format!(
260                 "casting pointers to integers in {}s is unstable",
261                 item.const_kind(),
262             ),
263         );
264     }
265 }
266
267 /// An access to a (non-thread-local) `static`.
268 #[derive(Debug)]
269 pub struct StaticAccess;
270 impl NonConstOp for StaticAccess {
271     fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
272         item.const_kind().is_static()
273     }
274
275     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
276         let mut err = struct_span_err!(item.tcx.sess, span, E0013,
277                                         "{}s cannot refer to statics, use \
278                                         a constant instead", item.const_kind());
279         if item.tcx.sess.teach(&err.get_code().unwrap()) {
280             err.note(
281                 "Static and const variables can refer to other const variables. \
282                     But a const variable cannot refer to a static variable."
283             );
284             err.help(
285                 "To fix this, the value can be extracted as a const and then used."
286             );
287         }
288         err.emit();
289     }
290 }
291
292 /// An access to a thread-local `static`.
293 #[derive(Debug)]
294 pub struct ThreadLocalAccess;
295 impl NonConstOp for ThreadLocalAccess {
296     const IS_SUPPORTED_IN_MIRI: bool = false;
297
298     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
299         span_err!(item.tcx.sess, span, E0625,
300             "thread-local statics cannot be \
301             accessed at compile-time");
302     }
303 }
304
305 #[derive(Debug)]
306 pub struct Transmute;
307 impl NonConstOp for Transmute {
308     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
309         Some(tcx.features().const_transmute)
310     }
311
312     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
313         emit_feature_err(
314             &item.tcx.sess.parse_sess, sym::const_transmute,
315             span, GateIssue::Language,
316             &format!("The use of std::mem::transmute() \
317             is gated in {}s", item.const_kind()));
318     }
319 }
320
321 #[derive(Debug)]
322 pub struct UnionAccess;
323 impl NonConstOp for UnionAccess {
324     fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
325         // Union accesses are stable in all contexts except `const fn`.
326         item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap()
327     }
328
329     fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
330         Some(tcx.features().const_fn_union)
331     }
332
333     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
334         emit_feature_err(
335             &item.tcx.sess.parse_sess, sym::const_fn_union,
336             span, GateIssue::Language,
337             "unions in const fn are unstable",
338         );
339     }
340 }