Skip to main content

rustc_infer/infer/snapshot/
fudge.rs

1use std::fmt::Debug;
2use std::ops::Range;
3
4use rustc_data_structures::{snapshot_vec as sv, unify as ut};
5use rustc_middle::ty::{
6    self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder,
7    TypeSuperFoldable, TypeVisitableExt,
8};
9use tracing::instrument;
10use ut::UnifyKey;
11
12use super::VariableLengths;
13use crate::infer::type_variable::{FloatVariableOrigin, TypeVariableOrigin};
14use crate::infer::unify_key::{ConstVariableValue, ConstVidKey};
15use crate::infer::{
16    ConstVariableOrigin, InferCtxt, InferCtxtInner, RegionVariableOrigin, UnificationTable,
17};
18
19fn vars_since_snapshot<'tcx, T>(
20    table: &UnificationTable<'_, 'tcx, T>,
21    snapshot_var_len: usize,
22) -> Range<T>
23where
24    T: UnifyKey,
25    super::UndoLog<'tcx>: From<sv::UndoLog<ut::Delegate<T>>>,
26{
27    T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32)
28}
29
30fn float_vars_since_snapshot(
31    inner: &mut InferCtxtInner<'_>,
32    snapshot_var_len: usize,
33) -> (Range<FloatVid>, Vec<FloatVariableOrigin>) {
34    let range = vars_since_snapshot(&inner.float_unification_table(), snapshot_var_len);
35    (range.clone(), range.map(|index| inner.float_origin_origin_storage[index]).collect())
36}
37
38fn const_vars_since_snapshot<'tcx>(
39    table: &mut UnificationTable<'_, 'tcx, ConstVidKey<'tcx>>,
40    snapshot_var_len: usize,
41) -> (Range<ConstVid>, Vec<ConstVariableOrigin>) {
42    let range = vars_since_snapshot(table, snapshot_var_len);
43    let range = range.start.vid..range.end.vid;
44
45    (
46        range.clone(),
47        range
48            .map(|index| match table.probe_value(index) {
49                ConstVariableValue::Known { value: _ } => {
50                    ConstVariableOrigin { param_def_id: None, span: rustc_span::DUMMY_SP }
51                }
52                ConstVariableValue::Unknown { origin, universe: _ } => origin,
53            })
54            .collect(),
55    )
56}
57
58impl<'tcx> InferCtxt<'tcx> {
59    /// This rather funky routine is used while processing expected
60    /// types. What happens here is that we want to propagate a
61    /// coercion through the return type of a fn to its
62    /// argument. Consider the type of `Option::Some`, which is
63    /// basically `for<T> fn(T) -> Option<T>`. So if we have an
64    /// expression `Some(&[1, 2, 3])`, and that has the expected type
65    /// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]`
66    /// with the expectation of `&[u32]`. This will cause us to coerce
67    /// from `&[u32; 3]` to `&[u32]` and make the users life more
68    /// pleasant.
69    ///
70    /// The way we do this is using `fudge_inference_if_ok`. What the
71    /// routine actually does is to start a snapshot and execute the
72    /// closure `f`. In our example above, what this closure will do
73    /// is to unify the expectation (`Option<&[u32]>`) with the actual
74    /// return type (`Option<?T>`, where `?T` represents the variable
75    /// instantiated for `T`). This will cause `?T` to be unified
76    /// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The
77    /// input type (`?T`) is then returned by `f()`.
78    ///
79    /// At this point, `fudge_inference_if_ok` will normalize all type
80    /// variables, converting `?T` to `&?a [u32]` and end the
81    /// snapshot. The problem is that we can't just return this type
82    /// out, because it references the region variable `?a`, and that
83    /// region variable was popped when we popped the snapshot.
84    ///
85    /// So what we do is to keep a list (`region_vars`, in the code below)
86    /// of region variables created during the snapshot (here, `?a`). We
87    /// fold the return value and replace any such regions with a *new*
88    /// region variable (e.g., `?b`) and return the result (`&?b [u32]`).
89    /// This can then be used as the expectation for the fn argument.
90    ///
91    /// The important point here is that, for soundness purposes, the
92    /// regions in question are not particularly important. We will
93    /// use the expected types to guide coercions, but we will still
94    /// type-check the resulting types from those coercions against
95    /// the actual types (`?T`, `Option<?T>`) -- and remember that
96    /// after the snapshot is popped, the variable `?T` is no longer
97    /// unified.
98    x;#[instrument(skip(self, f), level = "debug", ret)]
99    pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
100    where
101        F: FnOnce() -> Result<T, E>,
102        T: TypeFoldable<TyCtxt<'tcx>>,
103        E: Debug,
104    {
105        let variable_lengths = self.variable_lengths();
106        let (snapshot_vars, value) = self.probe(|_| {
107            let value = f()?;
108            // At this point, `value` could in principle refer
109            // to inference variables that have been created during
110            // the snapshot. Once we exit `probe()`, those are
111            // going to be popped, so we will have to
112            // eliminate any references to them.
113            let snapshot_vars = SnapshotVarData::new(self, variable_lengths);
114            Ok((snapshot_vars, self.resolve_vars_if_possible(value)))
115        })?;
116
117        // At this point, we need to replace any of the now-popped
118        // type/region variables that appear in `value` with a fresh
119        // variable of the appropriate kind. We can't do this during
120        // the probe because they would just get popped then too. =)
121        Ok(self.fudge_inference(snapshot_vars, value))
122    }
123
124    fn fudge_inference<T: TypeFoldable<TyCtxt<'tcx>>>(
125        &self,
126        snapshot_vars: SnapshotVarData<'tcx>,
127        value: T,
128    ) -> T {
129        // Micro-optimization: if no variables have been created, then
130        // `value` can't refer to any of them. =) So we can just return it.
131        if snapshot_vars.is_empty() {
132            value
133        } else {
134            value.fold_with(&mut InferenceFudger { infcx: self, snapshot_vars })
135        }
136    }
137}
138
139struct SnapshotVarData<'tcx> {
140    region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin<'tcx>>),
141    type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>),
142    int_vars: Range<IntVid>,
143    float_vars: (Range<FloatVid>, Vec<FloatVariableOrigin>),
144    const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>),
145}
146
147impl<'tcx> SnapshotVarData<'tcx> {
148    fn new(infcx: &InferCtxt<'tcx>, vars_pre_snapshot: VariableLengths) -> SnapshotVarData<'tcx> {
149        let mut inner = infcx.inner.borrow_mut();
150        let region_vars = inner
151            .unwrap_region_constraints()
152            .vars_since_snapshot(vars_pre_snapshot.region_constraints_len);
153        let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len);
154        let int_vars =
155            vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len);
156        let float_vars = float_vars_since_snapshot(&mut inner, vars_pre_snapshot.float_var_len);
157
158        let const_vars = const_vars_since_snapshot(
159            &mut inner.const_unification_table(),
160            vars_pre_snapshot.const_var_len,
161        );
162        SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars }
163    }
164
165    fn is_empty(&self) -> bool {
166        let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self;
167        region_vars.0.is_empty()
168            && type_vars.0.is_empty()
169            && int_vars.is_empty()
170            && float_vars.0.is_empty()
171            && const_vars.0.is_empty()
172    }
173}
174
175struct InferenceFudger<'a, 'tcx> {
176    infcx: &'a InferCtxt<'tcx>,
177    snapshot_vars: SnapshotVarData<'tcx>,
178}
179
180impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> {
181    fn cx(&self) -> TyCtxt<'tcx> {
182        self.infcx.tcx
183    }
184
185    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
186        if let &ty::Infer(infer_ty) = ty.kind() {
187            match infer_ty {
188                ty::TyVar(vid) => {
189                    if self.snapshot_vars.type_vars.0.contains(&vid) {
190                        // This variable was created during the fudging.
191                        // Recreate it with a fresh variable here.
192                        let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize();
193                        let origin = self.snapshot_vars.type_vars.1[idx];
194                        self.infcx.next_ty_var_with_origin(origin)
195                    } else {
196                        // This variable was created before the
197                        // "fudging". Since we refresh all type
198                        // variables to their binding anyhow, we know
199                        // that it is unbound, so we can just return
200                        // it.
201                        if true {
    if !self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown()
        {
        ::core::panicking::panic("assertion failed: self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown()")
    };
};debug_assert!(
202                            self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown()
203                        );
204                        ty
205                    }
206                }
207                ty::IntVar(vid) => {
208                    if self.snapshot_vars.int_vars.contains(&vid) {
209                        self.infcx.next_int_var()
210                    } else {
211                        ty
212                    }
213                }
214                ty::FloatVar(vid) => {
215                    if self.snapshot_vars.float_vars.0.contains(&vid) {
216                        let idx = vid.as_usize() - self.snapshot_vars.float_vars.0.start.as_usize();
217                        let FloatVariableOrigin { span, lint_id } =
218                            self.snapshot_vars.float_vars.1[idx];
219                        self.infcx.next_float_var(span, lint_id)
220                    } else {
221                        ty
222                    }
223                }
224                ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
225                    {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("unexpected fresh infcx var")));
}unreachable!("unexpected fresh infcx var")
226                }
227            }
228        } else if ty.has_infer() {
229            ty.super_fold_with(self)
230        } else {
231            ty
232        }
233    }
234
235    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
236        if let ty::ReVar(vid) = r.kind() {
237            if self.snapshot_vars.region_vars.0.contains(&vid) {
238                let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index();
239                let origin = self.snapshot_vars.region_vars.1[idx];
240                self.infcx.next_region_var(origin)
241            } else {
242                r
243            }
244        } else {
245            r
246        }
247    }
248
249    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
250        if let ty::ConstKind::Infer(infer_ct) = ct.kind() {
251            match infer_ct {
252                ty::InferConst::Var(vid) => {
253                    if self.snapshot_vars.const_vars.0.contains(&vid) {
254                        let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index();
255                        let origin = self.snapshot_vars.const_vars.1[idx];
256                        self.infcx.next_const_var_with_origin(origin)
257                    } else {
258                        ct
259                    }
260                }
261                ty::InferConst::Fresh(_) => {
262                    {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("unexpected fresh infcx var")));
}unreachable!("unexpected fresh infcx var")
263                }
264            }
265        } else if ct.has_infer() {
266            ct.super_fold_with(self)
267        } else {
268            ct
269        }
270    }
271}