1use hir::{ConstContext, LangItem};
4use rustc_errors::codes::*;
5use rustc_errors::{Applicability, Diag};
6use rustc_hir as hir;
7use rustc_hir::def_id::DefId;
8use rustc_infer::infer::TyCtxtInferExt;
9use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
10use rustc_middle::mir::CallSource;
11use rustc_middle::span_bug;
12use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
13use rustc_middle::ty::{
14 self, Closure, FnDef, FnPtr, GenericArgKind, GenericArgsRef, Param, TraitRef, Ty,
15 suggest_constraining_type_param,
16};
17use rustc_session::parse::add_feature_diagnostics;
18use rustc_span::{BytePos, Pos, Span, Symbol, sym};
19use rustc_trait_selection::error_reporting::traits::call_kind::{
20 CallDesugaringKind, CallKind, call_kind,
21};
22use rustc_trait_selection::traits::SelectionContext;
23use tracing::debug;
24
25use super::ConstCx;
26use crate::{errors, fluent_generated};
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum Status {
30 Unstable {
31 gate: Symbol,
33 gate_already_checked: bool,
36 safe_to_expose_on_stable: bool,
39 is_function_call: bool,
42 },
43 Forbidden,
44}
45
46#[derive(Clone, Copy)]
47pub enum DiagImportance {
48 Primary,
50
51 Secondary,
53}
54
55pub trait NonConstOp<'tcx>: std::fmt::Debug {
57 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
59 Status::Forbidden
60 }
61
62 fn importance(&self) -> DiagImportance {
63 DiagImportance::Primary
64 }
65
66 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx>;
67}
68
69#[derive(Debug)]
71pub(crate) struct FnCallIndirect;
72impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
73 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
74 ccx.dcx().create_err(errors::UnallowedFnPointerCall { span, kind: ccx.const_kind() })
75 }
76}
77
78#[derive(Debug)]
80pub(crate) struct ConditionallyConstCall<'tcx> {
81 pub callee: DefId,
82 pub args: GenericArgsRef<'tcx>,
83 pub span: Span,
84 pub call_source: CallSource,
85}
86
87impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> {
88 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
89 Status::Unstable {
91 gate: sym::const_trait_impl,
92 gate_already_checked: false,
93 safe_to_expose_on_stable: false,
94 is_function_call: false,
96 }
97 }
98
99 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
100 let mut diag = build_error_for_const_call(
101 ccx,
102 self.callee,
103 self.args,
104 self.span,
105 self.call_source,
106 "conditionally",
107 |_, _, _| {},
108 );
109
110 diag.code(E0658);
112 add_feature_diagnostics(&mut diag, ccx.tcx.sess, sym::const_trait_impl);
113
114 diag
115 }
116}
117
118#[derive(Debug, Clone, Copy)]
120pub(crate) struct FnCallNonConst<'tcx> {
121 pub callee: DefId,
122 pub args: GenericArgsRef<'tcx>,
123 pub span: Span,
124 pub call_source: CallSource,
125}
126
127impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
128 #[allow(rustc::diagnostic_outside_of_impl)]
130 #[allow(rustc::untranslatable_diagnostic)]
131 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
132 let tcx = ccx.tcx;
133 let caller = ccx.def_id();
134
135 let mut err = build_error_for_const_call(
136 ccx,
137 self.callee,
138 self.args,
139 self.span,
140 self.call_source,
141 "non",
142 |err, self_ty, trait_id| {
143 let trait_ref = TraitRef::from_method(tcx, trait_id, self.args);
146
147 match self_ty.kind() {
148 Param(param_ty) => {
149 debug!(?param_ty);
150 if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() {
151 let constraint = with_no_trimmed_paths!(format!(
152 "~const {}",
153 trait_ref.print_trait_sugared(),
154 ));
155 suggest_constraining_type_param(
156 tcx,
157 generics,
158 err,
159 param_ty.name.as_str(),
160 &constraint,
161 Some(trait_ref.def_id),
162 None,
163 );
164 }
165 }
166 ty::Adt(..) => {
167 let (infcx, param_env) =
168 tcx.infer_ctxt().build_with_typing_env(ccx.typing_env);
169 let obligation =
170 Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
171 let mut selcx = SelectionContext::new(&infcx);
172 let implsrc = selcx.select(&obligation);
173 if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
174 if !tcx.is_const_trait_impl(data.impl_def_id) {
176 let span = tcx.def_span(data.impl_def_id);
177 err.subdiagnostic(errors::NonConstImplNote { span });
178 }
179 }
180 }
181 _ => {}
182 }
183 },
184 );
185
186 if let ConstContext::Static(_) = ccx.const_kind() {
187 err.note(fluent_generated::const_eval_lazy_lock);
188 }
189
190 err
191 }
192}
193
194fn build_error_for_const_call<'tcx>(
199 ccx: &ConstCx<'_, 'tcx>,
200 callee: DefId,
201 args: ty::GenericArgsRef<'tcx>,
202 span: Span,
203 call_source: CallSource,
204 non_or_conditionally: &'static str,
205 note_trait_if_possible: impl FnOnce(&mut Diag<'tcx>, Ty<'tcx>, DefId),
206) -> Diag<'tcx> {
207 let tcx = ccx.tcx;
208
209 let call_kind =
210 call_kind(tcx, ccx.typing_env, callee, args, span, call_source.from_hir_call(), None);
211
212 debug!(?call_kind);
213
214 let mut err = match call_kind {
215 CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
216 macro_rules! error {
217 ($err:ident) => {
218 tcx.dcx().create_err(errors::$err {
219 span,
220 ty: self_ty,
221 kind: ccx.const_kind(),
222 non_or_conditionally,
223 })
224 };
225 }
226
227 match kind {
230 CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => {
231 error!(NonConstForLoopIntoIter)
232 }
233 CallDesugaringKind::QuestionBranch => {
234 error!(NonConstQuestionBranch)
235 }
236 CallDesugaringKind::QuestionFromResidual => {
237 error!(NonConstQuestionFromResidual)
238 }
239 CallDesugaringKind::TryBlockFromOutput => {
240 error!(NonConstTryBlockFromOutput)
241 }
242 CallDesugaringKind::Await => {
243 error!(NonConstAwait)
244 }
245 }
246 }
247 CallKind::FnCall { fn_trait_id, self_ty } => {
248 let note = match self_ty.kind() {
249 FnDef(def_id, ..) => {
250 let span = tcx.def_span(*def_id);
251 if ccx.tcx.is_const_fn(*def_id) {
252 span_bug!(span, "calling const FnDef errored when it shouldn't");
253 }
254
255 Some(errors::NonConstClosureNote::FnDef { span })
256 }
257 FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr),
258 Closure(..) => Some(errors::NonConstClosureNote::Closure),
259 _ => None,
260 };
261
262 let mut err = tcx.dcx().create_err(errors::NonConstClosure {
263 span,
264 kind: ccx.const_kind(),
265 note,
266 non_or_conditionally,
267 });
268
269 note_trait_if_possible(&mut err, self_ty, fn_trait_id);
270 err
271 }
272 CallKind::Operator { trait_id, self_ty, .. } => {
273 let mut err = if let CallSource::MatchCmp = call_source {
274 tcx.dcx().create_err(errors::NonConstMatchEq {
275 span,
276 kind: ccx.const_kind(),
277 ty: self_ty,
278 non_or_conditionally,
279 })
280 } else {
281 let mut sugg = None;
282
283 if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) {
284 match (args[0].unpack(), args[1].unpack()) {
285 (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
286 if self_ty == rhs_ty
287 && self_ty.is_ref()
288 && self_ty.peel_refs().is_primitive() =>
289 {
290 let mut num_refs = 0;
291 let mut tmp_ty = self_ty;
292 while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
293 num_refs += 1;
294 tmp_ty = *inner_ty;
295 }
296 let deref = "*".repeat(num_refs);
297
298 if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
299 if let Some(eq_idx) = call_str.find("==") {
300 if let Some(rhs_idx) =
301 call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
302 {
303 let rhs_pos =
304 span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
305 let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
306 sugg = Some(errors::ConsiderDereferencing {
307 deref,
308 span: span.shrink_to_lo(),
309 rhs_span,
310 });
311 }
312 }
313 }
314 }
315 _ => {}
316 }
317 }
318 tcx.dcx().create_err(errors::NonConstOperator {
319 span,
320 kind: ccx.const_kind(),
321 sugg,
322 non_or_conditionally,
323 })
324 };
325
326 note_trait_if_possible(&mut err, self_ty, trait_id);
327 err
328 }
329 CallKind::DerefCoercion { deref_target_span, deref_target_ty, self_ty } => {
330 let target = if let Some(deref_target_span) = deref_target_span
332 && tcx.sess.source_map().is_span_accessible(deref_target_span)
333 {
334 Some(deref_target_span)
335 } else {
336 None
337 };
338
339 let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion {
340 span,
341 ty: self_ty,
342 kind: ccx.const_kind(),
343 target_ty: deref_target_ty,
344 deref_target: target,
345 non_or_conditionally,
346 });
347
348 note_trait_if_possible(
349 &mut err,
350 self_ty,
351 tcx.require_lang_item(LangItem::Deref, Some(span)),
352 );
353 err
354 }
355 _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => {
356 ccx.dcx().create_err(errors::NonConstFmtMacroCall {
357 span,
358 kind: ccx.const_kind(),
359 non_or_conditionally,
360 })
361 }
362 _ => ccx.dcx().create_err(errors::NonConstFnCall {
363 span,
364 def_descr: ccx.tcx.def_descr(callee),
365 def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
366 kind: ccx.const_kind(),
367 non_or_conditionally,
368 }),
369 };
370
371 err.note(format!(
372 "calls in {}s are limited to constant functions, \
373 tuple structs and tuple variants",
374 ccx.const_kind(),
375 ));
376
377 err
378}
379
380#[derive(Debug)]
384pub(crate) struct CallUnstable {
385 pub def_id: DefId,
386 pub feature: Symbol,
387 pub feature_enabled: bool,
390 pub safe_to_expose_on_stable: bool,
391 pub suggestion_span: Option<Span>,
392 pub is_function_call: bool,
394}
395
396impl<'tcx> NonConstOp<'tcx> for CallUnstable {
397 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
398 Status::Unstable {
399 gate: self.feature,
400 gate_already_checked: self.feature_enabled,
401 safe_to_expose_on_stable: self.safe_to_expose_on_stable,
402 is_function_call: self.is_function_call,
403 }
404 }
405
406 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
407 assert!(!self.feature_enabled);
408 let mut err = if self.is_function_call {
409 ccx.dcx().create_err(errors::UnstableConstFn {
410 span,
411 def_path: ccx.tcx.def_path_str(self.def_id),
412 })
413 } else {
414 ccx.dcx().create_err(errors::UnstableConstTrait {
415 span,
416 def_path: ccx.tcx.def_path_str(self.def_id),
417 })
418 };
419 let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature);
421 #[allow(rustc::untranslatable_diagnostic)]
422 if let Some(span) = self.suggestion_span {
423 err.span_suggestion_verbose(
424 span,
425 msg,
426 format!("#![feature({})]\n", self.feature),
427 Applicability::MachineApplicable,
428 );
429 } else {
430 err.help(msg);
431 }
432
433 err
434 }
435}
436
437#[derive(Debug)]
439pub(crate) struct IntrinsicNonConst {
440 pub name: Symbol,
441}
442
443impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
444 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
445 ccx.dcx().create_err(errors::NonConstIntrinsic {
446 span,
447 name: self.name,
448 kind: ccx.const_kind(),
449 })
450 }
451}
452
453#[derive(Debug)]
455pub(crate) struct IntrinsicUnstable {
456 pub name: Symbol,
457 pub feature: Symbol,
458 pub const_stable_indirect: bool,
459 pub suggestion: Option<Span>,
460}
461
462impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
463 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
464 Status::Unstable {
465 gate: self.feature,
466 gate_already_checked: false,
467 safe_to_expose_on_stable: self.const_stable_indirect,
468 is_function_call: false,
471 }
472 }
473
474 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
475 ccx.dcx().create_err(errors::UnstableIntrinsic {
476 span,
477 name: self.name,
478 feature: self.feature,
479 suggestion: self.suggestion,
480 help: self.suggestion.is_none(),
481 })
482 }
483}
484
485#[derive(Debug)]
486pub(crate) struct Coroutine(pub hir::CoroutineKind);
487impl<'tcx> NonConstOp<'tcx> for Coroutine {
488 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
489 if let hir::CoroutineKind::Desugared(
490 hir::CoroutineDesugaring::Async,
491 hir::CoroutineSource::Block,
492 ) = self.0
493 {
494 Status::Unstable {
495 gate: sym::const_async_blocks,
496 gate_already_checked: false,
497 safe_to_expose_on_stable: false,
498 is_function_call: false,
499 }
500 } else {
501 Status::Forbidden
502 }
503 }
504
505 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
506 let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
507 if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
508 ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
509 } else {
510 ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg })
511 }
512 }
513}
514
515#[derive(Debug)]
516pub(crate) struct HeapAllocation;
517impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
518 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
519 ccx.dcx().create_err(errors::UnallowedHeapAllocations {
520 span,
521 kind: ccx.const_kind(),
522 teach: ccx.tcx.sess.teach(E0010),
523 })
524 }
525}
526
527#[derive(Debug)]
528pub(crate) struct InlineAsm;
529impl<'tcx> NonConstOp<'tcx> for InlineAsm {
530 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
531 ccx.dcx().create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
532 }
533}
534
535#[derive(Debug)]
536pub(crate) struct LiveDrop<'tcx> {
537 pub dropped_at: Span,
538 pub dropped_ty: Ty<'tcx>,
539 pub needs_non_const_drop: bool,
540}
541impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
542 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
543 if self.needs_non_const_drop {
544 Status::Forbidden
545 } else {
546 Status::Unstable {
547 gate: sym::const_destruct,
548 gate_already_checked: false,
549 safe_to_expose_on_stable: false,
550 is_function_call: false,
551 }
552 }
553 }
554
555 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
556 if self.needs_non_const_drop {
557 ccx.dcx().create_err(errors::LiveDrop {
558 span,
559 dropped_ty: self.dropped_ty,
560 kind: ccx.const_kind(),
561 dropped_at: self.dropped_at,
562 })
563 } else {
564 ccx.tcx.sess.create_feature_err(
565 errors::LiveDrop {
566 span,
567 dropped_ty: self.dropped_ty,
568 kind: ccx.const_kind(),
569 dropped_at: self.dropped_at,
570 },
571 sym::const_destruct,
572 )
573 }
574 }
575}
576
577#[derive(Debug)]
578pub(crate) struct EscapingCellBorrow;
582impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
583 fn importance(&self) -> DiagImportance {
584 DiagImportance::Secondary
587 }
588 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
589 ccx.dcx().create_err(errors::InteriorMutableRefEscaping {
590 span,
591 opt_help: matches!(ccx.const_kind(), hir::ConstContext::Static(_)),
592 kind: ccx.const_kind(),
593 teach: ccx.tcx.sess.teach(E0492),
594 })
595 }
596}
597
598#[derive(Debug)]
599pub(crate) struct EscapingMutBorrow(pub hir::BorrowKind);
603
604impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
605 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
606 Status::Forbidden
607 }
608
609 fn importance(&self) -> DiagImportance {
610 DiagImportance::Secondary
613 }
614
615 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
616 match self.0 {
617 hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::MutableRawEscaping {
618 span,
619 kind: ccx.const_kind(),
620 teach: ccx.tcx.sess.teach(E0764),
621 }),
622 hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping {
623 span,
624 kind: ccx.const_kind(),
625 teach: ccx.tcx.sess.teach(E0764),
626 }),
627 }
628 }
629}
630
631#[derive(Debug)]
633pub(crate) struct PanicNonStr;
634impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
635 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
636 ccx.dcx().create_err(errors::PanicNonStrErr { span })
637 }
638}
639
640#[derive(Debug)]
644pub(crate) struct RawPtrComparison;
645impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
646 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
647 ccx.dcx().create_err(errors::RawPtrComparisonErr { span })
649 }
650}
651
652#[derive(Debug)]
656pub(crate) struct RawPtrToIntCast;
657impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
658 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
659 ccx.dcx().create_err(errors::RawPtrToIntErr { span })
660 }
661}
662
663#[derive(Debug)]
665pub(crate) struct ThreadLocalAccess;
666impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
667 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
668 ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
669 }
670}