1use std::fmt::Debug;
2use std::ops::Range;
34use rustc_data_structures::{snapshot_vecas sv, unifyas ut};
5use rustc_middle::ty::{
6self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder,
7TypeSuperFoldable, TypeVisitableExt,
8};
9use tracing::instrument;
10use ut::UnifyKey;
1112use super::VariableLengths;
13use crate::infer::type_variable::{FloatVariableOrigin, TypeVariableOrigin};
14use crate::infer::unify_key::{ConstVariableValue, ConstVidKey};
15use crate::infer::{
16ConstVariableOrigin, InferCtxt, InferCtxtInner, RegionVariableOrigin, UnificationTable,
17};
1819fn vars_since_snapshot<'tcx, T>(
20 table: &UnificationTable<'_, 'tcx, T>,
21 snapshot_var_len: usize,
22) -> Range<T>
23where
24T: UnifyKey,
25super::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}
2930fn float_vars_since_snapshot(
31 inner: &mut InferCtxtInner<'_>,
32 snapshot_var_len: usize,
33) -> (Range<FloatVid>, Vec<FloatVariableOrigin>) {
34let 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}
3738fn const_vars_since_snapshot<'tcx>(
39 table: &mut UnificationTable<'_, 'tcx, ConstVidKey<'tcx>>,
40 snapshot_var_len: usize,
41) -> (Range<ConstVid>, Vec<ConstVariableOrigin>) {
42let range = vars_since_snapshot(table, snapshot_var_len);
43let range = range.start.vid..range.end.vid;
4445 (
46range.clone(),
47range48 .map(|index| match table.probe_value(index) {
49 ConstVariableValue::Known { value: _ } => {
50ConstVariableOrigin { param_def_id: None, span: rustc_span::DUMMY_SP }
51 }
52 ConstVariableValue::Unknown { origin, universe: _ } => origin,
53 })
54 .collect(),
55 )
56}
5758impl<'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{}
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() || { false }
{
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("fudge_inference_if_ok",
"rustc_infer::infer::snapshot::fudge",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/infer/snapshot/fudge.rs"),
::tracing_core::__macro_support::Option::Some(98u32),
::tracing_core::__macro_support::Option::Some("rustc_infer::infer::snapshot::fudge"),
::tracing_core::field::FieldSet::new(&[],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{ meta.fields().value_set_all(&[]) })
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[allow(clippy :: redundant_closure_call)]
let x =
(move ||
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy
:: needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: Result<T, E> = loop {};
return __tracing_attr_fake_return;
}
{
let variable_lengths = self.variable_lengths();
let (snapshot_vars, value) =
self.probe(|_|
{
let value = f()?;
let snapshot_vars =
SnapshotVarData::new(self, variable_lengths);
Ok((snapshot_vars, self.resolve_vars_if_possible(value)))
})?;
Ok(self.fudge_inference(snapshot_vars, value))
}
})();
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/infer/snapshot/fudge.rs:98",
"rustc_infer::infer::snapshot::fudge",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/infer/snapshot/fudge.rs"),
::tracing_core::__macro_support::Option::Some(98u32),
::tracing_core::__macro_support::Option::Some("rustc_infer::infer::snapshot::fudge"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("return")
}> =
::tracing::__macro_support::FieldName::new("return");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&x)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};
x;#[instrument(skip(self, f), level = "debug", ret)]99pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
100where
101F: FnOnce() -> Result<T, E>,
102 T: TypeFoldable<TyCtxt<'tcx>>,
103 E: Debug,
104 {
105let variable_lengths = self.variable_lengths();
106let (snapshot_vars, value) = self.probe(|_| {
107let 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.
113let snapshot_vars = SnapshotVarData::new(self, variable_lengths);
114Ok((snapshot_vars, self.resolve_vars_if_possible(value)))
115 })?;
116117// 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. =)
121Ok(self.fudge_inference(snapshot_vars, value))
122 }
123124fn 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.
131if snapshot_vars.is_empty() {
132value133 } else {
134value.fold_with(&mut InferenceFudger { infcx: self, snapshot_vars })
135 }
136 }
137}
138139struct 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}
146147impl<'tcx> SnapshotVarData<'tcx> {
148fn new(infcx: &InferCtxt<'tcx>, vars_pre_snapshot: VariableLengths) -> SnapshotVarData<'tcx> {
149let mut inner = infcx.inner.borrow_mut();
150let region_vars = inner151 .unwrap_region_constraints()
152 .vars_since_snapshot(vars_pre_snapshot.region_constraints_len);
153let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len);
154let int_vars =
155vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len);
156let float_vars = float_vars_since_snapshot(&mut inner, vars_pre_snapshot.float_var_len);
157158let const_vars = const_vars_since_snapshot(
159&mut inner.const_unification_table(),
160vars_pre_snapshot.const_var_len,
161 );
162SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars }
163 }
164165fn is_empty(&self) -> bool {
166let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self;
167region_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}
174175struct InferenceFudger<'a, 'tcx> {
176 infcx: &'a InferCtxt<'tcx>,
177 snapshot_vars: SnapshotVarData<'tcx>,
178}
179180impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> {
181fn cx(&self) -> TyCtxt<'tcx> {
182self.infcx.tcx
183 }
184185fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
186if let &ty::Infer(infer_ty) = ty.kind() {
187match infer_ty {
188 ty::TyVar(vid) => {
189if self.snapshot_vars.type_vars.0.contains(&vid) {
190// This variable was created during the fudging.
191 // Recreate it with a fresh variable here.
192let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize();
193let origin = self.snapshot_vars.type_vars.1[idx];
194self.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.
201if 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!(
202self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown()
203 );
204ty205 }
206 }
207 ty::IntVar(vid) => {
208if self.snapshot_vars.int_vars.contains(&vid) {
209self.infcx.next_int_var()
210 } else {
211ty212 }
213 }
214 ty::FloatVar(vid) => {
215if self.snapshot_vars.float_vars.0.contains(&vid) {
216let idx = vid.as_usize() - self.snapshot_vars.float_vars.0.start.as_usize();
217let FloatVariableOrigin { span, lint_id } =
218self.snapshot_vars.float_vars.1[idx];
219self.infcx.next_float_var(span, lint_id)
220 } else {
221ty222 }
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() {
229ty.super_fold_with(self)
230 } else {
231ty232 }
233 }
234235fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
236if let ty::ReVar(vid) = r.kind() {
237if self.snapshot_vars.region_vars.0.contains(&vid) {
238let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index();
239let origin = self.snapshot_vars.region_vars.1[idx];
240self.infcx.next_region_var(origin)
241 } else {
242r243 }
244 } else {
245r246 }
247 }
248249fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
250if let ty::ConstKind::Infer(infer_ct) = ct.kind() {
251match infer_ct {
252 ty::InferConst::Var(vid) => {
253if self.snapshot_vars.const_vars.0.contains(&vid) {
254let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index();
255let origin = self.snapshot_vars.const_vars.1[idx];
256self.infcx.next_const_var_with_origin(origin)
257 } else {
258ct259 }
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() {
266ct.super_fold_with(self)
267 } else {
268ct269 }
270 }
271}