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