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 let Some(parent) = parent.as_local()
385 && ccx.tcx.sess.is_nightly_build()
386 {
387 if !ccx.tcx.features().const_trait_impl() {
388 err.help(
389 "add `#![feature(const_trait_impl)]` to the crate attributes to \
390 enable const traits",
391 );
392 }
393 let span = ccx.tcx.hir_expect_item(parent).vis_span;
394 let span = ccx.tcx.sess.source_map().span_extend_while_whitespace(span);
395 err.span_suggestion_verbose(
396 span.shrink_to_hi(),
397 format!("consider making trait `{trait_name}` const"),
398 "const ".to_owned(),
399 Applicability::MaybeIncorrect,
400 );
401 } else if !ccx.tcx.sess.is_nightly_build() {
402 err.help("const traits are not yet supported on stable Rust");
403 }
404 }
405 } else if ccx.tcx.constness(callee) != hir::Constness::Const {
406 let name = ccx.tcx.item_name(callee);
407 err.span_note(
408 ccx.tcx.def_span(callee),
409 format!("{def_descr} `{name}` is not const"),
410 );
411 }
412 err
413 }
414 };
415
416 err.note(format!(
417 "calls in {}s are limited to constant functions, tuple structs and tuple variants",
418 ccx.const_kind(),
419 ));
420
421 err
422}
423
424#[derive(Debug)]
428pub(crate) struct CallUnstable {
429 pub def_id: DefId,
430 pub feature: Symbol,
431 pub feature_enabled: bool,
434 pub safe_to_expose_on_stable: bool,
435 pub is_function_call: bool,
437}
438
439impl<'tcx> NonConstOp<'tcx> for CallUnstable {
440 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
441 Status::Unstable {
442 gate: self.feature,
443 gate_already_checked: self.feature_enabled,
444 safe_to_expose_on_stable: self.safe_to_expose_on_stable,
445 is_function_call: self.is_function_call,
446 }
447 }
448
449 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
450 assert!(!self.feature_enabled);
451 let mut err = if self.is_function_call {
452 ccx.dcx().create_err(errors::UnstableConstFn {
453 span,
454 def_path: ccx.tcx.def_path_str(self.def_id),
455 })
456 } else {
457 ccx.dcx().create_err(errors::UnstableConstTrait {
458 span,
459 def_path: ccx.tcx.def_path_str(self.def_id),
460 })
461 };
462 ccx.tcx.disabled_nightly_features(&mut err, [(String::new(), self.feature)]);
463 err
464 }
465}
466
467#[derive(Debug)]
469pub(crate) struct IntrinsicNonConst {
470 pub name: Symbol,
471}
472
473impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
474 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
475 ccx.dcx().create_err(errors::NonConstIntrinsic {
476 span,
477 name: self.name,
478 kind: ccx.const_kind(),
479 })
480 }
481}
482
483#[derive(Debug)]
485pub(crate) struct IntrinsicUnstable {
486 pub name: Symbol,
487 pub feature: Symbol,
488 pub const_stable_indirect: bool,
489}
490
491impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
492 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
493 Status::Unstable {
494 gate: self.feature,
495 gate_already_checked: false,
496 safe_to_expose_on_stable: self.const_stable_indirect,
497 is_function_call: false,
500 }
501 }
502
503 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
504 ccx.dcx().create_err(errors::UnstableIntrinsic {
505 span,
506 name: self.name,
507 feature: self.feature,
508 suggestion: ccx.tcx.crate_level_attribute_injection_span(),
509 })
510 }
511}
512
513#[derive(Debug)]
514pub(crate) struct Coroutine(pub hir::CoroutineKind);
515impl<'tcx> NonConstOp<'tcx> for Coroutine {
516 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
517 match self.0 {
518 hir::CoroutineKind::Desugared(
519 hir::CoroutineDesugaring::Async,
520 hir::CoroutineSource::Block,
521 )
522 | hir::CoroutineKind::Coroutine(_) => Status::Unstable {
525 gate: sym::const_async_blocks,
526 gate_already_checked: false,
527 safe_to_expose_on_stable: false,
528 is_function_call: false,
529 },
530 _ => Status::Forbidden,
531 }
532 }
533
534 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
535 let msg = format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind());
536 if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
537 ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
538 } else {
539 ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg })
540 }
541 }
542}
543
544#[derive(Debug)]
545pub(crate) struct HeapAllocation;
546impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
547 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
548 ccx.dcx().create_err(errors::UnallowedHeapAllocations {
549 span,
550 kind: ccx.const_kind(),
551 teach: ccx.tcx.sess.teach(E0010),
552 })
553 }
554}
555
556#[derive(Debug)]
557pub(crate) struct InlineAsm;
558impl<'tcx> NonConstOp<'tcx> for InlineAsm {
559 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
560 ccx.dcx().create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
561 }
562}
563
564#[derive(Debug)]
565pub(crate) struct LiveDrop<'tcx> {
566 pub dropped_at: Span,
567 pub dropped_ty: Ty<'tcx>,
568 pub needs_non_const_drop: bool,
569}
570impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
571 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
572 if self.needs_non_const_drop {
573 Status::Forbidden
574 } else {
575 Status::Unstable {
576 gate: sym::const_destruct,
577 gate_already_checked: false,
578 safe_to_expose_on_stable: false,
579 is_function_call: false,
580 }
581 }
582 }
583
584 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
585 if self.needs_non_const_drop {
586 ccx.dcx().create_err(errors::LiveDrop {
587 span,
588 dropped_ty: self.dropped_ty,
589 kind: ccx.const_kind(),
590 dropped_at: self.dropped_at,
591 })
592 } else {
593 ccx.tcx.sess.create_feature_err(
594 errors::LiveDrop {
595 span,
596 dropped_ty: self.dropped_ty,
597 kind: ccx.const_kind(),
598 dropped_at: self.dropped_at,
599 },
600 sym::const_destruct,
601 )
602 }
603 }
604}
605
606#[derive(Debug)]
607pub(crate) struct EscapingCellBorrow;
611impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
612 fn importance(&self) -> DiagImportance {
613 DiagImportance::Secondary
616 }
617 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
618 ccx.dcx().create_err(errors::InteriorMutableBorrowEscaping { span, kind: ccx.const_kind() })
619 }
620}
621
622#[derive(Debug)]
623pub(crate) struct EscapingMutBorrow;
627
628impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
629 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
630 Status::Forbidden
631 }
632
633 fn importance(&self) -> DiagImportance {
634 DiagImportance::Secondary
637 }
638
639 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
640 ccx.dcx().create_err(errors::MutableBorrowEscaping { span, kind: ccx.const_kind() })
641 }
642}
643
644#[derive(Debug)]
646pub(crate) struct PanicNonStr;
647impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
648 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
649 ccx.dcx().create_err(errors::PanicNonStrErr { span })
650 }
651}
652
653#[derive(Debug)]
657pub(crate) struct RawPtrComparison;
658impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
659 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
660 ccx.dcx().create_err(errors::RawPtrComparisonErr { span })
662 }
663}
664
665#[derive(Debug)]
669pub(crate) struct RawPtrToIntCast;
670impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
671 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
672 ccx.dcx().create_err(errors::RawPtrToIntErr { span })
673 }
674}
675
676#[derive(Debug)]
678pub(crate) struct ThreadLocalAccess;
679impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
680 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
681 ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
682 }
683}