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