]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
Auto merge of #95993 - jyn514:fix-stage0-doctests, r=Mark-Simulacrum
[rust.git] / compiler / rustc_trait_selection / src / traits / const_evaluatable.rs
1 //! Checking that constant values used in types can be successfully evaluated.
2 //!
3 //! For concrete constants, this is fairly simple as we can just try and evaluate it.
4 //!
5 //! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`,
6 //! this is not as easy.
7 //!
8 //! In this case we try to build an abstract representation of this constant using
9 //! `thir_abstract_const` which can then be checked for structural equality with other
10 //! generic constants mentioned in the `caller_bounds` of the current environment.
11 use rustc_errors::ErrorGuaranteed;
12 use rustc_hir::def::DefKind;
13 use rustc_index::vec::IndexVec;
14 use rustc_infer::infer::InferCtxt;
15 use rustc_middle::mir;
16 use rustc_middle::mir::interpret::{
17     ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar,
18 };
19 use rustc_middle::thir;
20 use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable};
21 use rustc_middle::ty::subst::{Subst, SubstsRef};
22 use rustc_middle::ty::{self, DelaySpanBugEmitted, TyCtxt, TypeFoldable};
23 use rustc_session::lint;
24 use rustc_span::def_id::LocalDefId;
25 use rustc_span::Span;
26
27 use std::cmp;
28 use std::iter;
29 use std::ops::ControlFlow;
30
31 /// Check if a given constant can be evaluated.
32 #[instrument(skip(infcx), level = "debug")]
33 pub fn is_const_evaluatable<'cx, 'tcx>(
34     infcx: &InferCtxt<'cx, 'tcx>,
35     uv: ty::Unevaluated<'tcx, ()>,
36     param_env: ty::ParamEnv<'tcx>,
37     span: Span,
38 ) -> Result<(), NotConstEvaluatable> {
39     let tcx = infcx.tcx;
40
41     if tcx.features().generic_const_exprs {
42         match AbstractConst::new(tcx, uv)? {
43             // We are looking at a generic abstract constant.
44             Some(ct) => {
45                 if satisfied_from_param_env(tcx, ct, param_env)? {
46                     return Ok(());
47                 }
48
49                 // We were unable to unify the abstract constant with
50                 // a constant found in the caller bounds, there are
51                 // now three possible cases here.
52                 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
53                 enum FailureKind {
54                     /// The abstract const still references an inference
55                     /// variable, in this case we return `TooGeneric`.
56                     MentionsInfer,
57                     /// The abstract const references a generic parameter,
58                     /// this means that we emit an error here.
59                     MentionsParam,
60                     /// The substs are concrete enough that we can simply
61                     /// try and evaluate the given constant.
62                     Concrete,
63                 }
64                 let mut failure_kind = FailureKind::Concrete;
65                 walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) {
66                     Node::Leaf(leaf) => {
67                         if leaf.has_infer_types_or_consts() {
68                             failure_kind = FailureKind::MentionsInfer;
69                         } else if leaf.has_param_types_or_consts() {
70                             failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
71                         }
72
73                         ControlFlow::CONTINUE
74                     }
75                     Node::Cast(_, _, ty) => {
76                         if ty.has_infer_types_or_consts() {
77                             failure_kind = FailureKind::MentionsInfer;
78                         } else if ty.has_param_types_or_consts() {
79                             failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
80                         }
81
82                         ControlFlow::CONTINUE
83                     }
84                     Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {
85                         ControlFlow::CONTINUE
86                     }
87                 });
88
89                 match failure_kind {
90                     FailureKind::MentionsInfer => {
91                         return Err(NotConstEvaluatable::MentionsInfer);
92                     }
93                     FailureKind::MentionsParam => {
94                         return Err(NotConstEvaluatable::MentionsParam);
95                     }
96                     FailureKind::Concrete => {
97                         // Dealt with below by the same code which handles this
98                         // without the feature gate.
99                     }
100                 }
101             }
102             None => {
103                 // If we are dealing with a concrete constant, we can
104                 // reuse the old code path and try to evaluate
105                 // the constant.
106             }
107         }
108     }
109
110     let future_compat_lint = || {
111         if let Some(local_def_id) = uv.def.did.as_local() {
112             infcx.tcx.struct_span_lint_hir(
113                 lint::builtin::CONST_EVALUATABLE_UNCHECKED,
114                 infcx.tcx.hir().local_def_id_to_hir_id(local_def_id),
115                 span,
116                 |err| {
117                     err.build("cannot use constants which depend on generic parameters in types")
118                         .emit();
119                 },
120             );
121         }
122     };
123
124     // FIXME: We should only try to evaluate a given constant here if it is fully concrete
125     // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
126     //
127     // We previously did not check this, so we only emit a future compat warning if
128     // const evaluation succeeds and the given constant is still polymorphic for now
129     // and hopefully soon change this to an error.
130     //
131     // See #74595 for more details about this.
132     let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
133
134     if concrete.is_ok() && uv.substs.has_param_types_or_consts() {
135         match infcx.tcx.def_kind(uv.def.did) {
136             DefKind::AnonConst | DefKind::InlineConst => {
137                 let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def);
138
139                 if mir_body.is_polymorphic {
140                     future_compat_lint();
141                 }
142             }
143             _ => future_compat_lint(),
144         }
145     }
146
147     // If we're evaluating a foreign constant, under a nightly compiler without generic
148     // const exprs, AND it would've passed if that expression had been evaluated with
149     // generic const exprs, then suggest using generic const exprs.
150     if concrete.is_err()
151         && tcx.sess.is_nightly_build()
152         && !uv.def.did.is_local()
153         && !tcx.features().generic_const_exprs
154         && let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
155         && satisfied_from_param_env(tcx, ct, param_env) == Ok(true)
156     {
157         tcx.sess
158             .struct_span_fatal(
159                 // Slightly better span than just using `span` alone
160                 if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
161                 "failed to evaluate generic const expression",
162             )
163             .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
164             .span_suggestion_verbose(
165                 rustc_span::DUMMY_SP,
166                 "consider enabling this feature",
167                 "#![feature(generic_const_exprs)]\n".to_string(),
168                 rustc_errors::Applicability::MaybeIncorrect,
169             )
170             .emit()
171     }
172
173     debug!(?concrete, "is_const_evaluatable");
174     match concrete {
175         Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
176             true => NotConstEvaluatable::MentionsInfer,
177             false => NotConstEvaluatable::MentionsParam,
178         }),
179         Err(ErrorHandled::Linted) => {
180             let reported =
181                 infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint");
182             Err(NotConstEvaluatable::Error(reported))
183         }
184         Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
185         Ok(_) => Ok(()),
186     }
187 }
188
189 #[instrument(skip(tcx), level = "debug")]
190 fn satisfied_from_param_env<'tcx>(
191     tcx: TyCtxt<'tcx>,
192     ct: AbstractConst<'tcx>,
193     param_env: ty::ParamEnv<'tcx>,
194 ) -> Result<bool, NotConstEvaluatable> {
195     for pred in param_env.caller_bounds() {
196         match pred.kind().skip_binder() {
197             ty::PredicateKind::ConstEvaluatable(uv) => {
198                 if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
199                     let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
200
201                     // Try to unify with each subtree in the AbstractConst to allow for
202                     // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
203                     // predicate for `(N + 1) * 2`
204                     let result = walk_abstract_const(tcx, b_ct, |b_ct| {
205                         match const_unify_ctxt.try_unify(ct, b_ct) {
206                             true => ControlFlow::BREAK,
207                             false => ControlFlow::CONTINUE,
208                         }
209                     });
210
211                     if let ControlFlow::Break(()) = result {
212                         debug!("is_const_evaluatable: abstract_const ~~> ok");
213                         return Ok(true);
214                     }
215                 }
216             }
217             _ => {} // don't care
218         }
219     }
220
221     Ok(false)
222 }
223
224 /// A tree representing an anonymous constant.
225 ///
226 /// This is only able to represent a subset of `MIR`,
227 /// and should not leak any information about desugarings.
228 #[derive(Debug, Clone, Copy)]
229 pub struct AbstractConst<'tcx> {
230     // FIXME: Consider adding something like `IndexSlice`
231     // and use this here.
232     inner: &'tcx [Node<'tcx>],
233     substs: SubstsRef<'tcx>,
234 }
235
236 impl<'tcx> AbstractConst<'tcx> {
237     pub fn new(
238         tcx: TyCtxt<'tcx>,
239         uv: ty::Unevaluated<'tcx, ()>,
240     ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
241         let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
242         debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
243         Ok(inner.map(|inner| AbstractConst { inner, substs: uv.substs }))
244     }
245
246     pub fn from_const(
247         tcx: TyCtxt<'tcx>,
248         ct: ty::Const<'tcx>,
249     ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
250         match ct.val() {
251             ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()),
252             ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported),
253             _ => Ok(None),
254         }
255     }
256
257     #[inline]
258     pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
259         AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
260     }
261
262     #[inline]
263     pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
264         let node = self.inner.last().copied().unwrap();
265         match node {
266             Node::Leaf(leaf) => Node::Leaf(leaf.subst(tcx, self.substs)),
267             Node::Cast(kind, operand, ty) => Node::Cast(kind, operand, ty.subst(tcx, self.substs)),
268             // Don't perform substitution on the following as they can't directly contain generic params
269             Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
270         }
271     }
272 }
273
274 struct AbstractConstBuilder<'a, 'tcx> {
275     tcx: TyCtxt<'tcx>,
276     body_id: thir::ExprId,
277     body: &'a thir::Thir<'tcx>,
278     /// The current WIP node tree.
279     nodes: IndexVec<NodeId, Node<'tcx>>,
280 }
281
282 impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
283     fn root_span(&self) -> Span {
284         self.body.exprs[self.body_id].span
285     }
286
287     fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
288         let reported = self
289             .tcx
290             .sess
291             .struct_span_err(self.root_span(), "overly complex generic constant")
292             .span_label(span, msg)
293             .help("consider moving this anonymous constant into a `const` function")
294             .emit();
295
296         Err(reported)
297     }
298     fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
299         let reported = self
300             .tcx
301             .sess
302             .struct_span_err(self.root_span(), "overly complex generic constant")
303             .span_label(span, msg)
304             .help("consider moving this anonymous constant into a `const` function")
305             .note("this operation may be supported in the future")
306             .emit();
307
308         Err(reported)
309     }
310
311     #[instrument(skip(tcx, body, body_id), level = "debug")]
312     fn new(
313         tcx: TyCtxt<'tcx>,
314         (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId),
315     ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> {
316         let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
317
318         struct IsThirPolymorphic<'a, 'tcx> {
319             is_poly: bool,
320             thir: &'a thir::Thir<'tcx>,
321         }
322
323         use crate::rustc_middle::thir::visit::Visitor;
324         use thir::visit;
325
326         impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
327             fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
328                 if expr.ty.has_param_types_or_consts() {
329                     return true;
330                 }
331
332                 match expr.kind {
333                     thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(),
334                     thir::ExprKind::ConstParam { .. } => true,
335                     thir::ExprKind::Repeat { value, count } => {
336                         self.visit_expr(&self.thir()[value]);
337                         count.has_param_types_or_consts()
338                     }
339                     _ => false,
340                 }
341             }
342
343             fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
344                 if pat.ty.has_param_types_or_consts() {
345                     return true;
346                 }
347
348                 match pat.kind.as_ref() {
349                     thir::PatKind::Constant { value } => value.has_param_types_or_consts(),
350                     thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => {
351                         lo.has_param_types_or_consts() || hi.has_param_types_or_consts()
352                     }
353                     _ => false,
354                 }
355             }
356         }
357
358         impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
359             fn thir(&self) -> &'a thir::Thir<'tcx> {
360                 &self.thir
361             }
362
363             #[instrument(skip(self), level = "debug")]
364             fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
365                 self.is_poly |= self.expr_is_poly(expr);
366                 if !self.is_poly {
367                     visit::walk_expr(self, expr)
368                 }
369             }
370
371             #[instrument(skip(self), level = "debug")]
372             fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
373                 self.is_poly |= self.pat_is_poly(pat);
374                 if !self.is_poly {
375                     visit::walk_pat(self, pat);
376                 }
377             }
378         }
379
380         let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
381         visit::walk_expr(&mut is_poly_vis, &body[body_id]);
382         debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly);
383         if !is_poly_vis.is_poly {
384             return Ok(None);
385         }
386
387         Ok(Some(builder))
388     }
389
390     /// We do not allow all binary operations in abstract consts, so filter disallowed ones.
391     fn check_binop(op: mir::BinOp) -> bool {
392         use mir::BinOp::*;
393         match op {
394             Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
395             | Ne | Ge | Gt => true,
396             Offset => false,
397         }
398     }
399
400     /// While we currently allow all unary operations, we still want to explicitly guard against
401     /// future changes here.
402     fn check_unop(op: mir::UnOp) -> bool {
403         use mir::UnOp::*;
404         match op {
405             Not | Neg => true,
406         }
407     }
408
409     /// Builds the abstract const by walking the thir and bailing out when
410     /// encountering an unsupported operation.
411     fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> {
412         debug!("Abstractconstbuilder::build: body={:?}", &*self.body);
413         self.recurse_build(self.body_id)?;
414
415         for n in self.nodes.iter() {
416             if let Node::Leaf(ct) = n {
417                 if let ty::ConstKind::Unevaluated(ct) = ct.val() {
418                     // `AbstractConst`s should not contain any promoteds as they require references which
419                     // are not allowed.
420                     assert_eq!(ct.promoted, None);
421                 }
422             }
423         }
424
425         Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter()))
426     }
427
428     fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> {
429         use thir::ExprKind;
430         let node = &self.body.exprs[node];
431         Ok(match &node.kind {
432             // I dont know if handling of these 3 is correct
433             &ExprKind::Scope { value, .. } => self.recurse_build(value)?,
434             &ExprKind::PlaceTypeAscription { source, .. }
435             | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?,
436             &ExprKind::Literal { lit, neg} => {
437                 let sp = node.span;
438                 let constant =
439                     match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
440                         Ok(c) => c,
441                         Err(LitToConstError::Reported) => {
442                             self.tcx.const_error(node.ty)
443                         }
444                         Err(LitToConstError::TypeError) => {
445                             bug!("encountered type error in lit_to_const")
446                         }
447                     };
448
449                 self.nodes.push(Node::Leaf(constant))
450             }
451             &ExprKind::NonHirLiteral { lit , user_ty: _} => {
452                 // FIXME Construct a Valtree from this ScalarInt when introducing Valtrees
453                 let const_value = ConstValue::Scalar(Scalar::Int(lit));
454                 self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, const_value, node.ty)))
455             }
456             &ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
457                 let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
458
459                 let constant = self.tcx.mk_const(ty::ConstS {
460                                 val: ty::ConstKind::Unevaluated(uneval),
461                                 ty: node.ty,
462                             });
463
464                 self.nodes.push(Node::Leaf(constant))
465             }
466
467             ExprKind::ConstParam {param, ..} => {
468                 let const_param = self.tcx.mk_const(ty::ConstS {
469                         val: ty::ConstKind::Param(*param),
470                         ty: node.ty,
471                     });
472                 self.nodes.push(Node::Leaf(const_param))
473             }
474
475             ExprKind::Call { fun, args, .. } => {
476                 let fun = self.recurse_build(*fun)?;
477
478                 let mut new_args = Vec::<NodeId>::with_capacity(args.len());
479                 for &id in args.iter() {
480                     new_args.push(self.recurse_build(id)?);
481                 }
482                 let new_args = self.tcx.arena.alloc_slice(&new_args);
483                 self.nodes.push(Node::FunctionCall(fun, new_args))
484             }
485             &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => {
486                 let lhs = self.recurse_build(lhs)?;
487                 let rhs = self.recurse_build(rhs)?;
488                 self.nodes.push(Node::Binop(op, lhs, rhs))
489             }
490             &ExprKind::Unary { op, arg } if Self::check_unop(op) => {
491                 let arg = self.recurse_build(arg)?;
492                 self.nodes.push(Node::UnaryOp(op, arg))
493             }
494             // This is necessary so that the following compiles:
495             //
496             // ```
497             // fn foo<const N: usize>(a: [(); N + 1]) {
498             //     bar::<{ N + 1 }>();
499             // }
500             // ```
501             ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => {
502                 self.recurse_build(*e)?
503             }
504             // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
505             // "coercion cast" i.e. using a coercion or is a no-op.
506             // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
507             &ExprKind::Use { source } => {
508                 let arg = self.recurse_build(source)?;
509                 self.nodes.push(Node::Cast(abstract_const::CastKind::Use, arg, node.ty))
510             }
511             &ExprKind::Cast { source } => {
512                 let arg = self.recurse_build(source)?;
513                 self.nodes.push(Node::Cast(abstract_const::CastKind::As, arg, node.ty))
514             }
515             ExprKind::Borrow{ arg, ..} => {
516                 let arg_node = &self.body.exprs[*arg];
517
518                 // Skip reborrows for now until we allow Deref/Borrow/AddressOf
519                 // expressions.
520                 // FIXME(generic_const_exprs): Verify/explain why this is sound
521                 if let ExprKind::Deref {arg} = arg_node.kind {
522                     self.recurse_build(arg)?
523                 } else {
524                     self.maybe_supported_error(
525                         node.span,
526                         "borrowing is not supported in generic constants",
527                     )?
528                 }
529             }
530             // FIXME(generic_const_exprs): We may want to support these.
531             ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error(
532                 node.span,
533                 "dereferencing or taking the address is not supported in generic constants",
534             )?,
535             ExprKind::Repeat { .. } | ExprKind::Array { .. } =>  self.maybe_supported_error(
536                 node.span,
537                 "array construction is not supported in generic constants",
538             )?,
539             ExprKind::Block { .. } => self.maybe_supported_error(
540                 node.span,
541                 "blocks are not supported in generic constant",
542             )?,
543             ExprKind::NeverToAny { .. } => self.maybe_supported_error(
544                 node.span,
545                 "converting nevers to any is not supported in generic constant",
546             )?,
547             ExprKind::Tuple { .. } => self.maybe_supported_error(
548                 node.span,
549                 "tuple construction is not supported in generic constants",
550             )?,
551             ExprKind::Index { .. } => self.maybe_supported_error(
552                 node.span,
553                 "indexing is not supported in generic constant",
554             )?,
555             ExprKind::Field { .. } => self.maybe_supported_error(
556                 node.span,
557                 "field access is not supported in generic constant",
558             )?,
559             ExprKind::ConstBlock { .. } => self.maybe_supported_error(
560                 node.span,
561                 "const blocks are not supported in generic constant",
562             )?,
563             ExprKind::Adt(_) => self.maybe_supported_error(
564                 node.span,
565                 "struct/enum construction is not supported in generic constants",
566             )?,
567             // dont know if this is correct
568             ExprKind::Pointer { .. } =>
569                 self.error(node.span, "pointer casts are not allowed in generic constants")?,
570             ExprKind::Yield { .. } =>
571                 self.error(node.span, "generator control flow is not allowed in generic constants")?,
572             ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self
573                 .error(
574                     node.span,
575                     "loops and loop control flow are not supported in generic constants",
576                 )?,
577             ExprKind::Box { .. } =>
578                 self.error(node.span, "allocations are not allowed in generic constants")?,
579
580             ExprKind::Unary { .. } => unreachable!(),
581             // we handle valid unary/binary ops above
582             ExprKind::Binary { .. } =>
583                 self.error(node.span, "unsupported binary operation in generic constants")?,
584             ExprKind::LogicalOp { .. } =>
585                 self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?,
586             ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
587                 self.error(node.span, "assignment is not supported in generic constants")?
588             }
589             ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error(
590                 node.span,
591                 "closures and function keywords are not supported in generic constants",
592             )?,
593             // let expressions imply control flow
594             ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } =>
595                 self.error(node.span, "control flow is not supported in generic constants")?,
596             ExprKind::InlineAsm { .. } => {
597                 self.error(node.span, "assembly is not supported in generic constants")?
598             }
599
600             // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
601             ExprKind::VarRef { .. }
602             | ExprKind::UpvarRef { .. }
603             | ExprKind::StaticRef { .. }
604             | ExprKind::ThreadLocalRef(_) => {
605                 self.error(node.span, "unsupported operation in generic constant")?
606             }
607         })
608     }
609 }
610
611 /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
612 pub(super) fn thir_abstract_const<'tcx>(
613     tcx: TyCtxt<'tcx>,
614     def: ty::WithOptConstParam<LocalDefId>,
615 ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
616     if tcx.features().generic_const_exprs {
617         match tcx.def_kind(def.did) {
618             // FIXME(generic_const_exprs): We currently only do this for anonymous constants,
619             // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
620             // we want to look into them or treat them as opaque projections.
621             //
622             // Right now we do neither of that and simply always fail to unify them.
623             DefKind::AnonConst | DefKind::InlineConst => (),
624             _ => return Ok(None),
625         }
626
627         let body = tcx.thir_body(def)?;
628
629         AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
630             .map(AbstractConstBuilder::build)
631             .transpose()
632     } else {
633         Ok(None)
634     }
635 }
636
637 #[instrument(skip(tcx), level = "debug")]
638 pub(super) fn try_unify_abstract_consts<'tcx>(
639     tcx: TyCtxt<'tcx>,
640     (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
641     param_env: ty::ParamEnv<'tcx>,
642 ) -> bool {
643     (|| {
644         if let Some(a) = AbstractConst::new(tcx, a)? {
645             if let Some(b) = AbstractConst::new(tcx, b)? {
646                 let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
647                 return Ok(const_unify_ctxt.try_unify(a, b));
648             }
649         }
650
651         Ok(false)
652     })()
653     .unwrap_or_else(|_: ErrorGuaranteed| true)
654     // FIXME(generic_const_exprs): We should instead have this
655     // method return the resulting `ty::Const` and return `ConstKind::Error`
656     // on `ErrorGuaranteed`.
657 }
658
659 #[instrument(skip(tcx, f), level = "debug")]
660 pub fn walk_abstract_const<'tcx, R, F>(
661     tcx: TyCtxt<'tcx>,
662     ct: AbstractConst<'tcx>,
663     mut f: F,
664 ) -> ControlFlow<R>
665 where
666     F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
667 {
668     #[instrument(skip(tcx, f), level = "debug")]
669     fn recurse<'tcx, R>(
670         tcx: TyCtxt<'tcx>,
671         ct: AbstractConst<'tcx>,
672         f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
673     ) -> ControlFlow<R> {
674         f(ct)?;
675         let root = ct.root(tcx);
676         debug!(?root);
677         match root {
678             Node::Leaf(_) => ControlFlow::CONTINUE,
679             Node::Binop(_, l, r) => {
680                 recurse(tcx, ct.subtree(l), f)?;
681                 recurse(tcx, ct.subtree(r), f)
682             }
683             Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f),
684             Node::FunctionCall(func, args) => {
685                 recurse(tcx, ct.subtree(func), f)?;
686                 args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f))
687             }
688             Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
689         }
690     }
691
692     recurse(tcx, ct, &mut f)
693 }
694
695 struct ConstUnifyCtxt<'tcx> {
696     tcx: TyCtxt<'tcx>,
697     param_env: ty::ParamEnv<'tcx>,
698 }
699
700 impl<'tcx> ConstUnifyCtxt<'tcx> {
701     // Substitutes generics repeatedly to allow AbstractConsts to unify where a
702     // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g.
703     // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
704     #[inline]
705     #[instrument(skip(self), level = "debug")]
706     fn try_replace_substs_in_root(
707         &self,
708         mut abstr_const: AbstractConst<'tcx>,
709     ) -> Option<AbstractConst<'tcx>> {
710         while let Node::Leaf(ct) = abstr_const.root(self.tcx) {
711             match AbstractConst::from_const(self.tcx, ct) {
712                 Ok(Some(act)) => abstr_const = act,
713                 Ok(None) => break,
714                 Err(_) => return None,
715             }
716         }
717
718         Some(abstr_const)
719     }
720
721     /// Tries to unify two abstract constants using structural equality.
722     #[instrument(skip(self), level = "debug")]
723     fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool {
724         let a = if let Some(a) = self.try_replace_substs_in_root(a) {
725             a
726         } else {
727             return true;
728         };
729
730         let b = if let Some(b) = self.try_replace_substs_in_root(b) {
731             b
732         } else {
733             return true;
734         };
735
736         let a_root = a.root(self.tcx);
737         let b_root = b.root(self.tcx);
738         debug!(?a_root, ?b_root);
739
740         match (a_root, b_root) {
741             (Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
742                 let a_ct = a_ct.eval(self.tcx, self.param_env);
743                 debug!("a_ct evaluated: {:?}", a_ct);
744                 let b_ct = b_ct.eval(self.tcx, self.param_env);
745                 debug!("b_ct evaluated: {:?}", b_ct);
746
747                 if a_ct.ty() != b_ct.ty() {
748                     return false;
749                 }
750
751                 match (a_ct.val(), b_ct.val()) {
752                     // We can just unify errors with everything to reduce the amount of
753                     // emitted errors here.
754                     (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
755                     (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
756                         a_param == b_param
757                     }
758                     (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
759                     // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
760                     // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
761                     // means that we only allow inference variables if they are equal.
762                     (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
763                     // We expand generic anonymous constants at the start of this function, so this
764                     // branch should only be taking when dealing with associated constants, at
765                     // which point directly comparing them seems like the desired behavior.
766                     //
767                     // FIXME(generic_const_exprs): This isn't actually the case.
768                     // We also take this branch for concrete anonymous constants and
769                     // expand generic anonymous constants with concrete substs.
770                     (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
771                         a_uv == b_uv
772                     }
773                     // FIXME(generic_const_exprs): We may want to either actually try
774                     // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
775                     // this, for now we just return false here.
776                     _ => false,
777                 }
778             }
779             (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
780                 self.try_unify(a.subtree(al), b.subtree(bl))
781                     && self.try_unify(a.subtree(ar), b.subtree(br))
782             }
783             (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
784                 self.try_unify(a.subtree(av), b.subtree(bv))
785             }
786             (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
787                 if a_args.len() == b_args.len() =>
788             {
789                 self.try_unify(a.subtree(a_f), b.subtree(b_f))
790                     && iter::zip(a_args, b_args)
791                         .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn)))
792             }
793             (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
794                 if (a_ty == b_ty) && (a_kind == b_kind) =>
795             {
796                 self.try_unify(a.subtree(a_operand), b.subtree(b_operand))
797             }
798             // use this over `_ => false` to make adding variants to `Node` less error prone
799             (Node::Cast(..), _)
800             | (Node::FunctionCall(..), _)
801             | (Node::UnaryOp(..), _)
802             | (Node::Binop(..), _)
803             | (Node::Leaf(..), _) => false,
804         }
805     }
806 }