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