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