Skip to content

Commit b77739a

Browse files
committed
Do not shallow resolve to root var while fudging
1 parent a0e206b commit b77739a

7 files changed

Lines changed: 86 additions & 11 deletions

File tree

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1879,7 +1879,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18791879
if !ocx.try_evaluate_obligations().is_empty() {
18801880
return Err(TypeError::Mismatch);
18811881
}
1882-
Ok(self.resolve_vars_if_possible(adt_ty))
1882+
Ok(adt_ty)
18831883
})
18841884
.ok()
18851885
});

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,12 +276,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
276276

277277
// Record all the argument types, with the args
278278
// produced from the above subtyping unification.
279-
Ok(Some(
280-
formal_input_tys
281-
.iter()
282-
.map(|&ty| self.resolve_vars_if_possible(ty))
283-
.collect(),
284-
))
279+
Ok(Some(formal_input_tys.to_vec()))
285280
})
286281
.ok()
287282
})

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,36 @@ impl<'tcx> InferCtxt<'tcx> {
12501250
value.fold_with(&mut r)
12511251
}
12521252

1253+
/// Normally, we shallow-resolve unresolved type variables to their root
1254+
/// variables. This is mainly done for performance reasons, and in most
1255+
/// cases resolving to the root variable (instead of the variable itself)
1256+
/// does not affect type inference.
1257+
///
1258+
/// However, there is an exceptional case: *fudging*. Fudging is intended
1259+
/// to guide inference rather than impose hard requirements. But our current
1260+
/// handling here is somewhat janky.
1261+
///
1262+
/// In particular, inference variables that are considered equal within the
1263+
/// fudging scope may not remain equal outside of it. This makes it observable
1264+
/// which inference variable we resolve to. For backwards compatibility, we
1265+
/// avoid resolving to the root variable by using this function inside the
1266+
/// fudge instead of [`InferCtxt::resolve_vars_if_possible`].
1267+
///
1268+
/// See #153869 for more details.
1269+
pub fn resolve_vars_if_possible_for_fudging<T>(&self, value: T) -> T
1270+
where
1271+
T: TypeFoldable<TyCtxt<'tcx>>,
1272+
{
1273+
if let Err(guar) = value.error_reported() {
1274+
self.set_tainted_by_errors(guar);
1275+
}
1276+
if !value.has_non_region_infer() {
1277+
return value;
1278+
}
1279+
let mut r = resolve::OpportunisticVarResolver::new_for_fudging(self);
1280+
value.fold_with(&mut r)
1281+
}
1282+
12531283
pub fn resolve_numeric_literals_with_default<T>(&self, value: T) -> T
12541284
where
12551285
T: TypeFoldable<TyCtxt<'tcx>>,

compiler/rustc_infer/src/infer/resolve.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ use crate::infer::TyOrConstInferVar;
1717
/// points for correctness.
1818
pub struct OpportunisticVarResolver<'a, 'tcx> {
1919
infcx: &'a InferCtxt<'tcx>,
20+
/// If true, we don't resolve ty/const vars to their roots.
21+
/// See comments on [`InferCtxt::resolve_vars_if_possible_for_fudging`]
22+
for_fudging: bool,
2023
/// We're able to use a cache here as the folder does
2124
/// not have any mutable state.
2225
cache: DelayedMap<Ty<'tcx>, Ty<'tcx>>,
@@ -25,7 +28,12 @@ pub struct OpportunisticVarResolver<'a, 'tcx> {
2528
impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> {
2629
#[inline]
2730
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
28-
OpportunisticVarResolver { infcx, cache: Default::default() }
31+
OpportunisticVarResolver { infcx, for_fudging: false, cache: Default::default() }
32+
}
33+
34+
#[inline]
35+
pub fn new_for_fudging(infcx: &'a InferCtxt<'tcx>) -> Self {
36+
OpportunisticVarResolver { infcx, for_fudging: true, cache: Default::default() }
2937
}
3038
}
3139

@@ -43,6 +51,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticVarResolver<'a, 'tcx> {
4351
} else {
4452
let shallow = self.infcx.shallow_resolve(t);
4553
let res = shallow.super_fold_with(self);
54+
let res = if self.for_fudging && res.is_ty_var() { t } else { res };
4655
assert!(self.cache.insert(t, res));
4756
res
4857
}
@@ -52,8 +61,11 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticVarResolver<'a, 'tcx> {
5261
if !ct.has_non_region_infer() {
5362
ct // micro-optimize -- if there is nothing in this const that this fold affects...
5463
} else {
55-
let ct = self.infcx.shallow_resolve_const(ct);
56-
ct.super_fold_with(self)
64+
let res = self.infcx.shallow_resolve_const(ct);
65+
if self.for_fudging && res.is_ct_infer() {
66+
return ct;
67+
};
68+
res.super_fold_with(self)
5769
}
5870
}
5971

compiler/rustc_infer/src/infer/snapshot/fudge.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl<'tcx> InferCtxt<'tcx> {
101101
// going to be popped, so we will have to
102102
// eliminate any references to them.
103103
let snapshot_vars = SnapshotVarData::new(self, variable_lengths);
104-
Ok((snapshot_vars, self.resolve_vars_if_possible(value)))
104+
Ok((snapshot_vars, self.resolve_vars_if_possible_for_fudging(value)))
105105
})?;
106106

107107
// At this point, we need to replace any of the now-popped
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ check-pass
2+
3+
// Regression test for <https://github.com/rust-lang/rust/issues/153816>
4+
5+
struct Inv<T, U>(*mut (T, U));
6+
7+
fn pass_through<F>(_: F) -> Inv<F, F> {
8+
todo!()
9+
}
10+
11+
fn map(_: Inv<impl FnOnce(), impl Fn()>) {}
12+
13+
fn traverse() {
14+
map(pass_through(|| ()))
15+
}
16+
17+
fn main() {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ check-pass
2+
3+
// Regression test for <https://github.com/rust-lang/rust/issues/153849>
4+
5+
#[expect(dead_code)]
6+
// Must be invariant
7+
pub struct Server<T>(*mut T);
8+
impl<T> Server<T> {
9+
fn new(_: T) -> Self
10+
where
11+
// Must be higher-ranked
12+
T: Fn(&mut i32),
13+
{
14+
todo!()
15+
}
16+
}
17+
18+
fn main() {
19+
// Must have a type annotation
20+
let _: Server<_> = Server::new(|_| ());
21+
}

0 commit comments

Comments
 (0)