1 use rustc::hir::{self, def_id::DefId};
2 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
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};
13 use std::cell::RefCell;
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};
23 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
24 pub enum CheckOpResult {
30 /// What kind of item we are in.
31 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
35 /// A `static mut` item.
37 /// A `const fn` item.
39 /// A `const` item or an anonymous constant (e.g. in array lengths).
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;
49 let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
51 let mode = match tcx.hir().body_owner_kind(hir_id) {
52 HirKind::Closure => return None,
54 HirKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
55 HirKind::Fn => return None,
57 HirKind::Const => Mode::Const,
59 HirKind::Static(hir::MutImmutable) => Mode::Static,
60 HirKind::Static(hir::MutMutable) => Mode::StaticMut,
66 pub fn is_static(self) -> bool {
68 Mode::Static | Mode::StaticMut => true,
69 Mode::ConstFn | Mode::Const => false,
73 /// Returns `true` if the value returned by this item must be `Sync`.
75 /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway.
76 pub fn requires_sync(self) -> bool {
79 Mode::ConstFn | Mode::Const | Mode::StaticMut => false,
84 impl fmt::Display for Mode {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 Mode::Const => write!(f, "constant"),
88 Mode::Static | Mode::StaticMut => write!(f, "static"),
89 Mode::ConstFn => write!(f, "constant function"),
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;
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> {
105 /// Returns `true` if this operation is allowed in the given item.
107 /// This check should assume that we are not in a non-const `fn`, where all operations are
109 fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
110 Self::feature_gate(item.tcx).unwrap_or(false)
113 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
114 let mut err = struct_span_err!(
118 "{} contains unimplemented expression type",
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.");
131 pub struct Qualifs<'a, 'mir, 'tcx> {
132 has_mut_interior: FlowSensitiveResolver<'a, 'mir, 'tcx, HasMutInterior>,
133 needs_drop: FlowSensitiveResolver<'a, 'mir, 'tcx, NeedsDrop>,
136 pub struct Validator<'a, 'mir, 'tcx> {
137 item: &'a Item<'mir, 'tcx>,
138 qualifs: Qualifs<'a, 'mir, 'tcx>,
140 /// The span of the current statement.
143 /// True if the local was assigned the result of an illegal borrow (`ops::MutBorrow`).
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.
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>,
154 errors: Vec<(Span, String)>,
156 /// Whether to actually emit errors or just store them in `errors`.
157 pub(crate) suppress_errors: bool,
160 impl Deref for Validator<'_, 'mir, 'tcx> {
161 type Target = Item<'mir, 'tcx>;
163 fn deref(&self) -> &Self::Target {
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());
172 let indirectly_mutable_locals = old_dataflow::do_dataflow(
178 old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
179 |_, local| old_dataflow::DebugFormatted::new(&local),
182 let indirectly_mutable_locals = old_dataflow::DataflowResultsCursor::new(
183 indirectly_mutable_locals,
186 let indirectly_mutable_locals = Rc::new(RefCell::new(indirectly_mutable_locals));
188 let needs_drop = FlowSensitiveResolver::new(
191 indirectly_mutable_locals.clone(),
195 let has_mut_interior = FlowSensitiveResolver::new(
198 indirectly_mutable_locals.clone(),
202 let qualifs = Qualifs {
208 span: item.body.span,
212 derived_from_illegal_borrow: BitSet::new_empty(item.body.local_decls.len()),
213 suppress_errors: false,
217 /// Resets the `QualifResolver`s used by this `Validator` and returns them so they can be
219 pub fn into_qualifs(mut self) -> Qualifs<'a, 'mir, 'tcx> {
220 self.qualifs.needs_drop.reset();
221 self.qualifs.has_mut_interior.reset();
225 pub fn take_errors(&mut self) -> Vec<(Span, String)> {
226 std::mem::replace(&mut self.errors, vec![])
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
233 O: NonConstOp + fmt::Debug
235 trace!("check_op: op={:?}", op);
237 if op.is_allowed_in_item(self) {
238 return CheckOpResult::Allowed;
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();
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;
251 if !self.suppress_errors {
252 op.emit_error(self, span);
255 self.errors.push((span, format!("{:?}", op)));
256 CheckOpResult::Forbidden
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)
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);
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);
283 if let Some(proj) = reborrow_place {
284 let ctx = match kind {
285 BorrowKind::Shared => PlaceContext::NonMutatingUse(
286 NonMutatingUseContext::SharedBorrow,
288 BorrowKind::Shallow => PlaceContext::NonMutatingUse(
289 NonMutatingUseContext::ShallowBorrow,
291 BorrowKind::Unique => PlaceContext::NonMutatingUse(
292 NonMutatingUseContext::UniqueBorrow,
294 BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
295 MutatingUseContext::Borrow,
298 self.visit_place_base(&place.base, ctx, location);
299 self.visit_projection(&place.base, proj, ctx, location);
301 self.super_rvalue(rvalue, location);
304 self.super_rvalue(rvalue, location);
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(..) |
318 Rvalue::Aggregate(..) => {}
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");
325 if let (CastTy::Ptr(_), CastTy::Int(_))
326 | (CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
327 self.check_op(ops::RawPtrToIntCast);
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);
339 self.check_op(ops::RawPtrComparison);
343 Rvalue::NullaryOp(NullOp::Box, _) => {
344 self.check_op(ops::HeapAllocation);
351 place_base: &PlaceBase<'tcx>,
352 context: PlaceContext,
356 "visit_place_base: place_base={:?} context={:?} location={:?}",
361 self.super_place_base(place_base, context, location);
364 PlaceBase::Local(_) => {}
365 PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
366 bug!("Promotion must be run after const validation");
369 PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
370 let is_thread_local = self.tcx.has_attr(*def_id, sym::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
379 self.tcx.sess.span_err(
381 "cannot mutate statics in the initializer of another static",
384 self.check_op(ops::StaticAccess);
390 fn visit_assign(&mut self, dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
391 trace!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
393 // Error on mutable borrows or shared borrows of values with interior mutability.
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(
401 self.qualifs.has_mut_interior.get(),
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
410 Place { base: PlaceBase::Local(borrowed_local), projection: box [] }
411 if self.derived_from_illegal_borrow.contains(borrowed_local) => true,
413 // Otherwise proceed normally: check the legality of a mutable borrow in this
415 _ => self.check_op(ops::MutBorrow(kind)) == CheckOpResult::Forbidden,
418 // When the target of the assignment is a local with no projections, mark it as
419 // derived from an illegal borrow if necessary.
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);
431 self.super_assign(dest, rvalue, location);
436 place_base: &PlaceBase<'tcx>,
437 proj: &[PlaceElem<'tcx>],
438 context: PlaceContext,
442 "visit_place_projection: proj={:?} context={:?} location={:?}",
447 self.super_projection(place_base, proj, context, location);
449 let (elem, proj_base) = match proj.split_last() {
455 ProjectionElem::Deref => {
456 if context.is_mutating_use() {
457 self.check_op(ops::MutDeref);
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);
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);
480 ProjectionElem::Downcast(..) => {
481 self.check_op(ops::Downcast);
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;
492 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
493 trace!("visit_statement: statement={:?} location={:?}", statement, location);
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());
500 match statement.kind {
501 StatementKind::Assign(..) => {
502 self.super_statement(statement, location);
504 StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
505 self.check_op(ops::IfOrMatch);
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 => {}
519 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
520 trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
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());
527 self.super_terminator(terminator, location);
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);
535 TerminatorKind::Call { func, .. } => {
536 let fn_ty = func.ty(self.body, self.tcx);
538 let def_id = match fn_ty.sty {
539 ty::FnDef(def_id, _) => def_id,
542 self.check_op(ops::FnCallIndirect);
546 self.check_op(ops::FnCallOther);
551 // At this point, we are calling a function whose `DefId` is known...
553 if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = self.tcx.fn_sig(def_id).abi() {
554 assert!(!self.tcx.is_const_fn(def_id));
556 if self.tcx.item_name(def_id) == sym::transmute {
557 self.check_op(ops::Transmute);
561 // To preserve the current semantics, we return early, allowing all
562 // intrinsics (except `transmute`) to pass unchecked to miri.
564 // FIXME: We should keep a whitelist of allowed intrinsics (or at least a
565 // blacklist of unimplemented ones) and fail here instead.
569 if self.tcx.is_const_fn(def_id) {
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));
582 self.check_op(ops::FnCallNonConst(def_id));
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, .. }
592 let mut err_span = self.span;
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)
599 .needs_drop(self.tcx, self.param_env);
605 let needs_drop = if let Place {
606 base: PlaceBase::Local(local),
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)
617 self.check_op_spanned(ops::LiveDrop, err_span);
626 /// All implementers of `NonConstOp`.
630 /// A `Downcast` projection.
633 impl NonConstOp for Downcast {}
635 /// A function call where the callee is a pointer.
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(
642 &format!("function pointers are not allowed in const fn"));
647 /// A function call where the callee is not marked as `const`.
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!(
656 "calls in {}s are limited to constant functions, \
657 tuple structs and tuple variants",
664 /// A function call where the callee is not a function definition or function pointer, e.g. a
667 /// This can be subdivided in the future to produce a better error message.
669 pub struct FnCallOther;
670 impl NonConstOp for FnCallOther {
671 const IS_SUPPORTED_IN_MIRI: bool = false;
674 /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
676 /// Contains the name of the feature that would allow the use of this function.
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;
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() {
688 "add `#![feature({})]` to the \
689 crate attributes to enable",
697 pub struct HeapAllocation;
698 impl NonConstOp for HeapAllocation {
699 const IS_SUPPORTED_IN_MIRI: bool = false;
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()) {
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."
718 pub struct IfOrMatch;
719 impl NonConstOp for IfOrMatch {}
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",
735 impl NonConstOp for Loop {}
738 pub struct MutBorrow(pub BorrowKind);
739 impl NonConstOp for MutBorrow {
740 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
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",
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 \
755 If you really want global mutable state, try using \
756 static mut or a global UnsafeCell.");
760 span_err!(item.tcx.sess, span, E0492,
761 "cannot borrow a constant which may contain \
762 interior mutability, create a static instead");
769 impl NonConstOp for MutDeref {}
773 impl NonConstOp for Panic {
774 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
775 Some(tcx.features().const_panic)
778 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
780 &item.tcx.sess.parse_sess,
784 &format!("panicking in {}s is unstable", item.mode),
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)
796 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
798 &item.tcx.sess.parse_sess,
799 sym::const_compare_raw_pointers,
802 &format!("comparing raw pointers inside {}", item.mode),
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)
814 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
816 &item.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
817 span, GateIssue::Language,
819 "dereferencing raw pointers in {}s is unstable",
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)
833 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
835 &item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
836 span, GateIssue::Language,
838 "casting pointers to integers in {}s is unstable",
845 /// An access to a (non-thread-local) `static`.
847 pub struct StaticAccess;
848 impl NonConstOp for StaticAccess {
849 fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
850 item.mode.is_static()
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()) {
859 "Static and const variables can refer to other const variables. \
860 But a const variable cannot refer to a static variable."
863 "To fix this, the value can be extracted as a const and then used."
870 /// An access to a thread-local `static`.
872 pub struct ThreadLocalAccess;
873 impl NonConstOp for ThreadLocalAccess {
874 const IS_SUPPORTED_IN_MIRI: bool = false;
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");
884 pub struct Transmute;
885 impl NonConstOp for Transmute {
886 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
887 Some(tcx.features().const_transmute)
890 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
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));
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()
907 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
908 Some(tcx.features().const_fn_union)
911 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
913 &item.tcx.sess.parse_sess, sym::const_fn_union,
914 span, GateIssue::Language,
915 "unions in const fn are unstable",