1 //! Concrete error types for all operations which may be invalid in a certain const context.
3 use rustc_errors::{struct_span_err, Applicability};
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};
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);
17 let gate = match op.status_in_item(ccx) {
18 Status::Allowed => return false,
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);
24 if unstable_in_stable {
28 &format!("const-stable function cannot use `#[feature({})]`", gate.as_str()),
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,
36 .note("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks")
40 return unstable_in_stable;
43 Status::Unstable(gate) => Some(gate),
44 Status::Forbidden => None,
47 if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
48 ccx.tcx.sess.miri_unleashed_feature(span, gate);
52 op.emit_error(ccx, span);
56 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
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;
67 /// Returns an enum indicating whether this operation is allowed within the given item.
68 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
72 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
73 let mut err = struct_span_err!(
77 "{} contains unimplemented expression type",
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));
87 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
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.",
93 "Remember: you can't use a function call inside a const's initialization \
94 expression! However, you can use it anywhere else.",
103 impl NonConstOp for Abort {
104 const STOPS_CONST_CHECKING: bool = true;
106 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
107 mcf_status_in_item(ccx)
110 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
111 mcf_emit_error(ccx, span, "abort is not stable in const fn")
116 pub struct NonPrimitiveOp;
117 impl NonConstOp for NonPrimitiveOp {
118 const STOPS_CONST_CHECKING: bool = true;
120 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
121 mcf_status_in_item(ccx)
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")
129 /// A function call where the callee is a pointer.
131 pub struct FnCallIndirect;
132 impl NonConstOp for FnCallIndirect {
133 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
135 ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
140 /// A function call where the callee is not marked as `const`.
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!(
149 "calls in {}s are limited to constant functions, \
150 tuple structs and tuple variants",
157 /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
159 /// Contains the name of the feature that would allow the use of this function.
161 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
163 impl NonConstOp for FnCallUnstable {
164 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
165 let FnCallUnstable(def_id, feature) = *self;
167 let mut err = ccx.tcx.sess.struct_span_err(
169 &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
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 {
177 "add `#![feature({})]` to the crate attributes to enable",
187 pub struct FnPtrCast;
188 impl NonConstOp for FnPtrCast {
189 const STOPS_CONST_CHECKING: bool = true;
191 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
192 mcf_status_in_item(ccx)
195 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
196 mcf_emit_error(ccx, span, "function pointer casts are not allowed in const fn");
201 pub struct Generator;
202 impl NonConstOp for Generator {
203 const STOPS_CONST_CHECKING: bool = true;
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)
211 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
212 mcf_emit_error(ccx, span, "const fn generators are unstable");
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!(
224 "allocations are not allowed in {}s",
227 err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
228 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
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.",
241 pub struct InlineAsm;
242 impl NonConstOp for InlineAsm {}
245 pub struct LiveDrop {
246 pub dropped_at: Option<Span>,
248 impl NonConstOp for LiveDrop {
249 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
250 let mut diagnostic = struct_span_err!(
254 "destructors cannot be evaluated at compile-time"
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");
265 pub struct CellBorrow;
266 impl NonConstOp for CellBorrow {
267 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
272 "cannot borrow a constant which may contain \
273 interior mutability, create a static instead"
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)
291 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
292 let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn {
294 &ccx.tcx.sess.parse_sess,
297 &format!("mutable references are not allowed in {}s", ccx.const_kind()),
300 let mut err = struct_span_err!(
304 "mutable references are not allowed in {}s",
307 err.span_label(span, format!("`&mut` is only allowed in `const fn`"));
310 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
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 \
318 If you really want global mutable state, try using \
319 static mut or a global UnsafeCell.",
326 // FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues.
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)
339 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
341 &ccx.tcx.sess.parse_sess,
344 &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()),
352 impl NonConstOp for MutDeref {
353 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
354 Status::Unstable(sym::const_mut_refs)
360 impl NonConstOp for Panic {
361 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
362 Status::Unstable(sym::const_panic)
365 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
367 &ccx.tcx.sess.parse_sess,
370 &format!("panicking in {}s is unstable", ccx.const_kind()),
377 pub struct RawPtrComparison;
378 impl NonConstOp for RawPtrComparison {
379 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
383 .struct_span_err(span, "pointers cannot be reliably compared during const eval.");
385 "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \
386 for more information",
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)
399 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
401 &ccx.tcx.sess.parse_sess,
402 sym::const_raw_ptr_deref,
404 &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
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)
417 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
419 &ccx.tcx.sess.parse_sess,
420 sym::const_raw_ptr_to_usize_cast,
422 &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),),
428 /// An access to a (non-thread-local) `static`.
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() {
440 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
441 let mut err = struct_span_err!(
445 "{}s cannot refer to statics",
449 "consider extracting the value of the `static` to a `const`, and referring to that",
451 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
453 "`static` and `const` variables can refer to other `const` variables. \
454 A `const` variable, however, cannot refer to a `static` variable.",
456 err.help("To fix this, the value can be extracted to a `const` and then used.");
462 /// An access to a thread-local `static`.
464 pub struct ThreadLocalAccess;
465 impl NonConstOp for ThreadLocalAccess {
466 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
471 "thread-local statics cannot be \
472 accessed at compile-time"
479 pub struct Transmute;
480 impl NonConstOp for Transmute {
481 const STOPS_CONST_CHECKING: bool = true;
483 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
484 if ccx.const_kind() != hir::ConstContext::ConstFn {
487 Status::Unstable(sym::const_fn_transmute)
491 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
493 &ccx.tcx.sess.parse_sess,
494 sym::const_fn_transmute,
496 &format!("`transmute` is not allowed in {}s", ccx.const_kind()),
498 .note("`transmute` is only allowed in constants and statics for now")
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 {
511 Status::Unstable(sym::const_fn_union)
515 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
517 &ccx.tcx.sess.parse_sess,
520 "unions in const fn are unstable",
528 /// [#64992]: https://github.com/rust-lang/rust/issues/64992
530 pub struct UnsizingCast;
531 impl NonConstOp for UnsizingCast {
532 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
533 mcf_status_in_item(ccx)
536 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
540 "unsizing casts to types besides slices are not allowed in const fn",
545 // Types that cannot appear in the signature or locals of a `const fn`.
551 impl NonConstOp for MutRef {
552 const STOPS_CONST_CHECKING: bool = true;
554 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
555 Status::Unstable(sym::const_mut_refs)
558 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
560 &ccx.tcx.sess.parse_sess,
563 &format!("mutable references are not allowed in {}s", ccx.const_kind()),
571 impl NonConstOp for FnPtr {
572 const STOPS_CONST_CHECKING: bool = true;
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) {
580 mcf_status_in_item(ccx)
584 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
585 mcf_emit_error(ccx, span, "function pointers in const fn are unstable");
590 pub struct ImplTrait;
591 impl NonConstOp for ImplTrait {
592 const STOPS_CONST_CHECKING: bool = true;
594 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
595 mcf_status_in_item(ccx)
598 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
599 mcf_emit_error(ccx, span, "`impl Trait` in const fn is unstable");
604 pub struct TraitBound;
605 impl NonConstOp for TraitBound {
606 const STOPS_CONST_CHECKING: bool = true;
608 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
609 mcf_status_in_item(ccx)
612 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
616 "trait bounds other than `Sized` on const fn parameters are unstable",
621 /// A trait bound with the `?const Trait` opt-out
623 pub struct TraitBoundNotConst;
624 impl NonConstOp for TraitBoundNotConst {
625 const STOPS_CONST_CHECKING: bool = true;
627 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
628 Status::Unstable(sym::const_trait_bound_opt_out)
631 fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
633 &ccx.tcx.sess.parse_sess,
634 sym::const_trait_bound_opt_out,
636 "`?const Trait` syntax is unstable",
643 fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
644 if ccx.const_kind() != hir::ConstContext::ConstFn {
647 Status::Unstable(sym::const_fn)
651 fn mcf_emit_error(ccx: &ConstCx<'_, '_>, span: Span, msg: &str) {
652 struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg)
654 "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
655 for more information",
657 .help("add `#![feature(const_fn)]` to the crate attributes to enable")