1use hir::{ConstContext, LangItem};
4use rustc_errors::Diag;
5use rustc_errors::codes::*;
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].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 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(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, span));
349 err
350 }
351 _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::FmtArgumentsNew) => {
352 ccx.dcx().create_err(errors::NonConstFmtMacroCall {
353 span,
354 kind: ccx.const_kind(),
355 non_or_conditionally,
356 })
357 }
358 _ => ccx.dcx().create_err(errors::NonConstFnCall {
359 span,
360 def_descr: ccx.tcx.def_descr(callee),
361 def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
362 kind: ccx.const_kind(),
363 non_or_conditionally,
364 }),
365 };
366
367 err.note(format!(
368 "calls in {}s are limited to constant functions, \
369 tuple structs and tuple variants",
370 ccx.const_kind(),
371 ));
372
373 err
374}
375
376#[derive(Debug)]
380pub(crate) struct CallUnstable {
381 pub def_id: DefId,
382 pub feature: Symbol,
383 pub feature_enabled: bool,
386 pub safe_to_expose_on_stable: bool,
387 pub is_function_call: bool,
389}
390
391impl<'tcx> NonConstOp<'tcx> for CallUnstable {
392 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
393 Status::Unstable {
394 gate: self.feature,
395 gate_already_checked: self.feature_enabled,
396 safe_to_expose_on_stable: self.safe_to_expose_on_stable,
397 is_function_call: self.is_function_call,
398 }
399 }
400
401 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
402 assert!(!self.feature_enabled);
403 let mut err = if self.is_function_call {
404 ccx.dcx().create_err(errors::UnstableConstFn {
405 span,
406 def_path: ccx.tcx.def_path_str(self.def_id),
407 })
408 } else {
409 ccx.dcx().create_err(errors::UnstableConstTrait {
410 span,
411 def_path: ccx.tcx.def_path_str(self.def_id),
412 })
413 };
414 ccx.tcx.disabled_nightly_features(&mut err, [(String::new(), self.feature)]);
415 err
416 }
417}
418
419#[derive(Debug)]
421pub(crate) struct IntrinsicNonConst {
422 pub name: Symbol,
423}
424
425impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
426 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
427 ccx.dcx().create_err(errors::NonConstIntrinsic {
428 span,
429 name: self.name,
430 kind: ccx.const_kind(),
431 })
432 }
433}
434
435#[derive(Debug)]
437pub(crate) struct IntrinsicUnstable {
438 pub name: Symbol,
439 pub feature: Symbol,
440 pub const_stable_indirect: bool,
441}
442
443impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
444 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
445 Status::Unstable {
446 gate: self.feature,
447 gate_already_checked: false,
448 safe_to_expose_on_stable: self.const_stable_indirect,
449 is_function_call: false,
452 }
453 }
454
455 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
456 ccx.dcx().create_err(errors::UnstableIntrinsic {
457 span,
458 name: self.name,
459 feature: self.feature,
460 suggestion: ccx.tcx.crate_level_attribute_injection_span(),
461 })
462 }
463}
464
465#[derive(Debug)]
466pub(crate) struct Coroutine(pub hir::CoroutineKind);
467impl<'tcx> NonConstOp<'tcx> for Coroutine {
468 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
469 match self.0 {
470 hir::CoroutineKind::Desugared(
471 hir::CoroutineDesugaring::Async,
472 hir::CoroutineSource::Block,
473 )
474 | hir::CoroutineKind::Coroutine(_) => Status::Unstable {
477 gate: sym::const_async_blocks,
478 gate_already_checked: false,
479 safe_to_expose_on_stable: false,
480 is_function_call: false,
481 },
482 _ => Status::Forbidden,
483 }
484 }
485
486 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
487 let msg = format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind());
488 if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
489 ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
490 } else {
491 ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg })
492 }
493 }
494}
495
496#[derive(Debug)]
497pub(crate) struct HeapAllocation;
498impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
499 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
500 ccx.dcx().create_err(errors::UnallowedHeapAllocations {
501 span,
502 kind: ccx.const_kind(),
503 teach: ccx.tcx.sess.teach(E0010),
504 })
505 }
506}
507
508#[derive(Debug)]
509pub(crate) struct InlineAsm;
510impl<'tcx> NonConstOp<'tcx> for InlineAsm {
511 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
512 ccx.dcx().create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
513 }
514}
515
516#[derive(Debug)]
517pub(crate) struct LiveDrop<'tcx> {
518 pub dropped_at: Span,
519 pub dropped_ty: Ty<'tcx>,
520 pub needs_non_const_drop: bool,
521}
522impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
523 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
524 if self.needs_non_const_drop {
525 Status::Forbidden
526 } else {
527 Status::Unstable {
528 gate: sym::const_destruct,
529 gate_already_checked: false,
530 safe_to_expose_on_stable: false,
531 is_function_call: false,
532 }
533 }
534 }
535
536 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
537 if self.needs_non_const_drop {
538 ccx.dcx().create_err(errors::LiveDrop {
539 span,
540 dropped_ty: self.dropped_ty,
541 kind: ccx.const_kind(),
542 dropped_at: self.dropped_at,
543 })
544 } else {
545 ccx.tcx.sess.create_feature_err(
546 errors::LiveDrop {
547 span,
548 dropped_ty: self.dropped_ty,
549 kind: ccx.const_kind(),
550 dropped_at: self.dropped_at,
551 },
552 sym::const_destruct,
553 )
554 }
555 }
556}
557
558#[derive(Debug)]
559pub(crate) struct EscapingCellBorrow;
563impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
564 fn importance(&self) -> DiagImportance {
565 DiagImportance::Secondary
568 }
569 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
570 ccx.dcx().create_err(errors::InteriorMutableBorrowEscaping { span, kind: ccx.const_kind() })
571 }
572}
573
574#[derive(Debug)]
575pub(crate) struct EscapingMutBorrow;
579
580impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
581 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
582 Status::Forbidden
583 }
584
585 fn importance(&self) -> DiagImportance {
586 DiagImportance::Secondary
589 }
590
591 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
592 ccx.dcx().create_err(errors::MutableBorrowEscaping { span, kind: ccx.const_kind() })
593 }
594}
595
596#[derive(Debug)]
598pub(crate) struct PanicNonStr;
599impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
600 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
601 ccx.dcx().create_err(errors::PanicNonStrErr { span })
602 }
603}
604
605#[derive(Debug)]
609pub(crate) struct RawPtrComparison;
610impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
611 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
612 ccx.dcx().create_err(errors::RawPtrComparisonErr { span })
614 }
615}
616
617#[derive(Debug)]
621pub(crate) struct RawPtrToIntCast;
622impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
623 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
624 ccx.dcx().create_err(errors::RawPtrToIntErr { span })
625 }
626}
627
628#[derive(Debug)]
630pub(crate) struct ThreadLocalAccess;
631impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
632 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
633 ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
634 }
635}