]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/ty/abstract_const.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / ty / abstract_const.rs
CommitLineData
064997fb
FG
1//! A subset of a mir body used for const evaluatability checking.
2use crate::mir;
3use crate::ty::visit::TypeVisitable;
4use crate::ty::{self, subst::Subst, DelaySpanBugEmitted, EarlyBinder, SubstsRef, Ty, TyCtxt};
5use rustc_errors::ErrorGuaranteed;
6use rustc_hir::def_id::DefId;
7use std::cmp;
8use std::ops::ControlFlow;
9
10rustc_index::newtype_index! {
11 /// An index into an `AbstractConst`.
12 pub struct NodeId {
13 derive [HashStable]
14 DEBUG_FORMAT = "n{}",
15 }
16}
17
18/// A tree representing an anonymous constant.
19///
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)]
23pub struct AbstractConst<'tcx> {
24 // FIXME: Consider adding something like `IndexSlice`
25 // and use this here.
26 inner: &'tcx [Node<'tcx>],
27 substs: SubstsRef<'tcx>,
28}
29
30impl<'tcx> AbstractConst<'tcx> {
31 pub fn new(
32 tcx: TyCtxt<'tcx>,
33 uv: ty::Unevaluated<'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) }))
38 }
39
40 pub fn from_const(
41 tcx: TyCtxt<'tcx>,
42 ct: ty::Const<'tcx>,
43 ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
44 match ct.kind() {
f2b60f7d 45 ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv),
064997fb
FG
46 ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported),
47 _ => Ok(None),
48 }
49 }
50
51 #[inline]
52 pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
53 AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
54 }
55
56 #[inline]
57 pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
58 let node = self.inner.last().copied().unwrap();
59 match node {
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))
63 }
64 // Don't perform substitution on the following as they can't directly contain generic params
65 Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
66 }
67 }
68
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) {
73 Node::Leaf(leaf) => {
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);
78 }
79 }
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);
85 }
86 }
87 Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {}
88 }
89 ControlFlow::CONTINUE
90 });
91 failure_kind
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
96pub enum CastKind {
97 /// thir::ExprKind::As
98 As,
99 /// thir::ExprKind::Use
100 Use,
101}
102
103/// A node of an `AbstractConst`.
104#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
105pub 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>),
111}
112
113#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
114pub enum NotConstEvaluatable {
115 Error(ErrorGuaranteed),
116 MentionsInfer,
117 MentionsParam,
118}
119
120impl From<ErrorGuaranteed> for NotConstEvaluatable {
121 fn from(e: ErrorGuaranteed) -> NotConstEvaluatable {
122 NotConstEvaluatable::Error(e)
123 }
124}
125
126TrivialTypeTraversalAndLiftImpls! {
127 NotConstEvaluatable,
128}
129
130impl<'tcx> TyCtxt<'tcx> {
131 #[inline]
132 pub fn thir_abstract_const_opt_const_arg(
133 self,
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))
138 } else {
139 self.thir_abstract_const(def.did)
140 }
141 }
142}
143
144#[instrument(skip(tcx, f), level = "debug")]
145pub fn walk_abstract_const<'tcx, R, F>(
146 tcx: TyCtxt<'tcx>,
147 ct: AbstractConst<'tcx>,
148 mut f: F,
149) -> ControlFlow<R>
150where
151 F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
152{
153 #[instrument(skip(tcx, f), level = "debug")]
154 fn recurse<'tcx, R>(
155 tcx: TyCtxt<'tcx>,
156 ct: AbstractConst<'tcx>,
157 f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
158 ) -> ControlFlow<R> {
159 f(ct)?;
160 let root = ct.root(tcx);
161 debug!(?root);
162 match root {
163 Node::Leaf(_) => ControlFlow::CONTINUE,
164 Node::Binop(_, l, r) => {
165 recurse(tcx, ct.subtree(l), f)?;
166 recurse(tcx, ct.subtree(r), f)
167 }
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))
172 }
173 Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
174 }
175 }
176
177 recurse(tcx, ct, &mut f)
178}
179
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)]
184pub enum FailureKind {
185 /// The abstract const still references an inference
186 /// variable, in this case we return `TooGeneric`.
187 MentionsInfer,
188 /// The abstract const references a generic parameter,
189 /// this means that we emit an error here.
190 MentionsParam,
191 /// The substs are concrete enough that we can simply
192 /// try and evaluate the given constant.
193 Concrete,
194}