1 //! A subset of a mir body used for const evaluatability checking.
3 use crate::ty::visit::TypeVisitable;
4 use crate::ty::{self, DelaySpanBugEmitted, EarlyBinder, SubstsRef, Ty, TyCtxt};
5 use rustc_errors::ErrorGuaranteed;
6 use rustc_hir::def_id::DefId;
8 use std::ops::ControlFlow;
10 rustc_index::newtype_index! {
11 /// An index into an `AbstractConst`.
18 /// A tree representing an anonymous constant.
20 /// This is only able to represent a subset of `MIR`,
21 /// and should not leak any information about desugarings.
22 #[derive(Debug, Clone, Copy)]
23 pub struct AbstractConst<'tcx> {
24 // FIXME: Consider adding something like `IndexSlice`
26 inner: &'tcx [Node<'tcx>],
27 substs: SubstsRef<'tcx>,
30 impl<'tcx> AbstractConst<'tcx> {
33 uv: ty::UnevaluatedConst<'tcx>,
34 ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
35 let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
36 debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
37 Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) }))
43 ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
45 ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv),
46 ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported),
52 pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
53 AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
57 pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
58 let node = self.inner.last().copied().unwrap();
60 Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)),
61 Node::Cast(kind, operand, ty) => {
62 Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs))
64 // Don't perform substitution on the following as they can't directly contain generic params
65 Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
69 pub fn unify_failure_kind(self, tcx: TyCtxt<'tcx>) -> FailureKind {
70 let mut failure_kind = FailureKind::Concrete;
71 walk_abstract_const::<!, _>(tcx, self, |node| {
72 match node.root(tcx) {
74 if leaf.has_infer_types_or_consts() {
75 failure_kind = FailureKind::MentionsInfer;
76 } else if leaf.has_param_types_or_consts() {
77 failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
80 Node::Cast(_, _, ty) => {
81 if ty.has_infer_types_or_consts() {
82 failure_kind = FailureKind::MentionsInfer;
83 } else if ty.has_param_types_or_consts() {
84 failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
87 Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {}
95 #[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
97 /// thir::ExprKind::As
99 /// thir::ExprKind::Use
103 /// A node of an `AbstractConst`.
104 #[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
105 pub enum Node<'tcx> {
106 Leaf(ty::Const<'tcx>),
107 Binop(mir::BinOp, NodeId, NodeId),
108 UnaryOp(mir::UnOp, NodeId),
109 FunctionCall(NodeId, &'tcx [NodeId]),
110 Cast(CastKind, NodeId, Ty<'tcx>),
113 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
114 pub enum NotConstEvaluatable {
115 Error(ErrorGuaranteed),
120 impl From<ErrorGuaranteed> for NotConstEvaluatable {
121 fn from(e: ErrorGuaranteed) -> NotConstEvaluatable {
122 NotConstEvaluatable::Error(e)
126 TrivialTypeTraversalAndLiftImpls! {
130 impl<'tcx> TyCtxt<'tcx> {
132 pub fn thir_abstract_const_opt_const_arg(
134 def: ty::WithOptConstParam<DefId>,
135 ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
136 if let Some((did, param_did)) = def.as_const_arg() {
137 self.thir_abstract_const_of_const_arg((did, param_did))
139 self.thir_abstract_const(def.did)
144 #[instrument(skip(tcx, f), level = "debug")]
145 pub fn walk_abstract_const<'tcx, R, F>(
147 ct: AbstractConst<'tcx>,
151 F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
153 #[instrument(skip(tcx, f), level = "debug")]
156 ct: AbstractConst<'tcx>,
157 f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
158 ) -> ControlFlow<R> {
160 let root = ct.root(tcx);
163 Node::Leaf(_) => ControlFlow::CONTINUE,
164 Node::Binop(_, l, r) => {
165 recurse(tcx, ct.subtree(l), f)?;
166 recurse(tcx, ct.subtree(r), f)
168 Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f),
169 Node::FunctionCall(func, args) => {
170 recurse(tcx, ct.subtree(func), f)?;
171 args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f))
173 Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
177 recurse(tcx, ct, &mut f)
180 // We were unable to unify the abstract constant with
181 // a constant found in the caller bounds, there are
182 // now three possible cases here.
183 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
184 pub enum FailureKind {
185 /// The abstract const still references an inference
186 /// variable, in this case we return `TooGeneric`.
188 /// The abstract const references a generic parameter,
189 /// this means that we emit an error here.
191 /// The substs are concrete enough that we can simply
192 /// try and evaluate the given constant.