1use hir::{ConstContext, LangItem};
4use rustc_errors::codes::*;
5use rustc_errors::{Applicability, Diag, MultiSpan};
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, AssocContainer, Closure, FnDef, FnPtr, GenericArgKind, GenericArgsRef, Param, TraitRef,
15 Ty, 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_assoc(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].kind(), args[1].kind()) {
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 && let Some(eq_idx) = call_str.find("==")
300 && let Some(rhs_idx) =
301 call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
302 {
303 let rhs_pos = span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
304 let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
305 sugg = Some(errors::ConsiderDereferencing {
306 deref,
307 span: span.shrink_to_lo(),
308 rhs_span,
309 });
310 }
311 }
312 _ => {}
313 }
314 }
315 tcx.dcx().create_err(errors::NonConstOperator {
316 span,
317 kind: ccx.const_kind(),
318 sugg,
319 non_or_conditionally,
320 })
321 };
322
323 note_trait_if_possible(&mut err, self_ty, trait_id);
324 err
325 }
326 CallKind::DerefCoercion { deref_target_span, deref_target_ty, self_ty } => {
327 let target = if let Some(deref_target_span) = deref_target_span
329 && tcx.sess.source_map().is_span_accessible(deref_target_span)
330 {
331 Some(deref_target_span)
332 } else {
333 None
334 };
335
336 let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion {
337 span,
338 ty: self_ty,
339 kind: ccx.const_kind(),
340 target_ty: deref_target_ty,
341 deref_target: target,
342 non_or_conditionally,
343 });
344
345 note_trait_if_possible(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, span));
346 err
347 }
348 _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::FmtArgumentsNew) => {
349 ccx.dcx().create_err(errors::NonConstFmtMacroCall {
350 span,
351 kind: ccx.const_kind(),
352 non_or_conditionally,
353 })
354 }
355 _ => {
356 let def_descr = ccx.tcx.def_descr(callee);
357 let mut err = ccx.dcx().create_err(errors::NonConstFnCall {
358 span,
359 def_descr,
360 def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
361 kind: ccx.const_kind(),
362 non_or_conditionally,
363 });
364 if let Some(item) = ccx.tcx.opt_associated_item(callee) {
365 if let AssocContainer::Trait = item.container
366 && let parent = item.container_id(ccx.tcx)
367 && !ccx.tcx.is_const_trait(parent)
368 {
369 let assoc_span = ccx.tcx.def_span(callee);
370 let assoc_name = ccx.tcx.item_name(callee);
371 let mut span: MultiSpan = ccx.tcx.def_span(parent).into();
372 span.push_span_label(assoc_span, format!("this {def_descr} is not const"));
373 let trait_descr = ccx.tcx.def_descr(parent);
374 let trait_span = ccx.tcx.def_span(parent);
375 let trait_name = ccx.tcx.item_name(parent);
376 span.push_span_label(trait_span, format!("this {trait_descr} is not const"));
377 err.span_note(
378 span,
379 format!(
380 "{def_descr} `{assoc_name}` is not const because {trait_descr} \
381 `{trait_name}` is not const",
382 ),
383 );
384 if parent.is_local() && ccx.tcx.sess.is_nightly_build() {
385 if !ccx.tcx.features().const_trait_impl() {
386 err.help(
387 "add `#![feature(const_trait_impl)]` to the crate attributes to \
388 enable `#[const_trait]`",
389 );
390 }
391 let indentation = ccx
392 .tcx
393 .sess
394 .source_map()
395 .indentation_before(trait_span)
396 .unwrap_or_default();
397 err.span_suggestion_verbose(
398 trait_span.shrink_to_lo(),
399 format!("consider making trait `{trait_name}` const"),
400 format!("#[const_trait]\n{indentation}"),
401 Applicability::MaybeIncorrect,
402 );
403 } else if !ccx.tcx.sess.is_nightly_build() {
404 err.help("const traits are not yet supported on stable Rust");
405 }
406 }
407 } else if ccx.tcx.constness(callee) != hir::Constness::Const {
408 let name = ccx.tcx.item_name(callee);
409 err.span_note(
410 ccx.tcx.def_span(callee),
411 format!("{def_descr} `{name}` is not const"),
412 );
413 }
414 err
415 }
416 };
417
418 err.note(format!(
419 "calls in {}s are limited to constant functions, tuple structs and tuple variants",
420 ccx.const_kind(),
421 ));
422
423 err
424}
425
426#[derive(Debug)]
430pub(crate) struct CallUnstable {
431 pub def_id: DefId,
432 pub feature: Symbol,
433 pub feature_enabled: bool,
436 pub safe_to_expose_on_stable: bool,
437 pub is_function_call: bool,
439}
440
441impl<'tcx> NonConstOp<'tcx> for CallUnstable {
442 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
443 Status::Unstable {
444 gate: self.feature,
445 gate_already_checked: self.feature_enabled,
446 safe_to_expose_on_stable: self.safe_to_expose_on_stable,
447 is_function_call: self.is_function_call,
448 }
449 }
450
451 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
452 assert!(!self.feature_enabled);
453 let mut err = if self.is_function_call {
454 ccx.dcx().create_err(errors::UnstableConstFn {
455 span,
456 def_path: ccx.tcx.def_path_str(self.def_id),
457 })
458 } else {
459 ccx.dcx().create_err(errors::UnstableConstTrait {
460 span,
461 def_path: ccx.tcx.def_path_str(self.def_id),
462 })
463 };
464 ccx.tcx.disabled_nightly_features(&mut err, [(String::new(), self.feature)]);
465 err
466 }
467}
468
469#[derive(Debug)]
471pub(crate) struct IntrinsicNonConst {
472 pub name: Symbol,
473}
474
475impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
476 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
477 ccx.dcx().create_err(errors::NonConstIntrinsic {
478 span,
479 name: self.name,
480 kind: ccx.const_kind(),
481 })
482 }
483}
484
485#[derive(Debug)]
487pub(crate) struct IntrinsicUnstable {
488 pub name: Symbol,
489 pub feature: Symbol,
490 pub const_stable_indirect: bool,
491}
492
493impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
494 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
495 Status::Unstable {
496 gate: self.feature,
497 gate_already_checked: false,
498 safe_to_expose_on_stable: self.const_stable_indirect,
499 is_function_call: false,
502 }
503 }
504
505 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
506 ccx.dcx().create_err(errors::UnstableIntrinsic {
507 span,
508 name: self.name,
509 feature: self.feature,
510 suggestion: ccx.tcx.crate_level_attribute_injection_span(),
511 })
512 }
513}
514
515#[derive(Debug)]
516pub(crate) struct Coroutine(pub hir::CoroutineKind);
517impl<'tcx> NonConstOp<'tcx> for Coroutine {
518 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
519 match self.0 {
520 hir::CoroutineKind::Desugared(
521 hir::CoroutineDesugaring::Async,
522 hir::CoroutineSource::Block,
523 )
524 | hir::CoroutineKind::Coroutine(_) => Status::Unstable {
527 gate: sym::const_async_blocks,
528 gate_already_checked: false,
529 safe_to_expose_on_stable: false,
530 is_function_call: false,
531 },
532 _ => Status::Forbidden,
533 }
534 }
535
536 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
537 let msg = format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind());
538 if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
539 ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
540 } else {
541 ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg })
542 }
543 }
544}
545
546#[derive(Debug)]
547pub(crate) struct HeapAllocation;
548impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
549 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
550 ccx.dcx().create_err(errors::UnallowedHeapAllocations {
551 span,
552 kind: ccx.const_kind(),
553 teach: ccx.tcx.sess.teach(E0010),
554 })
555 }
556}
557
558#[derive(Debug)]
559pub(crate) struct InlineAsm;
560impl<'tcx> NonConstOp<'tcx> for InlineAsm {
561 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
562 ccx.dcx().create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
563 }
564}
565
566#[derive(Debug)]
567pub(crate) struct LiveDrop<'tcx> {
568 pub dropped_at: Span,
569 pub dropped_ty: Ty<'tcx>,
570 pub needs_non_const_drop: bool,
571}
572impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
573 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
574 if self.needs_non_const_drop {
575 Status::Forbidden
576 } else {
577 Status::Unstable {
578 gate: sym::const_destruct,
579 gate_already_checked: false,
580 safe_to_expose_on_stable: false,
581 is_function_call: false,
582 }
583 }
584 }
585
586 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
587 if self.needs_non_const_drop {
588 ccx.dcx().create_err(errors::LiveDrop {
589 span,
590 dropped_ty: self.dropped_ty,
591 kind: ccx.const_kind(),
592 dropped_at: self.dropped_at,
593 })
594 } else {
595 ccx.tcx.sess.create_feature_err(
596 errors::LiveDrop {
597 span,
598 dropped_ty: self.dropped_ty,
599 kind: ccx.const_kind(),
600 dropped_at: self.dropped_at,
601 },
602 sym::const_destruct,
603 )
604 }
605 }
606}
607
608#[derive(Debug)]
609pub(crate) struct EscapingCellBorrow;
613impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
614 fn importance(&self) -> DiagImportance {
615 DiagImportance::Secondary
618 }
619 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
620 ccx.dcx().create_err(errors::InteriorMutableBorrowEscaping { span, kind: ccx.const_kind() })
621 }
622}
623
624#[derive(Debug)]
625pub(crate) struct EscapingMutBorrow;
629
630impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
631 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
632 Status::Forbidden
633 }
634
635 fn importance(&self) -> DiagImportance {
636 DiagImportance::Secondary
639 }
640
641 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
642 ccx.dcx().create_err(errors::MutableBorrowEscaping { span, kind: ccx.const_kind() })
643 }
644}
645
646#[derive(Debug)]
648pub(crate) struct PanicNonStr;
649impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
650 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
651 ccx.dcx().create_err(errors::PanicNonStrErr { span })
652 }
653}
654
655#[derive(Debug)]
659pub(crate) struct RawPtrComparison;
660impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
661 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
662 ccx.dcx().create_err(errors::RawPtrComparisonErr { span })
664 }
665}
666
667#[derive(Debug)]
671pub(crate) struct RawPtrToIntCast;
672impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
673 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
674 ccx.dcx().create_err(errors::RawPtrToIntErr { span })
675 }
676}
677
678#[derive(Debug)]
680pub(crate) struct ThreadLocalAccess;
681impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
682 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
683 ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
684 }
685}