Skip to content

Commit bca56e8

Browse files
committed
generalize type variables too
When we are generalizing a super/sub-type, we have to replace type variables with a fresh variable (and not just region variables). So if we know that `Box<?T> <: ?U`, for example, we instantiate `?U` with `Box<?V>` and then relate `Box<?T>` to `Box<?V>` (and hence require that `?T <: ?V`). This change has some complex interactions, however: First, the occurs check must be updated to detect constraints like `?T <: ?U` and `?U <: Box<?T>`. If we're not careful, we'll create a never-ending sequence of new variables. To address this, we add a second unification set into `type_variables` that tracks type variables related through **either** equality **or** subtyping, and use that during the occurs-check. Second, the "fudge regions if ok" code was expecting no new type variables to be created. It must be updated to create new type variables outside of the probe. This is relatively straight-forward under the new scheme, since type variables are now independent from one another, and any relations are moderated by pending subtype obliations and so forth. This part would be tricky to backport though. cc rust-lang#18653 cc rust-lang#40951
1 parent 3a5bbf8 commit bca56e8

File tree

8 files changed

+297
-50
lines changed

8 files changed

+297
-50
lines changed

src/librustc/infer/combine.rs

+32-14
Original file line numberDiff line numberDiff line change
@@ -264,20 +264,27 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
264264
Ok(())
265265
}
266266

267-
/// Attempts to generalize `ty` for the type variable `for_vid`. This checks for cycle -- that
268-
/// is, whether the type `ty` references `for_vid`. If `make_region_vars` is true, it will also
269-
/// replace all regions with fresh variables. Returns `TyError` in the case of a cycle, `Ok`
270-
/// otherwise.
267+
/// Attempts to generalize `ty` for the type variable `for_vid`.
268+
/// This checks for cycle -- that is, whether the type `ty`
269+
/// references `for_vid`. If `make_region_vars` is true, it will
270+
/// also replace all regions with fresh variables. Returns
271+
/// `TyError` in the case of a cycle, `Ok` otherwise.
272+
///
273+
/// Preconditions:
274+
///
275+
/// - `for_vid` is a "root vid"
271276
fn generalize(&self,
272277
ty: Ty<'tcx>,
273278
for_vid: ty::TyVid,
274279
make_region_vars: bool)
275280
-> RelateResult<'tcx, Ty<'tcx>>
276281
{
282+
debug_assert!(self.infcx.type_variables.borrow_mut().root_var(for_vid) == for_vid);
283+
277284
let mut generalize = Generalizer {
278285
infcx: self.infcx,
279286
span: self.trace.cause.span,
280-
for_vid: for_vid,
287+
for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
281288
make_region_vars: make_region_vars,
282289
cycle_detected: false
283290
};
@@ -293,7 +300,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
293300
struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
294301
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
295302
span: Span,
296-
for_vid: ty::TyVid,
303+
for_vid_sub_root: ty::TyVid,
297304
make_region_vars: bool,
298305
cycle_detected: bool,
299306
}
@@ -305,17 +312,17 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx
305312

306313
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
307314
// Check to see whether the type we are genealizing references
308-
// `vid`. At the same time, also update any type variables to
309-
// the values that they are bound to. This is needed to truly
310-
// check for cycles, but also just makes things readable.
311-
//
312-
// (In particular, you could have something like `$0 = Box<$1>`
313-
// where `$1` has already been instantiated with `Box<$0>`)
315+
// any other type variable related to `vid` via
316+
// subtyping. This is basically our "occurs check", preventing
317+
// us from creating infinitely sized types.
314318
match t.sty {
315319
ty::TyInfer(ty::TyVar(vid)) => {
316320
let mut variables = self.infcx.type_variables.borrow_mut();
317321
let vid = variables.root_var(vid);
318-
if vid == self.for_vid {
322+
let sub_vid = variables.sub_root_var(vid);
323+
if sub_vid == self.for_vid_sub_root {
324+
// If sub-roots are equal, then `for_vid` and
325+
// `vid` are related via subtyping.
319326
self.cycle_detected = true;
320327
self.tcx().types.err
321328
} else {
@@ -324,7 +331,18 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx
324331
drop(variables);
325332
self.fold_ty(u)
326333
}
327-
None => t,
334+
None => {
335+
if self.make_region_vars {
336+
let origin = variables.origin(vid);
337+
let new_var_id = variables.new_var(false, origin, None);
338+
let u = self.tcx().mk_var(new_var_id);
339+
debug!("generalize: replacing original vid={:?} with new={:?}",
340+
vid, u);
341+
u
342+
} else {
343+
t
344+
}
345+
}
328346
}
329347
}
330348
}

src/librustc/infer/fudge.rs

+53-22
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use ty::{self, TyCtxt};
11+
use infer::type_variable::TypeVariableMap;
12+
use ty::{self, Ty, TyCtxt};
1213
use ty::fold::{TypeFoldable, TypeFolder};
1314

1415
use super::InferCtxt;
@@ -54,57 +55,52 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
5455
/// the actual types (`?T`, `Option<?T`) -- and remember that
5556
/// after the snapshot is popped, the variable `?T` is no longer
5657
/// unified.
57-
///
58-
/// Assumptions:
59-
/// - no new type variables are created during `f()` (asserted
60-
/// below); this simplifies our logic since we don't have to
61-
/// check for escaping type variables
6258
pub fn fudge_regions_if_ok<T, E, F>(&self,
6359
origin: &RegionVariableOrigin,
6460
f: F) -> Result<T, E> where
6561
F: FnOnce() -> Result<T, E>,
6662
T: TypeFoldable<'tcx>,
6763
{
68-
let (region_vars, value) = self.probe(|snapshot| {
69-
let vars_at_start = self.type_variables.borrow().num_vars();
64+
debug!("fudge_regions_if_ok(origin={:?})", origin);
7065

66+
let (type_variables, region_vars, value) = self.probe(|snapshot| {
7167
match f() {
7268
Ok(value) => {
7369
let value = self.resolve_type_vars_if_possible(&value);
7470

7571
// At this point, `value` could in principle refer
76-
// to regions that have been created during the
77-
// snapshot (we assert below that `f()` does not
78-
// create any new type variables, so there
79-
// shouldn't be any of those). Once we exit
80-
// `probe()`, those are going to be popped, so we
81-
// will have to eliminate any references to them.
82-
83-
assert_eq!(self.type_variables.borrow().num_vars(), vars_at_start,
84-
"type variables were created during fudge_regions_if_ok");
72+
// to types/regions that have been created during
73+
// the snapshot. Once we exit `probe()`, those are
74+
// going to be popped, so we will have to
75+
// eliminate any references to them.
76+
77+
let type_variables =
78+
self.type_variables.borrow_mut().types_created_since_snapshot(
79+
&snapshot.type_snapshot);
8580
let region_vars =
8681
self.region_vars.vars_created_since_snapshot(
8782
&snapshot.region_vars_snapshot);
8883

89-
Ok((region_vars, value))
84+
Ok((type_variables, region_vars, value))
9085
}
9186
Err(e) => Err(e),
9287
}
9388
})?;
9489

9590
// At this point, we need to replace any of the now-popped
96-
// region variables that appear in `value` with a fresh region
97-
// variable. We can't do this during the probe because they
98-
// would just get popped then too. =)
91+
// type/region variables that appear in `value` with a fresh
92+
// variable of the appropriate kind. We can't do this during
93+
// the probe because they would just get popped then too. =)
9994

10095
// Micro-optimization: if no variables have been created, then
10196
// `value` can't refer to any of them. =) So we can just return it.
102-
if region_vars.is_empty() {
97+
if type_variables.is_empty() && region_vars.is_empty() {
10398
return Ok(value);
10499
}
105100

106101
let mut fudger = RegionFudger {
107102
infcx: self,
103+
type_variables: &type_variables,
108104
region_vars: &region_vars,
109105
origin: origin
110106
};
@@ -115,6 +111,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
115111

116112
pub struct RegionFudger<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
117113
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
114+
type_variables: &'a TypeVariableMap,
118115
region_vars: &'a Vec<ty::RegionVid>,
119116
origin: &'a RegionVariableOrigin,
120117
}
@@ -124,6 +121,40 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> {
124121
self.infcx.tcx
125122
}
126123

124+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
125+
match ty.sty {
126+
ty::TyInfer(ty::InferTy::TyVar(vid)) => {
127+
match self.type_variables.get(&vid) {
128+
None => {
129+
// This variable was created before the
130+
// "fudging". Since we refresh all type
131+
// variables to their binding anyhow, we know
132+
// that it is unbound, so we can just return
133+
// it.
134+
debug_assert!(self.infcx.type_variables.borrow_mut().probe(vid).is_none());
135+
ty
136+
}
137+
138+
Some(info) => {
139+
// This variable was created during the
140+
// fudging; it was mapped the root
141+
// `root_vid`. There are now two
142+
// possibilities: either the root was creating
143+
// during the fudging too, in which case we
144+
// want a fresh variable, or it was not, in
145+
// which case we can return it.
146+
if self.type_variables.contains_key(&info.root_vid) {
147+
self.infcx.next_ty_var(info.root_origin)
148+
} else {
149+
self.infcx.tcx.mk_var(info.root_vid)
150+
}
151+
}
152+
}
153+
}
154+
_ => ty.super_fold_with(self),
155+
}
156+
}
157+
127158
fn fold_region(&mut self, r: &'tcx ty::Region) -> &'tcx ty::Region {
128159
match *r {
129160
ty::ReVar(v) if self.region_vars.contains(&v) => {

src/librustc/infer/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1036,9 +1036,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10361036
self.probe(|_| {
10371037
let origin = &ObligationCause::dummy();
10381038
let trace = TypeTrace::types(origin, true, a, b);
1039-
self.sub(true, trace, &a, &b).map(|InferOk { obligations, .. }| {
1040-
// FIXME(#32730) propagate obligations
1041-
assert!(obligations.is_empty());
1039+
self.sub(true, trace, &a, &b).map(|InferOk { obligations: _, .. }| {
1040+
// Ignore obligations, since we are unrolling
1041+
// everything anyway.
10421042
})
10431043
})
10441044
}

src/librustc/infer/sub.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,19 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
8080
let a = infcx.type_variables.borrow_mut().replace_if_possible(a);
8181
let b = infcx.type_variables.borrow_mut().replace_if_possible(b);
8282
match (&a.sty, &b.sty) {
83-
(&ty::TyInfer(TyVar(_)), &ty::TyInfer(TyVar(_))) => {
83+
(&ty::TyInfer(TyVar(a_vid)), &ty::TyInfer(TyVar(b_vid))) => {
8484
// Shouldn't have any LBR here, so we can safely put
8585
// this under a binder below without fear of accidental
8686
// capture.
8787
assert!(!a.has_escaping_regions());
8888
assert!(!b.has_escaping_regions());
8989

9090
// can't make progress on `A <: B` if both A and B are
91-
// type variables, so record an obligation.
91+
// type variables, so record an obligation. We also
92+
// have to record in the `type_variables` tracker that
93+
// the two variables are equal modulo subtyping, which
94+
// is important to the occurs check later on.
95+
infcx.type_variables.borrow_mut().sub(a_vid, b_vid);
9296
self.fields.obligations.push(
9397
Obligation::new(
9498
self.fields.trace.cause.clone(),

0 commit comments

Comments
 (0)