hir_ty/next_solver/infer/snapshot/
fudge.rs1use std::ops::Range;
2
3use ena::{
4 snapshot_vec as sv,
5 unify::{self as ut, UnifyKey},
6};
7use rustc_type_ir::{
8 ConstVid, FloatVid, IntVid, RegionVid, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable,
9 TypeVisitableExt, inherent::IntoKind,
10};
11
12use crate::{
13 Span,
14 next_solver::{
15 Const, ConstKind, DbInterner, Region, RegionKind, Ty, TyKind,
16 infer::{
17 InferCtxt, UnificationTable, iter_idx_range,
18 snapshot::VariableLengths,
19 unify_key::{ConstVariableValue, ConstVidKey},
20 },
21 },
22};
23
24fn vars_since_snapshot<'db, T>(
25 table: &UnificationTable<'_, 'db, T>,
26 snapshot_var_len: usize,
27) -> Range<T>
28where
29 T: UnifyKey,
30 super::UndoLog<'db>: From<sv::UndoLog<ut::Delegate<T>>>,
31{
32 T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32)
33}
34
35fn const_vars_since_snapshot<'db>(
36 table: &mut UnificationTable<'_, 'db, ConstVidKey<'db>>,
37 snapshot_var_len: usize,
38) -> (Range<ConstVid>, Vec<Span>) {
39 let range = vars_since_snapshot(table, snapshot_var_len);
40 let range = range.start.vid..range.end.vid;
41
42 (
43 range.clone(),
44 iter_idx_range(range)
45 .map(|index| match table.probe_value(index) {
46 ConstVariableValue::Known { value: _ } => Span::Dummy,
47 ConstVariableValue::Unknown { span, universe: _ } => span,
48 })
49 .collect(),
50 )
51}
52
53impl<'db> InferCtxt<'db> {
54 pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
94 where
95 F: FnOnce() -> Result<T, E>,
96 T: TypeFoldable<DbInterner<'db>>,
97 {
98 let variable_lengths = self.variable_lengths();
99 let (snapshot_vars, value) = self.probe(|_| {
100 let value = f()?;
101 let snapshot_vars = SnapshotVarData::new(self, variable_lengths);
107 Ok((snapshot_vars, self.resolve_vars_if_possible(value)))
108 })?;
109
110 Ok(self.fudge_inference(snapshot_vars, value))
115 }
116
117 fn fudge_inference<T: TypeFoldable<DbInterner<'db>>>(
118 &self,
119 snapshot_vars: SnapshotVarData,
120 value: T,
121 ) -> T {
122 if snapshot_vars.is_empty() {
125 value
126 } else {
127 value.fold_with(&mut InferenceFudger { infcx: self, snapshot_vars })
128 }
129 }
130}
131
132struct SnapshotVarData {
133 region_vars: (Range<RegionVid>, Vec<Span>),
134 type_vars: (Range<TyVid>, Vec<Span>),
135 int_vars: Range<IntVid>,
136 float_vars: Range<FloatVid>,
137 const_vars: (Range<ConstVid>, Vec<Span>),
138}
139
140impl SnapshotVarData {
141 fn new(infcx: &InferCtxt<'_>, vars_pre_snapshot: VariableLengths) -> SnapshotVarData {
142 let mut inner = infcx.inner.borrow_mut();
143 let region_vars = inner
144 .unwrap_region_constraints()
145 .vars_since_snapshot(vars_pre_snapshot.region_constraints_len);
146 let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len);
147 let int_vars =
148 vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len);
149 let float_vars =
150 vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len);
151
152 let const_vars = const_vars_since_snapshot(
153 &mut inner.const_unification_table(),
154 vars_pre_snapshot.const_var_len,
155 );
156 SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars }
157 }
158
159 fn is_empty(&self) -> bool {
160 let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self;
161 region_vars.0.is_empty()
162 && type_vars.0.is_empty()
163 && int_vars.is_empty()
164 && float_vars.is_empty()
165 && const_vars.0.is_empty()
166 }
167}
168
169struct InferenceFudger<'a, 'db> {
170 infcx: &'a InferCtxt<'db>,
171 snapshot_vars: SnapshotVarData,
172}
173
174impl<'a, 'db> TypeFolder<DbInterner<'db>> for InferenceFudger<'a, 'db> {
175 fn cx(&self) -> DbInterner<'db> {
176 self.infcx.interner
177 }
178
179 fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
180 if let TyKind::Infer(infer_ty) = ty.kind() {
181 match infer_ty {
182 rustc_type_ir::TyVar(vid) => {
183 if self.snapshot_vars.type_vars.0.contains(&vid) {
184 let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize();
187 let span = self.snapshot_vars.type_vars.1[idx];
188 self.infcx.next_ty_var(span)
189 } else {
190 debug_assert!(
196 self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown()
197 );
198 ty
199 }
200 }
201 rustc_type_ir::IntVar(vid) => {
202 if self.snapshot_vars.int_vars.contains(&vid) {
203 self.infcx.next_int_var()
204 } else {
205 ty
206 }
207 }
208 rustc_type_ir::FloatVar(vid) => {
209 if self.snapshot_vars.float_vars.contains(&vid) {
210 self.infcx.next_float_var()
211 } else {
212 ty
213 }
214 }
215 rustc_type_ir::FreshTy(_)
216 | rustc_type_ir::FreshIntTy(_)
217 | rustc_type_ir::FreshFloatTy(_) => {
218 unreachable!("unexpected fresh infcx var")
219 }
220 }
221 } else if ty.has_infer() {
222 ty.super_fold_with(self)
223 } else {
224 ty
225 }
226 }
227
228 fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
229 if let RegionKind::ReVar(vid) = r.kind() {
230 if self.snapshot_vars.region_vars.0.contains(&vid) {
231 let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index();
232 let span = self.snapshot_vars.region_vars.1[idx];
233 self.infcx.next_region_var(span)
234 } else {
235 r
236 }
237 } else {
238 r
239 }
240 }
241
242 fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
243 if let ConstKind::Infer(infer_ct) = ct.kind() {
244 match infer_ct {
245 rustc_type_ir::InferConst::Var(vid) => {
246 if self.snapshot_vars.const_vars.0.contains(&vid) {
247 let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index();
248 let span = self.snapshot_vars.const_vars.1[idx];
249 self.infcx.next_const_var(span)
250 } else {
251 ct
252 }
253 }
254 rustc_type_ir::InferConst::Fresh(_) => {
255 unreachable!("unexpected fresh infcx var")
256 }
257 }
258 } else if ct.has_infer() {
259 ct.super_fold_with(self)
260 } else {
261 ct
262 }
263 }
264}