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