]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir/src/transform/check_consts/ops.rs
496e620dd9df61d10b53e061b1eee67008561b04
[rust.git] / compiler / rustc_mir / src / 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, Applicability};
4 use rustc_hir as hir;
5 use rustc_hir::def_id::DefId;
6 use rustc_session::config::nightly_options;
7 use rustc_session::parse::feature_err;
8 use rustc_span::symbol::sym;
9 use rustc_span::{Span, Symbol};
10
11 use super::ConstCx;
12
13 /// Emits an error and returns `true` if `op` is not allowed in the given const context.
14 pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) -> bool {
15     debug!("illegal_op: op={:?}", op);
16
17     let gate = match op.status_in_item(ccx) {
18         Status::Allowed => return false,
19
20         Status::Unstable(gate) if ccx.tcx.features().enabled(gate) => {
21             let unstable_in_stable = ccx.is_const_stable_const_fn()
22                 && !super::allow_internal_unstable(ccx.tcx, ccx.def_id.to_def_id(), gate);
23
24             if unstable_in_stable {
25                 ccx.tcx.sess
26                     .struct_span_err(
27                         span,
28                         &format!("const-stable function cannot use `#[feature({})]`", gate.as_str()),
29                     )
30                     .span_suggestion(
31                         ccx.body.span,
32                         "if it is not part of the public API, make this function unstably const",
33                         concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n').to_owned(),
34                         Applicability::HasPlaceholders,
35                     )
36                     .note("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks")
37                     .emit();
38             }
39
40             return unstable_in_stable;
41         }
42
43         Status::Unstable(gate) => Some(gate),
44         Status::Forbidden => None,
45     };
46
47     if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
48         ccx.tcx.sess.miri_unleashed_feature(span, gate);
49         return false;
50     }
51
52     op.emit_error(ccx, span);
53     true
54 }
55
56 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
57 pub enum Status {
58     Allowed,
59     Unstable(Symbol),
60     Forbidden,
61 }
62
63 /// An operation that is not *always* allowed in a const context.
64 pub trait NonConstOp: std::fmt::Debug {
65     const STOPS_CONST_CHECKING: bool = false;
66
67     /// Returns an enum indicating whether this operation is allowed within the given item.
68     fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
69         Status::Forbidden
70     }
71
72     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
73         let mut err = struct_span_err!(
74             ccx.tcx.sess,
75             span,
76             E0019,
77             "{} contains unimplemented expression type",
78             ccx.const_kind()
79         );
80
81         if let Status::Unstable(gate) = self.status_in_item(ccx) {
82             if !ccx.tcx.features().enabled(gate) && nightly_options::is_nightly_build() {
83                 err.help(&format!("add `#![feature({})]` to the crate attributes to enable", gate));
84             }
85         }
86
87         if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
88             err.note(
89                 "A function call isn't allowed in the const's initialization expression \
90                       because the expression's value must be known at compile-time.",
91             );
92             err.note(
93                 "Remember: you can't use a function call inside a const's initialization \
94                       expression! However, you can use it anywhere else.",
95             );
96         }
97         err.emit();
98     }
99 }
100
101 #[derive(Debug)]
102 pub struct Abort;
103 impl NonConstOp for Abort {
104     const STOPS_CONST_CHECKING: bool = true;
105
106     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
107         mcf_status_in_item(ccx)
108     }
109
110     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
111         mcf_emit_error(ccx, span, "abort is not stable in const fn")
112     }
113 }
114
115 #[derive(Debug)]
116 pub struct NonPrimitiveOp;
117 impl NonConstOp for NonPrimitiveOp {
118     const STOPS_CONST_CHECKING: bool = true;
119
120     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
121         mcf_status_in_item(ccx)
122     }
123
124     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
125         mcf_emit_error(ccx, span, "only int, `bool` and `char` operations are stable in const fn")
126     }
127 }
128
129 /// A function call where the callee is a pointer.
130 #[derive(Debug)]
131 pub struct FnCallIndirect;
132 impl NonConstOp for FnCallIndirect {
133     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
134         let mut err =
135             ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
136         err.emit();
137     }
138 }
139
140 /// A function call where the callee is not marked as `const`.
141 #[derive(Debug)]
142 pub struct FnCallNonConst(pub DefId);
143 impl NonConstOp for FnCallNonConst {
144     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
145         let mut err = struct_span_err!(
146             ccx.tcx.sess,
147             span,
148             E0015,
149             "calls in {}s are limited to constant functions, \
150              tuple structs and tuple variants",
151             ccx.const_kind(),
152         );
153         err.emit();
154     }
155 }
156
157 /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
158 ///
159 /// Contains the name of the feature that would allow the use of this function.
160 #[derive(Debug)]
161 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
162
163 impl NonConstOp for FnCallUnstable {
164     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
165         let FnCallUnstable(def_id, feature) = *self;
166
167         let mut err = ccx.tcx.sess.struct_span_err(
168             span,
169             &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
170         );
171
172         if ccx.is_const_stable_const_fn() {
173             err.help("Const-stable functions can only call other const-stable functions");
174         } else if nightly_options::is_nightly_build() {
175             if let Some(feature) = feature {
176                 err.help(&format!(
177                     "add `#![feature({})]` to the crate attributes to enable",
178                     feature
179                 ));
180             }
181         }
182         err.emit();
183     }
184 }
185
186 #[derive(Debug)]
187 pub struct FnPtrCast;
188 impl NonConstOp for FnPtrCast {
189     const STOPS_CONST_CHECKING: bool = true;
190
191     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
192         mcf_status_in_item(ccx)
193     }
194
195     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
196         mcf_emit_error(ccx, span, "function pointer casts are not allowed in const fn");
197     }
198 }
199
200 #[derive(Debug)]
201 pub struct Generator;
202 impl NonConstOp for Generator {
203     const STOPS_CONST_CHECKING: bool = true;
204
205     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
206         // FIXME: This means generator-only MIR is only forbidden in const fn. This is for
207         // compatibility with the old code. Such MIR should be forbidden everywhere.
208         mcf_status_in_item(ccx)
209     }
210
211     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
212         mcf_emit_error(ccx, span, "const fn generators are unstable");
213     }
214 }
215
216 #[derive(Debug)]
217 pub struct HeapAllocation;
218 impl NonConstOp for HeapAllocation {
219     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
220         let mut err = struct_span_err!(
221             ccx.tcx.sess,
222             span,
223             E0010,
224             "allocations are not allowed in {}s",
225             ccx.const_kind()
226         );
227         err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
228         if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
229             err.note(
230                 "The value of statics and constants must be known at compile time, \
231                  and they live for the entire lifetime of a program. Creating a boxed \
232                  value allocates memory on the heap at runtime, and therefore cannot \
233                  be done at compile time.",
234             );
235         }
236         err.emit();
237     }
238 }
239
240 #[derive(Debug)]
241 pub struct InlineAsm;
242 impl NonConstOp for InlineAsm {}
243
244 #[derive(Debug)]
245 pub struct LiveDrop {
246     pub dropped_at: Option<Span>,
247 }
248 impl NonConstOp for LiveDrop {
249     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
250         let mut diagnostic = struct_span_err!(
251             ccx.tcx.sess,
252             span,
253             E0493,
254             "destructors cannot be evaluated at compile-time"
255         );
256         diagnostic.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
257         if let Some(span) = self.dropped_at {
258             diagnostic.span_label(span, "value is dropped here");
259         }
260         diagnostic.emit();
261     }
262 }
263
264 #[derive(Debug)]
265 pub struct CellBorrow;
266 impl NonConstOp for CellBorrow {
267     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
268         struct_span_err!(
269             ccx.tcx.sess,
270             span,
271             E0492,
272             "cannot borrow a constant which may contain \
273             interior mutability, create a static instead"
274         )
275         .emit();
276     }
277 }
278
279 #[derive(Debug)]
280 pub struct MutBorrow;
281 impl NonConstOp for MutBorrow {
282     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
283         // Forbid everywhere except in const fn with a feature gate
284         if ccx.const_kind() == hir::ConstContext::ConstFn {
285             Status::Unstable(sym::const_mut_refs)
286         } else {
287             Status::Forbidden
288         }
289     }
290
291     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
292         let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn {
293             feature_err(
294                 &ccx.tcx.sess.parse_sess,
295                 sym::const_mut_refs,
296                 span,
297                 &format!("mutable references are not allowed in {}s", ccx.const_kind()),
298             )
299         } else {
300             let mut err = struct_span_err!(
301                 ccx.tcx.sess,
302                 span,
303                 E0764,
304                 "mutable references are not allowed in {}s",
305                 ccx.const_kind(),
306             );
307             err.span_label(span, format!("`&mut` is only allowed in `const fn`"));
308             err
309         };
310         if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
311             err.note(
312                 "References in statics and constants may only refer \
313                       to immutable values.\n\n\
314                       Statics are shared everywhere, and if they refer to \
315                       mutable data one might violate memory safety since \
316                       holding multiple mutable references to shared data \
317                       is not allowed.\n\n\
318                       If you really want global mutable state, try using \
319                       static mut or a global UnsafeCell.",
320             );
321         }
322         err.emit();
323     }
324 }
325
326 // FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues.
327 #[derive(Debug)]
328 pub struct MutAddressOf;
329 impl NonConstOp for MutAddressOf {
330     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
331         // Forbid everywhere except in const fn with a feature gate
332         if ccx.const_kind() == hir::ConstContext::ConstFn {
333             Status::Unstable(sym::const_mut_refs)
334         } else {
335             Status::Forbidden
336         }
337     }
338
339     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
340         feature_err(
341             &ccx.tcx.sess.parse_sess,
342             sym::const_mut_refs,
343             span,
344             &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()),
345         )
346         .emit();
347     }
348 }
349
350 #[derive(Debug)]
351 pub struct MutDeref;
352 impl NonConstOp for MutDeref {
353     fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
354         Status::Unstable(sym::const_mut_refs)
355     }
356 }
357
358 #[derive(Debug)]
359 pub struct Panic;
360 impl NonConstOp for Panic {
361     fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
362         Status::Unstable(sym::const_panic)
363     }
364
365     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
366         feature_err(
367             &ccx.tcx.sess.parse_sess,
368             sym::const_panic,
369             span,
370             &format!("panicking in {}s is unstable", ccx.const_kind()),
371         )
372         .emit();
373     }
374 }
375
376 #[derive(Debug)]
377 pub struct RawPtrComparison;
378 impl NonConstOp for RawPtrComparison {
379     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
380         let mut err = ccx
381             .tcx
382             .sess
383             .struct_span_err(span, "pointers cannot be reliably compared during const eval.");
384         err.note(
385             "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \
386             for more information",
387         );
388         err.emit();
389     }
390 }
391
392 #[derive(Debug)]
393 pub struct RawPtrDeref;
394 impl NonConstOp for RawPtrDeref {
395     fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
396         Status::Unstable(sym::const_raw_ptr_deref)
397     }
398
399     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
400         feature_err(
401             &ccx.tcx.sess.parse_sess,
402             sym::const_raw_ptr_deref,
403             span,
404             &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
405         )
406         .emit();
407     }
408 }
409
410 #[derive(Debug)]
411 pub struct RawPtrToIntCast;
412 impl NonConstOp for RawPtrToIntCast {
413     fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
414         Status::Unstable(sym::const_raw_ptr_to_usize_cast)
415     }
416
417     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
418         feature_err(
419             &ccx.tcx.sess.parse_sess,
420             sym::const_raw_ptr_to_usize_cast,
421             span,
422             &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),),
423         )
424         .emit();
425     }
426 }
427
428 /// An access to a (non-thread-local) `static`.
429 #[derive(Debug)]
430 pub struct StaticAccess;
431 impl NonConstOp for StaticAccess {
432     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
433         if let hir::ConstContext::Static(_) = ccx.const_kind() {
434             Status::Allowed
435         } else {
436             Status::Forbidden
437         }
438     }
439
440     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
441         let mut err = struct_span_err!(
442             ccx.tcx.sess,
443             span,
444             E0013,
445             "{}s cannot refer to statics",
446             ccx.const_kind()
447         );
448         err.help(
449             "consider extracting the value of the `static` to a `const`, and referring to that",
450         );
451         if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
452             err.note(
453                 "`static` and `const` variables can refer to other `const` variables. \
454                     A `const` variable, however, cannot refer to a `static` variable.",
455             );
456             err.help("To fix this, the value can be extracted to a `const` and then used.");
457         }
458         err.emit();
459     }
460 }
461
462 /// An access to a thread-local `static`.
463 #[derive(Debug)]
464 pub struct ThreadLocalAccess;
465 impl NonConstOp for ThreadLocalAccess {
466     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
467         struct_span_err!(
468             ccx.tcx.sess,
469             span,
470             E0625,
471             "thread-local statics cannot be \
472             accessed at compile-time"
473         )
474         .emit();
475     }
476 }
477
478 #[derive(Debug)]
479 pub struct Transmute;
480 impl NonConstOp for Transmute {
481     const STOPS_CONST_CHECKING: bool = true;
482
483     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
484         if ccx.const_kind() != hir::ConstContext::ConstFn {
485             Status::Allowed
486         } else {
487             Status::Unstable(sym::const_fn_transmute)
488         }
489     }
490
491     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
492         feature_err(
493             &ccx.tcx.sess.parse_sess,
494             sym::const_fn_transmute,
495             span,
496             &format!("`transmute` is not allowed in {}s", ccx.const_kind()),
497         )
498         .note("`transmute` is only allowed in constants and statics for now")
499         .emit();
500     }
501 }
502
503 #[derive(Debug)]
504 pub struct UnionAccess;
505 impl NonConstOp for UnionAccess {
506     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
507         // Union accesses are stable in all contexts except `const fn`.
508         if ccx.const_kind() != hir::ConstContext::ConstFn {
509             Status::Allowed
510         } else {
511             Status::Unstable(sym::const_fn_union)
512         }
513     }
514
515     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
516         feature_err(
517             &ccx.tcx.sess.parse_sess,
518             sym::const_fn_union,
519             span,
520             "unions in const fn are unstable",
521         )
522         .emit();
523     }
524 }
525
526 /// See [#64992].
527 ///
528 /// [#64992]: https://github.com/rust-lang/rust/issues/64992
529 #[derive(Debug)]
530 pub struct UnsizingCast;
531 impl NonConstOp for UnsizingCast {
532     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
533         mcf_status_in_item(ccx)
534     }
535
536     fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
537         mcf_emit_error(
538             ccx,
539             span,
540             "unsizing casts to types besides slices are not allowed in const fn",
541         );
542     }
543 }
544
545 // Types that cannot appear in the signature or locals of a `const fn`.
546 pub mod ty {
547     use super::*;
548
549     #[derive(Debug)]
550     pub struct MutRef;
551     impl NonConstOp for MutRef {
552         const STOPS_CONST_CHECKING: bool = true;
553
554         fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
555             Status::Unstable(sym::const_mut_refs)
556         }
557
558         fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
559             feature_err(
560                 &ccx.tcx.sess.parse_sess,
561                 sym::const_mut_refs,
562                 span,
563                 &format!("mutable references are not allowed in {}s", ccx.const_kind()),
564             )
565             .emit()
566         }
567     }
568
569     #[derive(Debug)]
570     pub struct FnPtr;
571     impl NonConstOp for FnPtr {
572         const STOPS_CONST_CHECKING: bool = true;
573
574         fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
575             // FIXME: This attribute a hack to allow the specialization of the `futures` API. See
576             // #59739. We should have a proper feature gate for this.
577             if ccx.tcx.has_attr(ccx.def_id.to_def_id(), sym::rustc_allow_const_fn_ptr) {
578                 Status::Allowed
579             } else {
580                 mcf_status_in_item(ccx)
581             }
582         }
583
584         fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
585             mcf_emit_error(ccx, span, "function pointers in const fn are unstable");
586         }
587     }
588
589     #[derive(Debug)]
590     pub struct ImplTrait;
591     impl NonConstOp for ImplTrait {
592         const STOPS_CONST_CHECKING: bool = true;
593
594         fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
595             mcf_status_in_item(ccx)
596         }
597
598         fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
599             mcf_emit_error(ccx, span, "`impl Trait` in const fn is unstable");
600         }
601     }
602
603     #[derive(Debug)]
604     pub struct TraitBound;
605     impl NonConstOp for TraitBound {
606         const STOPS_CONST_CHECKING: bool = true;
607
608         fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
609             mcf_status_in_item(ccx)
610         }
611
612         fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
613             mcf_emit_error(
614                 ccx,
615                 span,
616                 "trait bounds other than `Sized` on const fn parameters are unstable",
617             );
618         }
619     }
620
621     /// A trait bound with the `?const Trait` opt-out
622     #[derive(Debug)]
623     pub struct TraitBoundNotConst;
624     impl NonConstOp for TraitBoundNotConst {
625         const STOPS_CONST_CHECKING: bool = true;
626
627         fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
628             Status::Unstable(sym::const_trait_bound_opt_out)
629         }
630
631         fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
632             feature_err(
633                 &ccx.tcx.sess.parse_sess,
634                 sym::const_trait_bound_opt_out,
635                 span,
636                 "`?const Trait` syntax is unstable",
637             )
638             .emit()
639         }
640     }
641 }
642
643 fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
644     if ccx.const_kind() != hir::ConstContext::ConstFn {
645         Status::Allowed
646     } else {
647         Status::Unstable(sym::const_fn)
648     }
649 }
650
651 fn mcf_emit_error(ccx: &ConstCx<'_, '_>, span: Span, msg: &str) {
652     struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg)
653         .note(
654             "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
655              for more information",
656         )
657         .help("add `#![feature(const_fn)]` to the crate attributes to enable")
658         .emit();
659 }