1 //! Checking that constant values used in types can be successfully evaluated.
3 //! For concrete constants, this is fairly simple as we can just try and evaluate it.
5 //! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`,
6 //! this is not as easy.
8 //! In this case we try to build an abstract representation of this constant using
9 //! `mir_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
::ErrorReported
;
12 use rustc_hir
::def
::DefKind
;
13 use rustc_index
::bit_set
::BitSet
;
14 use rustc_index
::vec
::IndexVec
;
15 use rustc_infer
::infer
::InferCtxt
;
16 use rustc_middle
::mir
::abstract_const
::{Node, NodeId, NotConstEvaluatable}
;
17 use rustc_middle
::mir
::interpret
::ErrorHandled
;
18 use rustc_middle
::mir
::{self, Rvalue, StatementKind, TerminatorKind}
;
19 use rustc_middle
::ty
::subst
::{Subst, SubstsRef}
;
20 use rustc_middle
::ty
::{self, TyCtxt, TypeFoldable}
;
21 use rustc_session
::lint
;
22 use rustc_span
::def_id
::{DefId, LocalDefId}
;
27 use std
::ops
::ControlFlow
;
29 /// Check if a given constant can be evaluated.
30 pub fn is_const_evaluatable
<'cx
, 'tcx
>(
31 infcx
: &InferCtxt
<'cx
, 'tcx
>,
32 def
: ty
::WithOptConstParam
<DefId
>,
33 substs
: SubstsRef
<'tcx
>,
34 param_env
: ty
::ParamEnv
<'tcx
>,
36 ) -> Result
<(), NotConstEvaluatable
> {
37 debug
!("is_const_evaluatable({:?}, {:?})", def
, substs
);
38 if infcx
.tcx
.features().const_evaluatable_checked
{
40 match AbstractConst
::new(tcx
, def
, substs
)?
{
41 // We are looking at a generic abstract constant.
43 for pred
in param_env
.caller_bounds() {
44 match pred
.kind().skip_binder() {
45 ty
::PredicateKind
::ConstEvaluatable(b_def
, b_substs
) => {
46 if b_def
== def
&& b_substs
== substs
{
47 debug
!("is_const_evaluatable: caller_bound ~~> ok");
51 if let Some(b_ct
) = AbstractConst
::new(tcx
, b_def
, b_substs
)?
{
52 // Try to unify with each subtree in the AbstractConst to allow for
53 // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
54 // predicate for `(N + 1) * 2`
56 walk_abstract_const(tcx
, b_ct
, |b_ct
| {
57 match try_unify(tcx
, ct
, b_ct
) {
58 true => ControlFlow
::BREAK
,
59 false => ControlFlow
::CONTINUE
,
63 if let ControlFlow
::Break(()) = result
{
64 debug
!("is_const_evaluatable: abstract_const ~~> ok");
73 // We were unable to unify the abstract constant with
74 // a constant found in the caller bounds, there are
75 // now three possible cases here.
76 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
78 /// The abstract const still references an inference
79 /// variable, in this case we return `TooGeneric`.
81 /// The abstract const references a generic parameter,
82 /// this means that we emit an error here.
84 /// The substs are concrete enough that we can simply
85 /// try and evaluate the given constant.
88 let mut failure_kind
= FailureKind
::Concrete
;
89 walk_abstract_const
::<!, _
>(tcx
, ct
, |node
| match node
.root() {
91 let leaf
= leaf
.subst(tcx
, ct
.substs
);
92 if leaf
.has_infer_types_or_consts() {
93 failure_kind
= FailureKind
::MentionsInfer
;
94 } else if leaf
.has_param_types_or_consts() {
95 failure_kind
= cmp
::min(failure_kind
, FailureKind
::MentionsParam
);
100 Node
::Cast(_
, _
, ty
) => {
101 let ty
= ty
.subst(tcx
, ct
.substs
);
102 if ty
.has_infer_types_or_consts() {
103 failure_kind
= FailureKind
::MentionsInfer
;
104 } else if ty
.has_param_types_or_consts() {
105 failure_kind
= cmp
::min(failure_kind
, FailureKind
::MentionsParam
);
108 ControlFlow
::CONTINUE
110 Node
::Binop(_
, _
, _
) | Node
::UnaryOp(_
, _
) | Node
::FunctionCall(_
, _
) => {
111 ControlFlow
::CONTINUE
116 FailureKind
::MentionsInfer
=> {
117 return Err(NotConstEvaluatable
::MentionsInfer
);
119 FailureKind
::MentionsParam
=> {
120 return Err(NotConstEvaluatable
::MentionsParam
);
122 FailureKind
::Concrete
=> {
123 // Dealt with below by the same code which handles this
124 // without the feature gate.
129 // If we are dealing with a concrete constant, we can
130 // reuse the old code path and try to evaluate
136 let future_compat_lint
= || {
137 if let Some(local_def_id
) = def
.did
.as_local() {
138 infcx
.tcx
.struct_span_lint_hir(
139 lint
::builtin
::CONST_EVALUATABLE_UNCHECKED
,
140 infcx
.tcx
.hir().local_def_id_to_hir_id(local_def_id
),
143 err
.build("cannot use constants which depend on generic parameters in types")
150 // FIXME: We should only try to evaluate a given constant here if it is fully concrete
151 // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
153 // We previously did not check this, so we only emit a future compat warning if
154 // const evaluation succeeds and the given constant is still polymorphic for now
155 // and hopefully soon change this to an error.
157 // See #74595 for more details about this.
158 let concrete
= infcx
.const_eval_resolve(
160 ty
::Unevaluated { def, substs, promoted: None }
,
164 if concrete
.is_ok() && substs
.has_param_types_or_consts() {
165 match infcx
.tcx
.def_kind(def
.did
) {
166 DefKind
::AnonConst
=> {
167 let mir_body
= infcx
.tcx
.mir_for_ctfe_opt_const_arg(def
);
169 if mir_body
.is_polymorphic
{
170 future_compat_lint();
173 _
=> future_compat_lint(),
177 debug
!(?concrete
, "is_const_evaluatable");
179 Err(ErrorHandled
::TooGeneric
) => Err(match substs
.has_infer_types_or_consts() {
180 true => NotConstEvaluatable
::MentionsInfer
,
181 false => NotConstEvaluatable
::MentionsParam
,
183 Err(ErrorHandled
::Linted
) => {
184 infcx
.tcx
.sess
.delay_span_bug(span
, "constant in type had error reported as lint");
185 Err(NotConstEvaluatable
::Error(ErrorReported
))
187 Err(ErrorHandled
::Reported(e
)) => Err(NotConstEvaluatable
::Error(e
)),
192 /// A tree representing an anonymous constant.
194 /// This is only able to represent a subset of `MIR`,
195 /// and should not leak any information about desugarings.
196 #[derive(Debug, Clone, Copy)]
197 pub struct AbstractConst
<'tcx
> {
198 // FIXME: Consider adding something like `IndexSlice`
199 // and use this here.
200 pub inner
: &'tcx
[Node
<'tcx
>],
201 pub substs
: SubstsRef
<'tcx
>,
204 impl AbstractConst
<'tcx
> {
207 def
: ty
::WithOptConstParam
<DefId
>,
208 substs
: SubstsRef
<'tcx
>,
209 ) -> Result
<Option
<AbstractConst
<'tcx
>>, ErrorReported
> {
210 let inner
= tcx
.mir_abstract_const_opt_const_arg(def
)?
;
211 debug
!("AbstractConst::new({:?}) = {:?}", def
, inner
);
212 Ok(inner
.map(|inner
| AbstractConst { inner, substs }
))
217 ct
: &ty
::Const
<'tcx
>,
218 ) -> Result
<Option
<AbstractConst
<'tcx
>>, ErrorReported
> {
220 ty
::ConstKind
::Unevaluated(ty
::Unevaluated { def, substs, promoted: _ }
) => {
221 AbstractConst
::new(tcx
, def
, substs
)
223 ty
::ConstKind
::Error(_
) => Err(ErrorReported
),
229 pub fn subtree(self, node
: NodeId
) -> AbstractConst
<'tcx
> {
230 AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
234 pub fn root(self) -> Node
<'tcx
> {
235 self.inner
.last().copied().unwrap()
239 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
240 struct WorkNode
<'tcx
> {
246 struct AbstractConstBuilder
<'a
, 'tcx
> {
248 body
: &'a mir
::Body
<'tcx
>,
249 /// The current WIP node tree.
251 /// We require all nodes to be used in the final abstract const,
252 /// so we store this here. Note that we also consider nodes as used
253 /// if they are mentioned in an assert, so some used nodes are never
254 /// actually reachable by walking the [`AbstractConst`].
255 nodes
: IndexVec
<NodeId
, WorkNode
<'tcx
>>,
256 locals
: IndexVec
<mir
::Local
, NodeId
>,
257 /// We only allow field accesses if they access
258 /// the result of a checked operation.
259 checked_op_locals
: BitSet
<mir
::Local
>,
262 impl<'a
, 'tcx
> AbstractConstBuilder
<'a
, 'tcx
> {
263 fn error(&mut self, span
: Option
<Span
>, msg
: &str) -> Result
<!, ErrorReported
> {
266 .struct_span_err(self.body
.span
, "overly complex generic constant")
267 .span_label(span
.unwrap_or(self.body
.span
), msg
)
268 .help("consider moving this anonymous constant into a `const` function")
276 body
: &'a mir
::Body
<'tcx
>,
277 ) -> Result
<Option
<AbstractConstBuilder
<'a
, 'tcx
>>, ErrorReported
> {
278 let mut builder
= AbstractConstBuilder
{
281 nodes
: IndexVec
::new(),
282 locals
: IndexVec
::from_elem(NodeId
::MAX
, &body
.local_decls
),
283 checked_op_locals
: BitSet
::new_empty(body
.local_decls
.len()),
286 // We don't have to look at concrete constants, as we
287 // can just evaluate them.
288 if !body
.is_polymorphic
{
292 // We only allow consts without control flow, so
293 // we check for cycles here which simplifies the
294 // rest of this implementation.
295 if body
.is_cfg_cyclic() {
296 builder
.error(None
, "cyclic anonymous constants are forbidden")?
;
302 fn add_node(&mut self, node
: Node
<'tcx
>, span
: Span
) -> NodeId
{
306 Node
::Binop(_
, lhs
, rhs
) => {
307 self.nodes
[lhs
].used
= true;
308 self.nodes
[rhs
].used
= true;
310 Node
::UnaryOp(_
, input
) => {
311 self.nodes
[input
].used
= true;
313 Node
::FunctionCall(func
, nodes
) => {
314 self.nodes
[func
].used
= true;
315 nodes
.iter().for_each(|&n
| self.nodes
[n
].used
= true);
317 Node
::Cast(_
, operand
, _
) => {
318 self.nodes
[operand
].used
= true;
322 // Nodes start as unused.
323 self.nodes
.push(WorkNode { node, span, used: false }
)
329 p
: &mir
::Place
<'tcx
>,
330 ) -> Result
<mir
::Local
, ErrorReported
> {
331 const ZERO_FIELD
: mir
::Field
= mir
::Field
::from_usize(0);
332 // Do not allow any projections.
334 // One exception are field accesses on the result of checked operations,
335 // which are required to support things like `1 + 2`.
336 if let Some(p
) = p
.as_local() {
337 debug_assert
!(!self.checked_op_locals
.contains(p
));
339 } else if let &[mir
::ProjectionElem
::Field(ZERO_FIELD
, _
)] = p
.projection
.as_ref() {
340 // Only allow field accesses if the given local
341 // contains the result of a checked operation.
342 if self.checked_op_locals
.contains(p
.local
) {
345 self.error(Some(span
), "unsupported projection")?
;
348 self.error(Some(span
), "unsupported projection")?
;
355 op
: &mir
::Operand
<'tcx
>,
356 ) -> Result
<NodeId
, ErrorReported
> {
357 debug
!("operand_to_node: op={:?}", op
);
359 mir
::Operand
::Copy(p
) | mir
::Operand
::Move(p
) => {
360 let local
= self.place_to_local(span
, p
)?
;
361 Ok(self.locals
[local
])
363 mir
::Operand
::Constant(ct
) => match ct
.literal
{
364 mir
::ConstantKind
::Ty(ct
) => Ok(self.add_node(Node
::Leaf(ct
), span
)),
365 mir
::ConstantKind
::Val(..) => self.error(Some(span
), "unsupported constant")?
,
370 /// We do not allow all binary operations in abstract consts, so filter disallowed ones.
371 fn check_binop(op
: mir
::BinOp
) -> bool
{
374 Add
| Sub
| Mul
| Div
| Rem
| BitXor
| BitAnd
| BitOr
| Shl
| Shr
| Eq
| Lt
| Le
375 | Ne
| Ge
| Gt
=> true,
380 /// While we currently allow all unary operations, we still want to explicitly guard against
381 /// future changes here.
382 fn check_unop(op
: mir
::UnOp
) -> bool
{
389 fn build_statement(&mut self, stmt
: &mir
::Statement
<'tcx
>) -> Result
<(), ErrorReported
> {
390 debug
!("AbstractConstBuilder: stmt={:?}", stmt
);
391 let span
= stmt
.source_info
.span
;
393 StatementKind
::Assign(box (ref place
, ref rvalue
)) => {
394 let local
= self.place_to_local(span
, place
)?
;
396 Rvalue
::Use(ref operand
) => {
397 self.locals
[local
] = self.operand_to_node(span
, operand
)?
;
400 Rvalue
::BinaryOp(op
, box (ref lhs
, ref rhs
)) if Self::check_binop(op
) => {
401 let lhs
= self.operand_to_node(span
, lhs
)?
;
402 let rhs
= self.operand_to_node(span
, rhs
)?
;
403 self.locals
[local
] = self.add_node(Node
::Binop(op
, lhs
, rhs
), span
);
404 if op
.is_checkable() {
405 bug
!("unexpected unchecked checkable binary operation");
410 Rvalue
::CheckedBinaryOp(op
, box (ref lhs
, ref rhs
))
411 if Self::check_binop(op
) =>
413 let lhs
= self.operand_to_node(span
, lhs
)?
;
414 let rhs
= self.operand_to_node(span
, rhs
)?
;
415 self.locals
[local
] = self.add_node(Node
::Binop(op
, lhs
, rhs
), span
);
416 self.checked_op_locals
.insert(local
);
419 Rvalue
::UnaryOp(op
, ref operand
) if Self::check_unop(op
) => {
420 let operand
= self.operand_to_node(span
, operand
)?
;
421 self.locals
[local
] = self.add_node(Node
::UnaryOp(op
, operand
), span
);
424 Rvalue
::Cast(cast_kind
, ref operand
, ty
) => {
425 let operand
= self.operand_to_node(span
, operand
)?
;
427 self.add_node(Node
::Cast(cast_kind
, operand
, ty
), span
);
430 _
=> self.error(Some(span
), "unsupported rvalue")?
,
433 // These are not actually relevant for us here, so we can ignore them.
434 StatementKind
::AscribeUserType(..)
435 | StatementKind
::StorageLive(_
)
436 | StatementKind
::StorageDead(_
) => Ok(()),
437 _
=> self.error(Some(stmt
.source_info
.span
), "unsupported statement")?
,
441 /// Possible return values:
443 /// - `None`: unsupported terminator, stop building
444 /// - `Some(None)`: supported terminator, finish building
445 /// - `Some(Some(block))`: support terminator, build `block` next
448 terminator
: &mir
::Terminator
<'tcx
>,
449 ) -> Result
<Option
<mir
::BasicBlock
>, ErrorReported
> {
450 debug
!("AbstractConstBuilder: terminator={:?}", terminator
);
451 match terminator
.kind
{
452 TerminatorKind
::Goto { target }
=> Ok(Some(target
)),
453 TerminatorKind
::Return
=> Ok(None
),
454 TerminatorKind
::Call
{
457 destination
: Some((ref place
, target
)),
458 // We do not care about `cleanup` here. Any branch which
459 // uses `cleanup` will fail const-eval and they therefore
460 // do not matter when checking for const evaluatability.
462 // Do note that even if `panic::catch_unwind` is made const,
463 // we still do not have to care about this, as we do not look
466 // Do not allow overloaded operators for now,
467 // we probably do want to allow this in the future.
469 // This is currently fairly irrelevant as it requires `const Trait`s.
473 let local
= self.place_to_local(fn_span
, place
)?
;
474 let func
= self.operand_to_node(fn_span
, func
)?
;
475 let args
= self.tcx
.arena
.alloc_from_iter(
477 .map(|arg
| self.operand_to_node(terminator
.source_info
.span
, arg
))
478 .collect
::<Result
<Vec
<NodeId
>, _
>>()?
,
480 self.locals
[local
] = self.add_node(Node
::FunctionCall(func
, args
), fn_span
);
483 TerminatorKind
::Assert { ref cond, expected: false, target, .. }
=> {
485 mir
::Operand
::Copy(p
) | mir
::Operand
::Move(p
) => p
,
486 mir
::Operand
::Constant(_
) => bug
!("unexpected assert"),
489 const ONE_FIELD
: mir
::Field
= mir
::Field
::from_usize(1);
490 debug
!("proj: {:?}", p
.projection
);
491 if let Some(p
) = p
.as_local() {
492 debug_assert
!(!self.checked_op_locals
.contains(p
));
493 // Mark locals directly used in asserts as used.
495 // This is needed because division does not use `CheckedBinop` but instead
496 // adds an explicit assert for `divisor != 0`.
497 self.nodes
[self.locals
[p
]].used
= true;
498 return Ok(Some(target
));
499 } else if let &[mir
::ProjectionElem
::Field(ONE_FIELD
, _
)] = p
.projection
.as_ref() {
500 // Only allow asserts checking the result of a checked operation.
501 if self.checked_op_locals
.contains(p
.local
) {
502 return Ok(Some(target
));
506 self.error(Some(terminator
.source_info
.span
), "unsupported assertion")?
;
508 _
=> self.error(Some(terminator
.source_info
.span
), "unsupported terminator")?
,
512 /// Builds the abstract const by walking the mir from start to finish
513 /// and bailing out when encountering an unsupported operation.
514 fn build(mut self) -> Result
<&'tcx
[Node
<'tcx
>], ErrorReported
> {
515 let mut block
= &self.body
.basic_blocks()[mir
::START_BLOCK
];
516 // We checked for a cyclic cfg above, so this should terminate.
518 debug
!("AbstractConstBuilder: block={:?}", block
);
519 for stmt
in block
.statements
.iter() {
520 self.build_statement(stmt
)?
;
523 if let Some(next
) = self.build_terminator(block
.terminator())?
{
524 block
= &self.body
.basic_blocks()[next
];
530 assert_eq
!(self.locals
[mir
::RETURN_PLACE
], self.nodes
.last().unwrap());
531 for n
in self.nodes
.iter() {
532 if let Node
::Leaf(ty
::Const { val: ty::ConstKind::Unevaluated(ct), ty: _ }
) = n
.node
{
533 // `AbstractConst`s should not contain any promoteds as they require references which
535 assert_eq
!(ct
.promoted
, None
);
539 self.nodes
[self.locals
[mir
::RETURN_PLACE
]].used
= true;
540 if let Some(&unused
) = self.nodes
.iter().find(|n
| !n
.used
) {
541 self.error(Some(unused
.span
), "dead code")?
;
544 Ok(self.tcx
.arena
.alloc_from_iter(self.nodes
.into_iter().map(|n
| n
.node
)))
548 /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
549 pub(super) fn mir_abstract_const
<'tcx
>(
551 def
: ty
::WithOptConstParam
<LocalDefId
>,
552 ) -> Result
<Option
<&'tcx
[mir
::abstract_const
::Node
<'tcx
>]>, ErrorReported
> {
553 if tcx
.features().const_evaluatable_checked
{
554 match tcx
.def_kind(def
.did
) {
555 // FIXME(const_evaluatable_checked): We currently only do this for anonymous constants,
556 // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
557 // we want to look into them or treat them as opaque projections.
559 // Right now we do neither of that and simply always fail to unify them.
560 DefKind
::AnonConst
=> (),
561 _
=> return Ok(None
),
563 let body
= tcx
.mir_const(def
).borrow();
564 AbstractConstBuilder
::new(tcx
, &body
)?
.map(AbstractConstBuilder
::build
).transpose()
570 pub(super) fn try_unify_abstract_consts
<'tcx
>(
572 ((a
, a_substs
), (b
, b_substs
)): (
573 (ty
::WithOptConstParam
<DefId
>, SubstsRef
<'tcx
>),
574 (ty
::WithOptConstParam
<DefId
>, SubstsRef
<'tcx
>),
578 if let Some(a
) = AbstractConst
::new(tcx
, a
, a_substs
)?
{
579 if let Some(b
) = AbstractConst
::new(tcx
, b
, b_substs
)?
{
580 return Ok(try_unify(tcx
, a
, b
));
586 .unwrap_or_else(|ErrorReported
| true)
587 // FIXME(const_evaluatable_checked): We should instead have this
588 // method return the resulting `ty::Const` and return `ConstKind::Error`
589 // on `ErrorReported`.
592 pub fn walk_abstract_const
<'tcx
, R
, F
>(
594 ct
: AbstractConst
<'tcx
>,
598 F
: FnMut(AbstractConst
<'tcx
>) -> ControlFlow
<R
>,
602 ct
: AbstractConst
<'tcx
>,
603 f
: &mut dyn FnMut(AbstractConst
<'tcx
>) -> ControlFlow
<R
>,
604 ) -> ControlFlow
<R
> {
606 let root
= ct
.root();
608 Node
::Leaf(_
) => ControlFlow
::CONTINUE
,
609 Node
::Binop(_
, l
, r
) => {
610 recurse(tcx
, ct
.subtree(l
), f
)?
;
611 recurse(tcx
, ct
.subtree(r
), f
)
613 Node
::UnaryOp(_
, v
) => recurse(tcx
, ct
.subtree(v
), f
),
614 Node
::FunctionCall(func
, args
) => {
615 recurse(tcx
, ct
.subtree(func
), f
)?
;
616 args
.iter().try_for_each(|&arg
| recurse(tcx
, ct
.subtree(arg
), f
))
618 Node
::Cast(_
, operand
, _
) => recurse(tcx
, ct
.subtree(operand
), f
),
622 recurse(tcx
, ct
, &mut f
)
625 /// Tries to unify two abstract constants using structural equality.
626 pub(super) fn try_unify
<'tcx
>(
628 mut a
: AbstractConst
<'tcx
>,
629 mut b
: AbstractConst
<'tcx
>,
631 // We substitute generics repeatedly to allow AbstractConsts to unify where a
632 // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g.
633 // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
634 while let Node
::Leaf(a_ct
) = a
.root() {
635 let a_ct
= a_ct
.subst(tcx
, a
.substs
);
636 match AbstractConst
::from_const(tcx
, a_ct
) {
637 Ok(Some(a_act
)) => a
= a_act
,
639 Err(_
) => return true,
642 while let Node
::Leaf(b_ct
) = b
.root() {
643 let b_ct
= b_ct
.subst(tcx
, b
.substs
);
644 match AbstractConst
::from_const(tcx
, b_ct
) {
645 Ok(Some(b_act
)) => b
= b_act
,
647 Err(_
) => return true,
651 match (a
.root(), b
.root()) {
652 (Node
::Leaf(a_ct
), Node
::Leaf(b_ct
)) => {
653 let a_ct
= a_ct
.subst(tcx
, a
.substs
);
654 let b_ct
= b_ct
.subst(tcx
, b
.substs
);
655 if a_ct
.ty
!= b_ct
.ty
{
659 match (a_ct
.val
, b_ct
.val
) {
660 // We can just unify errors with everything to reduce the amount of
661 // emitted errors here.
662 (ty
::ConstKind
::Error(_
), _
) | (_
, ty
::ConstKind
::Error(_
)) => true,
663 (ty
::ConstKind
::Param(a_param
), ty
::ConstKind
::Param(b_param
)) => {
666 (ty
::ConstKind
::Value(a_val
), ty
::ConstKind
::Value(b_val
)) => a_val
== b_val
,
667 // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
668 // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
669 // means that we only allow inference variables if they are equal.
670 (ty
::ConstKind
::Infer(a_val
), ty
::ConstKind
::Infer(b_val
)) => a_val
== b_val
,
671 // We expand generic anonymous constants at the start of this function, so this
672 // branch should only be taking when dealing with associated constants, at
673 // which point directly comparing them seems like the desired behavior.
675 // FIXME(const_evaluatable_checked): This isn't actually the case.
676 // We also take this branch for concrete anonymous constants and
677 // expand generic anonymous constants with concrete substs.
678 (ty
::ConstKind
::Unevaluated(a_uv
), ty
::ConstKind
::Unevaluated(b_uv
)) => {
681 // FIXME(const_evaluatable_checked): We may want to either actually try
682 // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
683 // this, for now we just return false here.
687 (Node
::Binop(a_op
, al
, ar
), Node
::Binop(b_op
, bl
, br
)) if a_op
== b_op
=> {
688 try_unify(tcx
, a
.subtree(al
), b
.subtree(bl
))
689 && try_unify(tcx
, a
.subtree(ar
), b
.subtree(br
))
691 (Node
::UnaryOp(a_op
, av
), Node
::UnaryOp(b_op
, bv
)) if a_op
== b_op
=> {
692 try_unify(tcx
, a
.subtree(av
), b
.subtree(bv
))
694 (Node
::FunctionCall(a_f
, a_args
), Node
::FunctionCall(b_f
, b_args
))
695 if a_args
.len() == b_args
.len() =>
697 try_unify(tcx
, a
.subtree(a_f
), b
.subtree(b_f
))
698 && iter
::zip(a_args
, b_args
)
699 .all(|(&an
, &bn
)| try_unify(tcx
, a
.subtree(an
), b
.subtree(bn
)))
701 (Node
::Cast(a_cast_kind
, a_operand
, a_ty
), Node
::Cast(b_cast_kind
, b_operand
, b_ty
))
702 if (a_ty
== b_ty
) && (a_cast_kind
== b_cast_kind
) =>
704 try_unify(tcx
, a
.subtree(a_operand
), b
.subtree(b_operand
))