rustc_infer/infer/
resolve.rs

1use rustc_middle::bug;
2use rustc_middle::ty::{
3    self, Const, DelayedMap, FallibleTypeFolder, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder,
4    TypeSuperFoldable, TypeVisitableExt,
5};
6
7use super::{FixupError, FixupResult, InferCtxt};
8use crate::infer::TyOrConstInferVar;
9
10///////////////////////////////////////////////////////////////////////////
11// OPPORTUNISTIC VAR RESOLVER
12
13/// The opportunistic resolver can be used at any time. It simply replaces
14/// type/const variables that have been unified with the things they have
15/// been unified with (similar to `shallow_resolve`, but deep). This is
16/// useful for printing messages etc but also required at various
17/// points for correctness.
18pub struct OpportunisticVarResolver<'a, 'tcx> {
19    infcx: &'a InferCtxt<'tcx>,
20    /// We're able to use a cache here as the folder does
21    /// not have any mutable state.
22    cache: DelayedMap<Ty<'tcx>, Ty<'tcx>>,
23}
24
25impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> {
26    #[inline]
27    pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
28        OpportunisticVarResolver { infcx, cache: Default::default() }
29    }
30}
31
32impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticVarResolver<'a, 'tcx> {
33    fn cx(&self) -> TyCtxt<'tcx> {
34        self.infcx.tcx
35    }
36
37    #[inline]
38    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
39        if !t.has_non_region_infer() {
40            t // micro-optimize -- if there is nothing in this type that this fold affects...
41        } else if let Some(&ty) = self.cache.get(&t) {
42            ty
43        } else {
44            let shallow = self.infcx.shallow_resolve(t);
45            let res = shallow.super_fold_with(self);
46            assert!(self.cache.insert(t, res));
47            res
48        }
49    }
50
51    fn fold_const(&mut self, ct: Const<'tcx>) -> Const<'tcx> {
52        if !ct.has_non_region_infer() {
53            ct // micro-optimize -- if there is nothing in this const that this fold affects...
54        } else {
55            let ct = self.infcx.shallow_resolve_const(ct);
56            ct.super_fold_with(self)
57        }
58    }
59
60    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
61        if !p.has_non_region_infer() { p } else { p.super_fold_with(self) }
62    }
63
64    fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
65        if !c.has_non_region_infer() { c } else { c.super_fold_with(self) }
66    }
67}
68
69/// The opportunistic region resolver opportunistically resolves regions
70/// variables to the variable with the least variable id. It is used when
71/// normalizing projections to avoid hitting the recursion limit by creating
72/// many versions of a predicate for types that in the end have to unify.
73///
74/// If you want to resolve type and const variables as well, call
75/// [InferCtxt::resolve_vars_if_possible] first.
76pub struct OpportunisticRegionResolver<'a, 'tcx> {
77    infcx: &'a InferCtxt<'tcx>,
78}
79
80impl<'a, 'tcx> OpportunisticRegionResolver<'a, 'tcx> {
81    pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
82        OpportunisticRegionResolver { infcx }
83    }
84}
85
86impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticRegionResolver<'a, 'tcx> {
87    fn cx(&self) -> TyCtxt<'tcx> {
88        self.infcx.tcx
89    }
90
91    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
92        if !t.has_infer_regions() {
93            t // micro-optimize -- if there is nothing in this type that this fold affects...
94        } else {
95            t.super_fold_with(self)
96        }
97    }
98
99    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
100        match r.kind() {
101            ty::ReVar(vid) => self
102                .infcx
103                .inner
104                .borrow_mut()
105                .unwrap_region_constraints()
106                .opportunistic_resolve_var(TypeFolder::cx(self), vid),
107            _ => r,
108        }
109    }
110
111    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
112        if !ct.has_infer_regions() {
113            ct // micro-optimize -- if there is nothing in this const that this fold affects...
114        } else {
115            ct.super_fold_with(self)
116        }
117    }
118}
119
120///////////////////////////////////////////////////////////////////////////
121// FULL TYPE RESOLUTION
122
123/// Full type resolution replaces all type and region variables with
124/// their concrete results. If any variable cannot be replaced (never unified, etc)
125/// then an `Err` result is returned.
126pub fn fully_resolve<'tcx, T>(infcx: &InferCtxt<'tcx>, value: T) -> FixupResult<T>
127where
128    T: TypeFoldable<TyCtxt<'tcx>>,
129{
130    value.try_fold_with(&mut FullTypeResolver { infcx })
131}
132
133struct FullTypeResolver<'a, 'tcx> {
134    infcx: &'a InferCtxt<'tcx>,
135}
136
137impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for FullTypeResolver<'a, 'tcx> {
138    type Error = FixupError;
139
140    fn cx(&self) -> TyCtxt<'tcx> {
141        self.infcx.tcx
142    }
143
144    fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
145        if !t.has_infer() {
146            Ok(t) // micro-optimize -- if there is nothing in this type that this fold affects...
147        } else {
148            let t = self.infcx.shallow_resolve(t);
149            match *t.kind() {
150                ty::Infer(ty::TyVar(vid)) => {
151                    Err(FixupError { unresolved: TyOrConstInferVar::Ty(vid) })
152                }
153                ty::Infer(ty::IntVar(vid)) => {
154                    Err(FixupError { unresolved: TyOrConstInferVar::TyInt(vid) })
155                }
156                ty::Infer(ty::FloatVar(vid)) => {
157                    Err(FixupError { unresolved: TyOrConstInferVar::TyFloat(vid) })
158                }
159                ty::Infer(_) => {
160                    bug!("Unexpected type in full type resolver: {:?}", t);
161                }
162                _ => t.try_super_fold_with(self),
163            }
164        }
165    }
166
167    fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> {
168        match r.kind() {
169            ty::ReVar(_) => Ok(self
170                .infcx
171                .lexical_region_resolutions
172                .borrow()
173                .as_ref()
174                .expect("region resolution not performed")
175                .resolve_region(self.infcx.tcx, r)),
176            _ => Ok(r),
177        }
178    }
179
180    fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
181        if !c.has_infer() {
182            Ok(c) // micro-optimize -- if there is nothing in this const that this fold affects...
183        } else {
184            let c = self.infcx.shallow_resolve_const(c);
185            match c.kind() {
186                ty::ConstKind::Infer(InferConst::Var(vid)) => {
187                    return Err(FixupError { unresolved: super::TyOrConstInferVar::Const(vid) });
188                }
189                ty::ConstKind::Infer(InferConst::Fresh(_)) => {
190                    bug!("Unexpected const in full const resolver: {:?}", c);
191                }
192                _ => {}
193            }
194            c.try_super_fold_with(self)
195        }
196    }
197}