]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_consts/validation.rs
Correct list of miri-supported operations
[rust.git] / src / librustc_mir / transform / check_consts / validation.rs
1 use rustc::hir::{self, def_id::DefId};
2 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
3 use rustc::mir::*;
4 use rustc::session::config::nightly_options;
5 use rustc::ty::cast::CastTy;
6 use rustc::ty::{self, TyCtxt};
7 use rustc_data_structures::bit_set::BitSet;
8 use rustc_target::spec::abi::Abi;
9 use syntax::feature_gate::{emit_feature_err, GateIssue};
10 use syntax::symbol::sym;
11 use syntax_pos::{Span, Symbol};
12
13 use std::cell::RefCell;
14 use std::fmt;
15 use std::ops::Deref;
16 use std::rc::Rc;
17
18 use crate::dataflow as old_dataflow;
19 use super::{Item, Qualif, is_lang_panic_fn};
20 use super::resolver::{QualifResolver, FlowSensitiveResolver};
21 use super::qualifs::{HasMutInterior, NeedsDrop};
22
23 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
24 pub enum CheckOpResult {
25     Forbidden,
26     Unleashed,
27     Allowed,
28 }
29
30 /// What kind of item we are in.
31 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
32 pub enum Mode {
33     /// A `static` item.
34     Static,
35     /// A `static mut` item.
36     StaticMut,
37     /// A `const fn` item.
38     ConstFn,
39     /// A `const` item or an anonymous constant (e.g. in array lengths).
40     Const,
41 }
42
43 impl Mode {
44     /// Returns the validation mode for the item with the given `DefId`, or `None` if this item
45     /// does not require validation (e.g. a non-const `fn`).
46     pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Self> {
47         use hir::BodyOwnerKind as HirKind;
48
49         let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
50
51         let mode = match tcx.hir().body_owner_kind(hir_id) {
52             HirKind::Closure => return None,
53
54             HirKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
55             HirKind::Fn => return None,
56
57             HirKind::Const => Mode::Const,
58
59             HirKind::Static(hir::MutImmutable) => Mode::Static,
60             HirKind::Static(hir::MutMutable) => Mode::StaticMut,
61         };
62
63         Some(mode)
64     }
65
66     pub fn is_static(self) -> bool {
67         match self {
68             Mode::Static | Mode::StaticMut => true,
69             Mode::ConstFn | Mode::Const => false,
70         }
71     }
72
73     /// Returns `true` if the value returned by this item must be `Sync`.
74     ///
75     /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway.
76     pub fn requires_sync(self) -> bool {
77         match self {
78             Mode::Static => true,
79             Mode::ConstFn | Mode::Const |  Mode::StaticMut => false,
80         }
81     }
82 }
83
84 impl fmt::Display for Mode {
85     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86         match *self {
87             Mode::Const => write!(f, "constant"),
88             Mode::Static | Mode::StaticMut => write!(f, "static"),
89             Mode::ConstFn => write!(f, "constant function"),
90         }
91     }
92 }
93
94 /// An operation that is not *always* allowed in a const context.
95 pub trait NonConstOp {
96     /// Whether this operation can be evaluated by miri.
97     const IS_SUPPORTED_IN_MIRI: bool = true;
98
99     /// Returns a boolean indicating whether the feature gate that would allow this operation is
100     /// enabled, or `None` if such a feature gate does not exist.
101     fn feature_gate(_tcx: TyCtxt<'tcx>) -> Option<bool> {
102         None
103     }
104
105     /// Returns `true` if this operation is allowed in the given item.
106     ///
107     /// This check should assume that we are not in a non-const `fn`, where all operations are
108     /// legal.
109     fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
110         Self::feature_gate(item.tcx).unwrap_or(false)
111     }
112
113     fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
114         let mut err = struct_span_err!(
115             item.tcx.sess,
116             span,
117             E0019,
118             "{} contains unimplemented expression type",
119             item.mode
120         );
121         if item.tcx.sess.teach(&err.get_code().unwrap()) {
122             err.note("A function call isn't allowed in the const's initialization expression \
123                       because the expression's value must be known at compile-time.");
124             err.note("Remember: you can't use a function call inside a const's initialization \
125                       expression! However, you can use it anywhere else.");
126         }
127         err.emit();
128     }
129 }
130
131 pub struct Qualifs<'a, 'mir, 'tcx> {
132     has_mut_interior: FlowSensitiveResolver<'a, 'mir, 'tcx, HasMutInterior>,
133     needs_drop: FlowSensitiveResolver<'a, 'mir, 'tcx, NeedsDrop>,
134 }
135
136 pub struct Validator<'a, 'mir, 'tcx> {
137     item: &'a Item<'mir, 'tcx>,
138     qualifs: Qualifs<'a, 'mir, 'tcx>,
139
140     /// The span of the current statement.
141     span: Span,
142
143     /// True if the local was assigned the result of an illegal borrow (`ops::MutBorrow`).
144     ///
145     /// This is used to hide errors from {re,}borrowing the newly-assigned local, instead pointing
146     /// the user to the place where the illegal borrow occurred. This set is only populated once an
147     /// error has been emitted, so it will never cause an erroneous `mir::Body` to pass validation.
148     ///
149     /// FIXME(ecstaticmorse): assert at the end of checking that if `tcx.has_errors() == false`,
150     /// this set is empty. Note that if we start removing locals from
151     /// `derived_from_illegal_borrow`, just checking at the end won't be enough.
152     derived_from_illegal_borrow: BitSet<Local>,
153
154     errors: Vec<(Span, String)>,
155
156     /// Whether to actually emit errors or just store them in `errors`.
157     pub(crate) suppress_errors: bool,
158 }
159
160 impl Deref for Validator<'_, 'mir, 'tcx> {
161     type Target = Item<'mir, 'tcx>;
162
163     fn deref(&self) -> &Self::Target {
164         &self.item
165     }
166 }
167
168 impl Validator<'a, 'mir, 'tcx> {
169     pub fn new(item: &'a Item<'mir, 'tcx>) -> Self {
170         let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
171
172         let indirectly_mutable_locals = old_dataflow::do_dataflow(
173             item.tcx,
174             item.body,
175             item.def_id,
176             &[],
177             &dead_unwinds,
178             old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
179             |_, local| old_dataflow::DebugFormatted::new(&local),
180         );
181
182         let indirectly_mutable_locals = old_dataflow::DataflowResultsCursor::new(
183             indirectly_mutable_locals,
184             item.body,
185         );
186         let indirectly_mutable_locals = Rc::new(RefCell::new(indirectly_mutable_locals));
187
188         let needs_drop = FlowSensitiveResolver::new(
189             NeedsDrop,
190             item,
191             indirectly_mutable_locals.clone(),
192             &dead_unwinds,
193         );
194
195         let has_mut_interior = FlowSensitiveResolver::new(
196             HasMutInterior,
197             item,
198             indirectly_mutable_locals.clone(),
199             &dead_unwinds,
200         );
201
202         let qualifs = Qualifs {
203             needs_drop,
204             has_mut_interior,
205         };
206
207         Validator {
208             span: item.body.span,
209             item,
210             qualifs,
211             errors: vec![],
212             derived_from_illegal_borrow: BitSet::new_empty(item.body.local_decls.len()),
213             suppress_errors: false,
214         }
215     }
216
217     /// Resets the `QualifResolver`s used by this `Validator` and returns them so they can be
218     /// reused.
219     pub fn into_qualifs(mut self) -> Qualifs<'a, 'mir, 'tcx> {
220         self.qualifs.needs_drop.reset();
221         self.qualifs.has_mut_interior.reset();
222         self.qualifs
223     }
224
225     pub fn take_errors(&mut self) -> Vec<(Span, String)> {
226         std::mem::replace(&mut self.errors, vec![])
227     }
228
229     /// Emits an error at the given `span` if an expression cannot be evaluated in the current
230     /// context. Returns `Forbidden` if an error was emitted.
231     pub fn check_op_spanned<O>(&mut self, op: O, span: Span) -> CheckOpResult
232     where
233         O: NonConstOp + fmt::Debug
234     {
235         trace!("check_op: op={:?}", op);
236
237         if op.is_allowed_in_item(self) {
238             return CheckOpResult::Allowed;
239         }
240
241         // If an operation is supported in miri (and is not already controlled by a feature gate) it
242         // can be turned on with `-Zunleash-the-miri-inside-of-you`.
243         let is_unleashable = O::IS_SUPPORTED_IN_MIRI
244             && O::feature_gate(self.tcx).is_none();
245
246         if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
247             self.tcx.sess.span_warn(span, "skipping const checks");
248             return CheckOpResult::Unleashed;
249         }
250
251         if !self.suppress_errors {
252             op.emit_error(self, span);
253         }
254
255         self.errors.push((span, format!("{:?}", op)));
256         CheckOpResult::Forbidden
257     }
258
259     /// Emits an error if an expression cannot be evaluated in the current context.
260     pub fn check_op(&mut self, op: impl NonConstOp + fmt::Debug) -> CheckOpResult {
261         let span = self.span;
262         self.check_op_spanned(op, span)
263     }
264 }
265
266 impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
267     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
268         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
269
270         // Check nested operands and places.
271         if let Rvalue::Ref(_, kind, ref place) = *rvalue {
272             // Special-case reborrows to be more like a copy of a reference.
273             let mut reborrow_place = None;
274             if let box [proj_base @ .., elem] = &place.projection {
275                 if *elem == ProjectionElem::Deref {
276                     let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
277                     if let ty::Ref(..) = base_ty.sty {
278                         reborrow_place = Some(proj_base);
279                     }
280                 }
281             }
282
283             if let Some(proj) = reborrow_place {
284                 let ctx = match kind {
285                     BorrowKind::Shared => PlaceContext::NonMutatingUse(
286                         NonMutatingUseContext::SharedBorrow,
287                     ),
288                     BorrowKind::Shallow => PlaceContext::NonMutatingUse(
289                         NonMutatingUseContext::ShallowBorrow,
290                     ),
291                     BorrowKind::Unique => PlaceContext::NonMutatingUse(
292                         NonMutatingUseContext::UniqueBorrow,
293                     ),
294                     BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
295                         MutatingUseContext::Borrow,
296                     ),
297                 };
298                 self.visit_place_base(&place.base, ctx, location);
299                 self.visit_projection(&place.base, proj, ctx, location);
300             } else {
301                 self.super_rvalue(rvalue, location);
302             }
303         } else {
304             self.super_rvalue(rvalue, location);
305         }
306
307         match *rvalue {
308             Rvalue::Use(_) |
309             Rvalue::Repeat(..) |
310             Rvalue::UnaryOp(UnOp::Neg, _) |
311             Rvalue::UnaryOp(UnOp::Not, _) |
312             Rvalue::NullaryOp(NullOp::SizeOf, _) |
313             Rvalue::CheckedBinaryOp(..) |
314             Rvalue::Cast(CastKind::Pointer(_), ..) |
315             Rvalue::Discriminant(..) |
316             Rvalue::Len(_) |
317             Rvalue::Ref(..) |
318             Rvalue::Aggregate(..) => {}
319
320             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
321                 let operand_ty = operand.ty(self.body, self.tcx);
322                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
323                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
324
325                 if let (CastTy::Ptr(_), CastTy::Int(_))
326                      | (CastTy::FnPtr,  CastTy::Int(_)) = (cast_in, cast_out) {
327                     self.check_op(ops::RawPtrToIntCast);
328                 }
329             }
330
331             Rvalue::BinaryOp(op, ref lhs, _) => {
332                 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).sty {
333                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
334                             op == BinOp::Le || op == BinOp::Lt ||
335                             op == BinOp::Ge || op == BinOp::Gt ||
336                             op == BinOp::Offset);
337
338
339                     self.check_op(ops::RawPtrComparison);
340                 }
341             }
342
343             Rvalue::NullaryOp(NullOp::Box, _) => {
344                 self.check_op(ops::HeapAllocation);
345             }
346         }
347     }
348
349     fn visit_place_base(
350         &mut self,
351         place_base: &PlaceBase<'tcx>,
352         context: PlaceContext,
353         location: Location,
354     ) {
355         trace!(
356             "visit_place_base: place_base={:?} context={:?} location={:?}",
357             place_base,
358             context,
359             location,
360         );
361         self.super_place_base(place_base, context, location);
362
363         match place_base {
364             PlaceBase::Local(_) => {}
365             PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
366                 bug!("Promotion must be run after const validation");
367             }
368
369             PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
370                 let is_thread_local = self.tcx.has_attr(*def_id, sym::thread_local);
371                 if is_thread_local {
372                     self.check_op(ops::ThreadLocalAccess);
373                 } else if self.mode == Mode::Static && context.is_mutating_use() {
374                     // this is not strictly necessary as miri will also bail out
375                     // For interior mutability we can't really catch this statically as that
376                     // goes through raw pointers and intermediate temporaries, so miri has
377                     // to catch this anyway
378
379                     self.tcx.sess.span_err(
380                         self.span,
381                         "cannot mutate statics in the initializer of another static",
382                     );
383                 } else {
384                     self.check_op(ops::StaticAccess);
385                 }
386             }
387         }
388     }
389
390     fn visit_assign(&mut self, dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
391         trace!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
392
393         // Error on mutable borrows or shared borrows of values with interior mutability.
394         //
395         // This replicates the logic at the start of `assign` in the old const checker.  Note that
396         // it depends on `HasMutInterior` being set for mutable borrows as well as values with
397         // interior mutability.
398         if let Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
399             let rvalue_has_mut_interior = HasMutInterior::in_rvalue(
400                 &self.item,
401                 self.qualifs.has_mut_interior.get(),
402                 rvalue,
403             );
404
405             if rvalue_has_mut_interior {
406                 let is_derived_from_illegal_borrow = match *borrowed_place {
407                     // If an unprojected local was borrowed and its value was the result of an
408                     // illegal borrow, suppress this error and mark the result of this borrow as
409                     // illegal as well.
410                     Place { base: PlaceBase::Local(borrowed_local), projection: box [] }
411                         if self.derived_from_illegal_borrow.contains(borrowed_local) => true,
412
413                     // Otherwise proceed normally: check the legality of a mutable borrow in this
414                     // context.
415                     _ => self.check_op(ops::MutBorrow(kind)) == CheckOpResult::Forbidden,
416                 };
417
418                 // When the target of the assignment is a local with no projections, mark it as
419                 // derived from an illegal borrow if necessary.
420                 //
421                 // FIXME: should we also clear `derived_from_illegal_borrow` when a local is
422                 // assigned a new value?
423                 if is_derived_from_illegal_borrow {
424                     if let Place { base: PlaceBase::Local(dest), projection: box [] } = *dest {
425                         self.derived_from_illegal_borrow.insert(dest);
426                     }
427                 }
428             }
429         }
430
431         self.super_assign(dest, rvalue, location);
432     }
433
434     fn visit_projection(
435         &mut self,
436         place_base: &PlaceBase<'tcx>,
437         proj: &[PlaceElem<'tcx>],
438         context: PlaceContext,
439         location: Location,
440     ) {
441         trace!(
442             "visit_place_projection: proj={:?} context={:?} location={:?}",
443             proj,
444             context,
445             location,
446         );
447         self.super_projection(place_base, proj, context, location);
448
449         let (elem, proj_base) = match proj.split_last() {
450             Some(x) => x,
451             None => return,
452         };
453
454         match elem {
455             ProjectionElem::Deref => {
456                 if context.is_mutating_use() {
457                     self.check_op(ops::MutDeref);
458                 }
459
460                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
461                 if let ty::RawPtr(_) = base_ty.sty {
462                     self.check_op(ops::RawPtrDeref);
463                 }
464             }
465
466             ProjectionElem::ConstantIndex {..} |
467             ProjectionElem::Subslice {..} |
468             ProjectionElem::Field(..) |
469             ProjectionElem::Index(_) => {
470                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
471                 match base_ty.ty_adt_def() {
472                     Some(def) if def.is_union() => {
473                         self.check_op(ops::UnionAccess);
474                     }
475
476                     _ => {}
477                 }
478             }
479
480             ProjectionElem::Downcast(..) => {
481                 self.check_op(ops::Downcast);
482             }
483         }
484     }
485
486
487     fn visit_source_info(&mut self, source_info: &SourceInfo) {
488         trace!("visit_source_info: source_info={:?}", source_info);
489         self.span = source_info.span;
490     }
491
492     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
493         trace!("visit_statement: statement={:?} location={:?}", statement, location);
494
495         self.qualifs.needs_drop.visit_statement(statement, location);
496         self.qualifs.has_mut_interior.visit_statement(statement, location);
497         debug!("needs_drop: {:?}", self.qualifs.needs_drop.get());
498         debug!("has_mut_interior: {:?}", self.qualifs.has_mut_interior.get());
499
500         match statement.kind {
501             StatementKind::Assign(..) => {
502                 self.super_statement(statement, location);
503             }
504             StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
505                 self.check_op(ops::IfOrMatch);
506             }
507             // FIXME(eddyb) should these really do nothing?
508             StatementKind::FakeRead(..) |
509             StatementKind::SetDiscriminant { .. } |
510             StatementKind::StorageLive(_) |
511             StatementKind::StorageDead(_) |
512             StatementKind::InlineAsm {..} |
513             StatementKind::Retag { .. } |
514             StatementKind::AscribeUserType(..) |
515             StatementKind::Nop => {}
516         }
517     }
518
519     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
520         trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
521
522         self.qualifs.needs_drop.visit_terminator(terminator, location);
523         self.qualifs.has_mut_interior.visit_terminator(terminator, location);
524         debug!("needs_drop: {:?}", self.qualifs.needs_drop.get());
525         debug!("has_mut_interior: {:?}", self.qualifs.has_mut_interior.get());
526
527         self.super_terminator(terminator, location);
528     }
529
530     fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) {
531         trace!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
532         self.super_terminator_kind(kind, location);
533
534         match kind {
535             TerminatorKind::Call { func, .. } => {
536                 let fn_ty = func.ty(self.body, self.tcx);
537
538                 let def_id = match fn_ty.sty {
539                     ty::FnDef(def_id, _) => def_id,
540
541                     ty::FnPtr(_) => {
542                         self.check_op(ops::FnCallIndirect);
543                         return;
544                     }
545                     _ => {
546                         self.check_op(ops::FnCallOther);
547                         return;
548                     }
549                 };
550
551                 // At this point, we are calling a function whose `DefId` is known...
552
553                 if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = self.tcx.fn_sig(def_id).abi() {
554                     assert!(!self.tcx.is_const_fn(def_id));
555
556                     if self.tcx.item_name(def_id) == sym::transmute {
557                         self.check_op(ops::Transmute);
558                         return;
559                     }
560
561                     // To preserve the current semantics, we return early, allowing all
562                     // intrinsics (except `transmute`) to pass unchecked to miri.
563                     //
564                     // FIXME: We should keep a whitelist of allowed intrinsics (or at least a
565                     // blacklist of unimplemented ones) and fail here instead.
566                     return;
567                 }
568
569                 if self.tcx.is_const_fn(def_id) {
570                     return;
571                 }
572
573                 if is_lang_panic_fn(self.tcx, def_id) {
574                     self.check_op(ops::Panic);
575                 } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
576                     // Exempt unstable const fns inside of macros with
577                     // `#[allow_internal_unstable]`.
578                     if !self.span.allows_unstable(feature) {
579                         self.check_op(ops::FnCallUnstable(def_id, feature));
580                     }
581                 } else {
582                     self.check_op(ops::FnCallNonConst(def_id));
583                 }
584
585             }
586
587             // Forbid all `Drop` terminators unless the place being dropped is a local with no
588             // projections that cannot be `NeedsDrop`.
589             | TerminatorKind::Drop { location: dropped_place, .. }
590             | TerminatorKind::DropAndReplace { location: dropped_place, .. }
591             => {
592                 let mut err_span = self.span;
593
594                 // Check to see if the type of this place can ever have a drop impl. If not, this
595                 // `Drop` terminator is frivolous.
596                 let ty_needs_drop = dropped_place
597                     .ty(self.body, self.tcx)
598                     .ty
599                     .needs_drop(self.tcx, self.param_env);
600
601                 if !ty_needs_drop {
602                     return;
603                 }
604
605                 let needs_drop = if let Place {
606                     base: PlaceBase::Local(local),
607                     projection: box [],
608                 } = *dropped_place {
609                     // Use the span where the local was declared as the span of the drop error.
610                     err_span = self.body.local_decls[local].source_info.span;
611                     self.qualifs.needs_drop.contains(local)
612                 } else {
613                     true
614                 };
615
616                 if needs_drop {
617                     self.check_op_spanned(ops::LiveDrop, err_span);
618                 }
619             }
620
621             _ => {}
622         }
623     }
624 }
625
626 /// All implementers of `NonConstOp`.
627 pub mod ops {
628     use super::*;
629
630     /// A `Downcast` projection.
631     #[derive(Debug)]
632     pub struct Downcast;
633     impl NonConstOp for Downcast {}
634
635     /// A function call where the callee is a pointer.
636     #[derive(Debug)]
637     pub struct FnCallIndirect;
638     impl NonConstOp for FnCallIndirect {
639         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
640             let mut err = item.tcx.sess.struct_span_err(
641                 span,
642                 &format!("function pointers are not allowed in const fn"));
643             err.emit();
644         }
645     }
646
647     /// A function call where the callee is not marked as `const`.
648     #[derive(Debug)]
649     pub struct FnCallNonConst(pub DefId);
650     impl NonConstOp for FnCallNonConst {
651         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
652             let mut err = struct_span_err!(
653                 item.tcx.sess,
654                 span,
655                 E0015,
656                 "calls in {}s are limited to constant functions, \
657                  tuple structs and tuple variants",
658                 item.mode,
659             );
660             err.emit();
661         }
662     }
663
664     /// A function call where the callee is not a function definition or function pointer, e.g. a
665     /// closure.
666     ///
667     /// This can be subdivided in the future to produce a better error message.
668     #[derive(Debug)]
669     pub struct FnCallOther;
670     impl NonConstOp for FnCallOther {
671         const IS_SUPPORTED_IN_MIRI: bool = false;
672     }
673
674     /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
675     ///
676     /// Contains the name of the feature that would allow the use of this function.
677     #[derive(Debug)]
678     pub struct FnCallUnstable(pub DefId, pub Symbol);
679     impl NonConstOp for FnCallUnstable {
680         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
681             let FnCallUnstable(def_id, feature) = *self;
682
683             let mut err = item.tcx.sess.struct_span_err(span,
684                 &format!("`{}` is not yet stable as a const fn",
685                         item.tcx.def_path_str(def_id)));
686             if nightly_options::is_nightly_build() {
687                 help!(&mut err,
688                       "add `#![feature({})]` to the \
689                        crate attributes to enable",
690                       feature);
691             }
692             err.emit();
693         }
694     }
695
696     #[derive(Debug)]
697     pub struct HeapAllocation;
698     impl NonConstOp for HeapAllocation {
699         const IS_SUPPORTED_IN_MIRI: bool = false;
700
701         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
702             let mut err = struct_span_err!(item.tcx.sess, span, E0010,
703                                            "allocations are not allowed in {}s", item.mode);
704             err.span_label(span, format!("allocation not allowed in {}s", item.mode));
705             if item.tcx.sess.teach(&err.get_code().unwrap()) {
706                 err.note(
707                     "The value of statics and constants must be known at compile time, \
708                      and they live for the entire lifetime of a program. Creating a boxed \
709                      value allocates memory on the heap at runtime, and therefore cannot \
710                      be done at compile time."
711                 );
712             }
713             err.emit();
714         }
715     }
716
717     #[derive(Debug)]
718     pub struct IfOrMatch;
719     impl NonConstOp for IfOrMatch {}
720
721     #[derive(Debug)]
722     pub struct LiveDrop;
723     impl NonConstOp for LiveDrop {
724         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
725             struct_span_err!(item.tcx.sess, span, E0493,
726                              "destructors cannot be evaluated at compile-time")
727                 .span_label(span, format!("{}s cannot evaluate destructors",
728                                           item.mode))
729                 .emit();
730         }
731     }
732
733     #[derive(Debug)]
734     pub struct Loop;
735     impl NonConstOp for Loop {}
736
737     #[derive(Debug)]
738     pub struct MutBorrow(pub BorrowKind);
739     impl NonConstOp for MutBorrow {
740         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
741             let kind = self.0;
742             if let BorrowKind::Mut { .. } = kind {
743                 let mut err = struct_span_err!(item.tcx.sess, span, E0017,
744                                                "references in {}s may only refer \
745                                                 to immutable values", item.mode);
746                 err.span_label(span, format!("{}s require immutable values",
747                                                     item.mode));
748                 if item.tcx.sess.teach(&err.get_code().unwrap()) {
749                     err.note("References in statics and constants may only refer \
750                               to immutable values.\n\n\
751                               Statics are shared everywhere, and if they refer to \
752                               mutable data one might violate memory safety since \
753                               holding multiple mutable references to shared data \
754                               is not allowed.\n\n\
755                               If you really want global mutable state, try using \
756                               static mut or a global UnsafeCell.");
757                 }
758                 err.emit();
759             } else {
760                 span_err!(item.tcx.sess, span, E0492,
761                           "cannot borrow a constant which may contain \
762                            interior mutability, create a static instead");
763             }
764         }
765     }
766
767     #[derive(Debug)]
768     pub struct MutDeref;
769     impl NonConstOp for MutDeref {}
770
771     #[derive(Debug)]
772     pub struct Panic;
773     impl NonConstOp for Panic {
774         fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
775             Some(tcx.features().const_panic)
776         }
777
778         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
779             emit_feature_err(
780                 &item.tcx.sess.parse_sess,
781                 sym::const_panic,
782                 span,
783                 GateIssue::Language,
784                 &format!("panicking in {}s is unstable", item.mode),
785             );
786         }
787     }
788
789     #[derive(Debug)]
790     pub struct RawPtrComparison;
791     impl NonConstOp for RawPtrComparison {
792         fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
793             Some(tcx.features().const_compare_raw_pointers)
794         }
795
796         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
797             emit_feature_err(
798                 &item.tcx.sess.parse_sess,
799                 sym::const_compare_raw_pointers,
800                 span,
801                 GateIssue::Language,
802                 &format!("comparing raw pointers inside {}", item.mode),
803             );
804         }
805     }
806
807     #[derive(Debug)]
808     pub struct RawPtrDeref;
809     impl NonConstOp for RawPtrDeref {
810         fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
811             Some(tcx.features().const_raw_ptr_deref)
812         }
813
814         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
815             emit_feature_err(
816                 &item.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
817                 span, GateIssue::Language,
818                 &format!(
819                     "dereferencing raw pointers in {}s is unstable",
820                     item.mode,
821                 ),
822             );
823         }
824     }
825
826     #[derive(Debug)]
827     pub struct RawPtrToIntCast;
828     impl NonConstOp for RawPtrToIntCast {
829         fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
830             Some(tcx.features().const_raw_ptr_to_usize_cast)
831         }
832
833         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
834             emit_feature_err(
835                 &item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
836                 span, GateIssue::Language,
837                 &format!(
838                     "casting pointers to integers in {}s is unstable",
839                     item.mode,
840                 ),
841             );
842         }
843     }
844
845     /// An access to a (non-thread-local) `static`.
846     #[derive(Debug)]
847     pub struct StaticAccess;
848     impl NonConstOp for StaticAccess {
849         fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
850             item.mode.is_static()
851         }
852
853         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
854             let mut err = struct_span_err!(item.tcx.sess, span, E0013,
855                                             "{}s cannot refer to statics, use \
856                                             a constant instead", item.mode);
857             if item.tcx.sess.teach(&err.get_code().unwrap()) {
858                 err.note(
859                     "Static and const variables can refer to other const variables. \
860                         But a const variable cannot refer to a static variable."
861                 );
862                 err.help(
863                     "To fix this, the value can be extracted as a const and then used."
864                 );
865             }
866             err.emit();
867         }
868     }
869
870     /// An access to a thread-local `static`.
871     #[derive(Debug)]
872     pub struct ThreadLocalAccess;
873     impl NonConstOp for ThreadLocalAccess {
874         const IS_SUPPORTED_IN_MIRI: bool = false;
875
876         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
877             span_err!(item.tcx.sess, span, E0625,
878                 "thread-local statics cannot be \
879                 accessed at compile-time");
880         }
881     }
882
883     #[derive(Debug)]
884     pub struct Transmute;
885     impl NonConstOp for Transmute {
886         fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
887             Some(tcx.features().const_transmute)
888         }
889
890         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
891             emit_feature_err(
892                 &item.tcx.sess.parse_sess, sym::const_transmute,
893                 span, GateIssue::Language,
894                 &format!("The use of std::mem::transmute() \
895                 is gated in {}s", item.mode));
896         }
897     }
898
899     #[derive(Debug)]
900     pub struct UnionAccess;
901     impl NonConstOp for UnionAccess {
902         fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
903             // Union accesses are stable in all contexts except `const fn`.
904             item.mode != Mode::ConstFn || Self::feature_gate(item.tcx).unwrap()
905         }
906
907         fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
908             Some(tcx.features().const_fn_union)
909         }
910
911         fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
912             emit_feature_err(
913                 &item.tcx.sess.parse_sess, sym::const_fn_union,
914                 span, GateIssue::Language,
915                 "unions in const fn are unstable",
916             );
917         }
918     }
919 }