mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-06 04:08:40 +00:00
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 #18653 cc #40951
This commit is contained in:
parent
3a5bbf89b2
commit
bca56e82a1
@ -264,20 +264,27 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to generalize `ty` for the type variable `for_vid`. This checks for cycle -- that
|
/// Attempts to generalize `ty` for the type variable `for_vid`.
|
||||||
/// is, whether the type `ty` references `for_vid`. If `make_region_vars` is true, it will also
|
/// This checks for cycle -- that is, whether the type `ty`
|
||||||
/// replace all regions with fresh variables. Returns `TyError` in the case of a cycle, `Ok`
|
/// references `for_vid`. If `make_region_vars` is true, it will
|
||||||
/// otherwise.
|
/// also replace all regions with fresh variables. Returns
|
||||||
|
/// `TyError` in the case of a cycle, `Ok` otherwise.
|
||||||
|
///
|
||||||
|
/// Preconditions:
|
||||||
|
///
|
||||||
|
/// - `for_vid` is a "root vid"
|
||||||
fn generalize(&self,
|
fn generalize(&self,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
for_vid: ty::TyVid,
|
for_vid: ty::TyVid,
|
||||||
make_region_vars: bool)
|
make_region_vars: bool)
|
||||||
-> RelateResult<'tcx, Ty<'tcx>>
|
-> RelateResult<'tcx, Ty<'tcx>>
|
||||||
{
|
{
|
||||||
|
debug_assert!(self.infcx.type_variables.borrow_mut().root_var(for_vid) == for_vid);
|
||||||
|
|
||||||
let mut generalize = Generalizer {
|
let mut generalize = Generalizer {
|
||||||
infcx: self.infcx,
|
infcx: self.infcx,
|
||||||
span: self.trace.cause.span,
|
span: self.trace.cause.span,
|
||||||
for_vid: for_vid,
|
for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
|
||||||
make_region_vars: make_region_vars,
|
make_region_vars: make_region_vars,
|
||||||
cycle_detected: false
|
cycle_detected: false
|
||||||
};
|
};
|
||||||
@ -293,7 +300,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
|
|||||||
struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
|
struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
|
||||||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
for_vid: ty::TyVid,
|
for_vid_sub_root: ty::TyVid,
|
||||||
make_region_vars: bool,
|
make_region_vars: bool,
|
||||||
cycle_detected: bool,
|
cycle_detected: bool,
|
||||||
}
|
}
|
||||||
@ -305,17 +312,17 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx
|
|||||||
|
|
||||||
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
// Check to see whether the type we are genealizing references
|
// Check to see whether the type we are genealizing references
|
||||||
// `vid`. At the same time, also update any type variables to
|
// any other type variable related to `vid` via
|
||||||
// the values that they are bound to. This is needed to truly
|
// subtyping. This is basically our "occurs check", preventing
|
||||||
// check for cycles, but also just makes things readable.
|
// us from creating infinitely sized types.
|
||||||
//
|
|
||||||
// (In particular, you could have something like `$0 = Box<$1>`
|
|
||||||
// where `$1` has already been instantiated with `Box<$0>`)
|
|
||||||
match t.sty {
|
match t.sty {
|
||||||
ty::TyInfer(ty::TyVar(vid)) => {
|
ty::TyInfer(ty::TyVar(vid)) => {
|
||||||
let mut variables = self.infcx.type_variables.borrow_mut();
|
let mut variables = self.infcx.type_variables.borrow_mut();
|
||||||
let vid = variables.root_var(vid);
|
let vid = variables.root_var(vid);
|
||||||
if vid == self.for_vid {
|
let sub_vid = variables.sub_root_var(vid);
|
||||||
|
if sub_vid == self.for_vid_sub_root {
|
||||||
|
// If sub-roots are equal, then `for_vid` and
|
||||||
|
// `vid` are related via subtyping.
|
||||||
self.cycle_detected = true;
|
self.cycle_detected = true;
|
||||||
self.tcx().types.err
|
self.tcx().types.err
|
||||||
} else {
|
} else {
|
||||||
@ -324,7 +331,18 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx
|
|||||||
drop(variables);
|
drop(variables);
|
||||||
self.fold_ty(u)
|
self.fold_ty(u)
|
||||||
}
|
}
|
||||||
None => t,
|
None => {
|
||||||
|
if self.make_region_vars {
|
||||||
|
let origin = variables.origin(vid);
|
||||||
|
let new_var_id = variables.new_var(false, origin, None);
|
||||||
|
let u = self.tcx().mk_var(new_var_id);
|
||||||
|
debug!("generalize: replacing original vid={:?} with new={:?}",
|
||||||
|
vid, u);
|
||||||
|
u
|
||||||
|
} else {
|
||||||
|
t
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use ty::{self, TyCtxt};
|
use infer::type_variable::TypeVariableMap;
|
||||||
|
use ty::{self, Ty, TyCtxt};
|
||||||
use ty::fold::{TypeFoldable, TypeFolder};
|
use ty::fold::{TypeFoldable, TypeFolder};
|
||||||
|
|
||||||
use super::InferCtxt;
|
use super::InferCtxt;
|
||||||
@ -54,57 +55,52 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||||||
/// the actual types (`?T`, `Option<?T`) -- and remember that
|
/// the actual types (`?T`, `Option<?T`) -- and remember that
|
||||||
/// after the snapshot is popped, the variable `?T` is no longer
|
/// after the snapshot is popped, the variable `?T` is no longer
|
||||||
/// unified.
|
/// unified.
|
||||||
///
|
|
||||||
/// Assumptions:
|
|
||||||
/// - no new type variables are created during `f()` (asserted
|
|
||||||
/// below); this simplifies our logic since we don't have to
|
|
||||||
/// check for escaping type variables
|
|
||||||
pub fn fudge_regions_if_ok<T, E, F>(&self,
|
pub fn fudge_regions_if_ok<T, E, F>(&self,
|
||||||
origin: &RegionVariableOrigin,
|
origin: &RegionVariableOrigin,
|
||||||
f: F) -> Result<T, E> where
|
f: F) -> Result<T, E> where
|
||||||
F: FnOnce() -> Result<T, E>,
|
F: FnOnce() -> Result<T, E>,
|
||||||
T: TypeFoldable<'tcx>,
|
T: TypeFoldable<'tcx>,
|
||||||
{
|
{
|
||||||
let (region_vars, value) = self.probe(|snapshot| {
|
debug!("fudge_regions_if_ok(origin={:?})", origin);
|
||||||
let vars_at_start = self.type_variables.borrow().num_vars();
|
|
||||||
|
|
||||||
|
let (type_variables, region_vars, value) = self.probe(|snapshot| {
|
||||||
match f() {
|
match f() {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
let value = self.resolve_type_vars_if_possible(&value);
|
let value = self.resolve_type_vars_if_possible(&value);
|
||||||
|
|
||||||
// At this point, `value` could in principle refer
|
// At this point, `value` could in principle refer
|
||||||
// to regions that have been created during the
|
// to types/regions that have been created during
|
||||||
// snapshot (we assert below that `f()` does not
|
// the snapshot. Once we exit `probe()`, those are
|
||||||
// create any new type variables, so there
|
// going to be popped, so we will have to
|
||||||
// shouldn't be any of those). Once we exit
|
// eliminate any references to them.
|
||||||
// `probe()`, those are going to be popped, so we
|
|
||||||
// will have to eliminate any references to them.
|
|
||||||
|
|
||||||
assert_eq!(self.type_variables.borrow().num_vars(), vars_at_start,
|
let type_variables =
|
||||||
"type variables were created during fudge_regions_if_ok");
|
self.type_variables.borrow_mut().types_created_since_snapshot(
|
||||||
|
&snapshot.type_snapshot);
|
||||||
let region_vars =
|
let region_vars =
|
||||||
self.region_vars.vars_created_since_snapshot(
|
self.region_vars.vars_created_since_snapshot(
|
||||||
&snapshot.region_vars_snapshot);
|
&snapshot.region_vars_snapshot);
|
||||||
|
|
||||||
Ok((region_vars, value))
|
Ok((type_variables, region_vars, value))
|
||||||
}
|
}
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// At this point, we need to replace any of the now-popped
|
// At this point, we need to replace any of the now-popped
|
||||||
// region variables that appear in `value` with a fresh region
|
// type/region variables that appear in `value` with a fresh
|
||||||
// variable. We can't do this during the probe because they
|
// variable of the appropriate kind. We can't do this during
|
||||||
// would just get popped then too. =)
|
// the probe because they would just get popped then too. =)
|
||||||
|
|
||||||
// Micro-optimization: if no variables have been created, then
|
// Micro-optimization: if no variables have been created, then
|
||||||
// `value` can't refer to any of them. =) So we can just return it.
|
// `value` can't refer to any of them. =) So we can just return it.
|
||||||
if region_vars.is_empty() {
|
if type_variables.is_empty() && region_vars.is_empty() {
|
||||||
return Ok(value);
|
return Ok(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fudger = RegionFudger {
|
let mut fudger = RegionFudger {
|
||||||
infcx: self,
|
infcx: self,
|
||||||
|
type_variables: &type_variables,
|
||||||
region_vars: ®ion_vars,
|
region_vars: ®ion_vars,
|
||||||
origin: origin
|
origin: origin
|
||||||
};
|
};
|
||||||
@ -115,6 +111,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
pub struct RegionFudger<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
pub struct RegionFudger<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||||
|
type_variables: &'a TypeVariableMap,
|
||||||
region_vars: &'a Vec<ty::RegionVid>,
|
region_vars: &'a Vec<ty::RegionVid>,
|
||||||
origin: &'a RegionVariableOrigin,
|
origin: &'a RegionVariableOrigin,
|
||||||
}
|
}
|
||||||
@ -124,6 +121,40 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> {
|
|||||||
self.infcx.tcx
|
self.infcx.tcx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
|
match ty.sty {
|
||||||
|
ty::TyInfer(ty::InferTy::TyVar(vid)) => {
|
||||||
|
match self.type_variables.get(&vid) {
|
||||||
|
None => {
|
||||||
|
// This variable was created before the
|
||||||
|
// "fudging". Since we refresh all type
|
||||||
|
// variables to their binding anyhow, we know
|
||||||
|
// that it is unbound, so we can just return
|
||||||
|
// it.
|
||||||
|
debug_assert!(self.infcx.type_variables.borrow_mut().probe(vid).is_none());
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(info) => {
|
||||||
|
// This variable was created during the
|
||||||
|
// fudging; it was mapped the root
|
||||||
|
// `root_vid`. There are now two
|
||||||
|
// possibilities: either the root was creating
|
||||||
|
// during the fudging too, in which case we
|
||||||
|
// want a fresh variable, or it was not, in
|
||||||
|
// which case we can return it.
|
||||||
|
if self.type_variables.contains_key(&info.root_vid) {
|
||||||
|
self.infcx.next_ty_var(info.root_origin)
|
||||||
|
} else {
|
||||||
|
self.infcx.tcx.mk_var(info.root_vid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ty.super_fold_with(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn fold_region(&mut self, r: &'tcx ty::Region) -> &'tcx ty::Region {
|
fn fold_region(&mut self, r: &'tcx ty::Region) -> &'tcx ty::Region {
|
||||||
match *r {
|
match *r {
|
||||||
ty::ReVar(v) if self.region_vars.contains(&v) => {
|
ty::ReVar(v) if self.region_vars.contains(&v) => {
|
||||||
|
@ -1036,9 +1036,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||||||
self.probe(|_| {
|
self.probe(|_| {
|
||||||
let origin = &ObligationCause::dummy();
|
let origin = &ObligationCause::dummy();
|
||||||
let trace = TypeTrace::types(origin, true, a, b);
|
let trace = TypeTrace::types(origin, true, a, b);
|
||||||
self.sub(true, trace, &a, &b).map(|InferOk { obligations, .. }| {
|
self.sub(true, trace, &a, &b).map(|InferOk { obligations: _, .. }| {
|
||||||
// FIXME(#32730) propagate obligations
|
// Ignore obligations, since we are unrolling
|
||||||
assert!(obligations.is_empty());
|
// everything anyway.
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
|
|||||||
let a = infcx.type_variables.borrow_mut().replace_if_possible(a);
|
let a = infcx.type_variables.borrow_mut().replace_if_possible(a);
|
||||||
let b = infcx.type_variables.borrow_mut().replace_if_possible(b);
|
let b = infcx.type_variables.borrow_mut().replace_if_possible(b);
|
||||||
match (&a.sty, &b.sty) {
|
match (&a.sty, &b.sty) {
|
||||||
(&ty::TyInfer(TyVar(_)), &ty::TyInfer(TyVar(_))) => {
|
(&ty::TyInfer(TyVar(a_vid)), &ty::TyInfer(TyVar(b_vid))) => {
|
||||||
// Shouldn't have any LBR here, so we can safely put
|
// Shouldn't have any LBR here, so we can safely put
|
||||||
// this under a binder below without fear of accidental
|
// this under a binder below without fear of accidental
|
||||||
// capture.
|
// capture.
|
||||||
@ -88,7 +88,11 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
|
|||||||
assert!(!b.has_escaping_regions());
|
assert!(!b.has_escaping_regions());
|
||||||
|
|
||||||
// can't make progress on `A <: B` if both A and B are
|
// can't make progress on `A <: B` if both A and B are
|
||||||
// type variables, so record an obligation.
|
// type variables, so record an obligation. We also
|
||||||
|
// have to record in the `type_variables` tracker that
|
||||||
|
// the two variables are equal modulo subtyping, which
|
||||||
|
// is important to the occurs check later on.
|
||||||
|
infcx.type_variables.borrow_mut().sub(a_vid, b_vid);
|
||||||
self.fields.obligations.push(
|
self.fields.obligations.push(
|
||||||
Obligation::new(
|
Obligation::new(
|
||||||
self.fields.trace.cause.clone(),
|
self.fields.trace.cause.clone(),
|
||||||
|
@ -18,16 +18,39 @@ use std::cmp::min;
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_data_structures::snapshot_vec as sv;
|
use rustc_data_structures::snapshot_vec as sv;
|
||||||
use rustc_data_structures::unify as ut;
|
use rustc_data_structures::unify as ut;
|
||||||
|
|
||||||
pub struct TypeVariableTable<'tcx> {
|
pub struct TypeVariableTable<'tcx> {
|
||||||
values: sv::SnapshotVec<Delegate<'tcx>>,
|
values: sv::SnapshotVec<Delegate<'tcx>>,
|
||||||
|
|
||||||
|
/// Two variables are unified in `eq_relations` when we have a
|
||||||
|
/// constraint `?X == ?Y`.
|
||||||
eq_relations: ut::UnificationTable<ty::TyVid>,
|
eq_relations: ut::UnificationTable<ty::TyVid>,
|
||||||
|
|
||||||
|
/// Two variables are unified in `eq_relations` when we have a
|
||||||
|
/// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second
|
||||||
|
/// table exists only to help with the occurs check. In particular,
|
||||||
|
/// we want to report constraints like these as an occurs check
|
||||||
|
/// violation:
|
||||||
|
///
|
||||||
|
/// ?1 <: ?3
|
||||||
|
/// Box<?3> <: ?1
|
||||||
|
///
|
||||||
|
/// This works because `?1` and `?3` are unified in the
|
||||||
|
/// `sub_relations` relation (not in `eq_relations`). Then when we
|
||||||
|
/// process the `Box<?3> <: ?1` constraint, we do an occurs check
|
||||||
|
/// on `Box<?3>` and find a potential cycle.
|
||||||
|
///
|
||||||
|
/// This is reasonable because, in Rust, subtypes have the same
|
||||||
|
/// "skeleton" and hence there is no possible type such that
|
||||||
|
/// (e.g.) `Box<?3> <: ?3` for any `?3`.
|
||||||
|
sub_relations: ut::UnificationTable<ty::TyVid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reasons to create a type inference variable
|
/// Reasons to create a type inference variable
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum TypeVariableOrigin {
|
pub enum TypeVariableOrigin {
|
||||||
MiscVariable(Span),
|
MiscVariable(Span),
|
||||||
NormalizeProjectionType(Span),
|
NormalizeProjectionType(Span),
|
||||||
@ -41,6 +64,14 @@ pub enum TypeVariableOrigin {
|
|||||||
DivergingBlockExpr(Span),
|
DivergingBlockExpr(Span),
|
||||||
DivergingFn(Span),
|
DivergingFn(Span),
|
||||||
LatticeVariable(Span),
|
LatticeVariable(Span),
|
||||||
|
Generalized(ty::TyVid),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TypeVariableMap = FxHashMap<ty::TyVid, TypeVariableInfo>;
|
||||||
|
|
||||||
|
pub struct TypeVariableInfo {
|
||||||
|
pub root_vid: ty::TyVid,
|
||||||
|
pub root_origin: TypeVariableOrigin,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypeVariableData<'tcx> {
|
struct TypeVariableData<'tcx> {
|
||||||
@ -70,6 +101,7 @@ pub struct Default<'tcx> {
|
|||||||
pub struct Snapshot {
|
pub struct Snapshot {
|
||||||
snapshot: sv::Snapshot,
|
snapshot: sv::Snapshot,
|
||||||
eq_snapshot: ut::Snapshot<ty::TyVid>,
|
eq_snapshot: ut::Snapshot<ty::TyVid>,
|
||||||
|
sub_snapshot: ut::Snapshot<ty::TyVid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Instantiate<'tcx> {
|
struct Instantiate<'tcx> {
|
||||||
@ -84,6 +116,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
|||||||
TypeVariableTable {
|
TypeVariableTable {
|
||||||
values: sv::SnapshotVec::new(),
|
values: sv::SnapshotVec::new(),
|
||||||
eq_relations: ut::UnificationTable::new(),
|
eq_relations: ut::UnificationTable::new(),
|
||||||
|
sub_relations: ut::UnificationTable::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +142,16 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
|||||||
debug_assert!(self.probe(a).is_none());
|
debug_assert!(self.probe(a).is_none());
|
||||||
debug_assert!(self.probe(b).is_none());
|
debug_assert!(self.probe(b).is_none());
|
||||||
self.eq_relations.union(a, b);
|
self.eq_relations.union(a, b);
|
||||||
|
self.sub_relations.union(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Records that `a <: b`, depending on `dir`.
|
||||||
|
///
|
||||||
|
/// Precondition: neither `a` nor `b` are known.
|
||||||
|
pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) {
|
||||||
|
debug_assert!(self.probe(a).is_none());
|
||||||
|
debug_assert!(self.probe(b).is_none());
|
||||||
|
self.sub_relations.union(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiates `vid` with the type `ty`.
|
/// Instantiates `vid` with the type `ty`.
|
||||||
@ -141,6 +184,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
|||||||
default: Option<Default<'tcx>>,) -> ty::TyVid {
|
default: Option<Default<'tcx>>,) -> ty::TyVid {
|
||||||
debug!("new_var(diverging={:?}, origin={:?})", diverging, origin);
|
debug!("new_var(diverging={:?}, origin={:?})", diverging, origin);
|
||||||
self.eq_relations.new_key(());
|
self.eq_relations.new_key(());
|
||||||
|
self.sub_relations.new_key(());
|
||||||
let index = self.values.push(TypeVariableData {
|
let index = self.values.push(TypeVariableData {
|
||||||
value: Bounded { default: default },
|
value: Bounded { default: default },
|
||||||
origin: origin,
|
origin: origin,
|
||||||
@ -155,15 +199,41 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
|||||||
self.values.len()
|
self.values.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the "root" variable of `vid` in the `eq_relations`
|
||||||
|
/// equivalence table. All type variables that have been equated
|
||||||
|
/// will yield the same root variable (per the union-find
|
||||||
|
/// algorithm), so `root_var(a) == root_var(b)` implies that `a ==
|
||||||
|
/// b` (transitively).
|
||||||
pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
|
pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
|
||||||
self.eq_relations.find(vid)
|
self.eq_relations.find(vid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the "root" variable of `vid` in the `sub_relations`
|
||||||
|
/// equivalence table. All type variables that have been are
|
||||||
|
/// related via equality or subtyping will yield the same root
|
||||||
|
/// variable (per the union-find algorithm), so `sub_root_var(a)
|
||||||
|
/// == sub_root_var(b)` implies that:
|
||||||
|
///
|
||||||
|
/// exists X. (a <: X || X <: a) && (b <: X || X <: b)
|
||||||
|
pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
|
||||||
|
self.sub_relations.find(vid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if `a` and `b` have same "sub-root" (i.e., exists some
|
||||||
|
/// type X such that `forall i in {a, b}. (i <: X || X <: i)`.
|
||||||
|
pub fn sub_unified(&mut self, a: ty::TyVid, b: ty::TyVid) -> bool {
|
||||||
|
self.sub_root_var(a) == self.sub_root_var(b)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn probe(&mut self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
|
pub fn probe(&mut self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
|
||||||
let vid = self.root_var(vid);
|
let vid = self.root_var(vid);
|
||||||
self.probe_root(vid)
|
self.probe_root(vid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn origin(&self, vid: ty::TyVid) -> TypeVariableOrigin {
|
||||||
|
self.values.get(vid.index as usize).origin.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves the type of `vid` given that it is currently a root in the unification table
|
/// Retrieves the type of `vid` given that it is currently a root in the unification table
|
||||||
pub fn probe_root(&mut self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
|
pub fn probe_root(&mut self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
|
||||||
debug_assert!(self.root_var(vid) == vid);
|
debug_assert!(self.root_var(vid) == vid);
|
||||||
@ -189,6 +259,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
|||||||
Snapshot {
|
Snapshot {
|
||||||
snapshot: self.values.start_snapshot(),
|
snapshot: self.values.start_snapshot(),
|
||||||
eq_snapshot: self.eq_relations.snapshot(),
|
eq_snapshot: self.eq_relations.snapshot(),
|
||||||
|
sub_snapshot: self.sub_relations.snapshot(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,13 +275,40 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.values.rollback_to(s.snapshot);
|
let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
|
||||||
self.eq_relations.rollback_to(s.eq_snapshot);
|
self.values.rollback_to(snapshot);
|
||||||
|
self.eq_relations.rollback_to(eq_snapshot);
|
||||||
|
self.sub_relations.rollback_to(sub_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit(&mut self, s: Snapshot) {
|
pub fn commit(&mut self, s: Snapshot) {
|
||||||
self.values.commit(s.snapshot);
|
let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
|
||||||
self.eq_relations.commit(s.eq_snapshot);
|
self.values.commit(snapshot);
|
||||||
|
self.eq_relations.commit(eq_snapshot);
|
||||||
|
self.sub_relations.commit(sub_snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a map `{V1 -> V2}`, where the keys `{V1}` are
|
||||||
|
/// ty-variables created during the snapshot, and the values
|
||||||
|
/// `{V2}` are the root variables that they were unified with,
|
||||||
|
/// along with their origin.
|
||||||
|
pub fn types_created_since_snapshot(&mut self, s: &Snapshot) -> TypeVariableMap {
|
||||||
|
let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
|
||||||
|
let eq_relations = &mut self.eq_relations;
|
||||||
|
let values = &self.values;
|
||||||
|
|
||||||
|
actions_since_snapshot
|
||||||
|
.iter()
|
||||||
|
.filter_map(|action| match action {
|
||||||
|
&sv::UndoLog::NewElem(index) => Some(ty::TyVid { index: index as u32 }),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.map(|vid| {
|
||||||
|
let root_vid = eq_relations.find(vid);
|
||||||
|
let root_origin = values.get(vid.index as usize).origin.clone();
|
||||||
|
(vid, TypeVariableInfo { root_vid, root_origin })
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn types_escaping_snapshot(&mut self, s: &Snapshot) -> Vec<Ty<'tcx>> {
|
pub fn types_escaping_snapshot(&mut self, s: &Snapshot) -> Vec<Ty<'tcx>> {
|
||||||
|
@ -69,6 +69,19 @@ struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
|
|||||||
found_pattern: Option<&'a Pat>,
|
found_pattern: Option<&'a Pat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
|
||||||
|
fn is_match(&self, ty: Ty<'tcx>) -> bool {
|
||||||
|
ty == *self.target_ty || match (&ty.sty, &self.target_ty.sty) {
|
||||||
|
(&ty::TyInfer(ty::TyVar(a_vid)), &ty::TyInfer(ty::TyVar(b_vid))) =>
|
||||||
|
self.infcx.type_variables
|
||||||
|
.borrow_mut()
|
||||||
|
.sub_unified(a_vid, b_vid),
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> Visitor<'a> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> Visitor<'a> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
|
||||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> {
|
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> {
|
||||||
NestedVisitorMap::None
|
NestedVisitorMap::None
|
||||||
@ -77,7 +90,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'a> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
|
|||||||
fn visit_local(&mut self, local: &'a Local) {
|
fn visit_local(&mut self, local: &'a Local) {
|
||||||
if let Some(&ty) = self.infcx.tables.borrow().node_types.get(&local.id) {
|
if let Some(&ty) = self.infcx.tables.borrow().node_types.get(&local.id) {
|
||||||
let ty = self.infcx.resolve_type_vars_if_possible(&ty);
|
let ty = self.infcx.resolve_type_vars_if_possible(&ty);
|
||||||
let is_match = ty.walk().any(|t| t == *self.target_ty);
|
let is_match = ty.walk().any(|t| self.is_match(t));
|
||||||
|
|
||||||
if is_match && self.found_pattern.is_none() {
|
if is_match && self.found_pattern.is_none() {
|
||||||
self.found_pattern = Some(&*local.pat);
|
self.found_pattern = Some(&*local.pat);
|
||||||
@ -564,8 +577,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ty::Predicate::Subtype(ref predicate) => {
|
ty::Predicate::Subtype(ref predicate) => {
|
||||||
// TODO
|
// Errors for Subtype predicates show up as
|
||||||
panic!("subtype requirement not satisfied {:?}", predicate)
|
// `FulfillmentErrorCode::CodeSubtypeError`,
|
||||||
|
// not selection error.
|
||||||
|
span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate)
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Predicate::Equate(ref predicate) => {
|
ty::Predicate::Equate(ref predicate) => {
|
||||||
@ -779,7 +794,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||||||
// no need to overload user in such cases
|
// no need to overload user in such cases
|
||||||
} else {
|
} else {
|
||||||
let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder();
|
let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder();
|
||||||
assert!(a.is_ty_var() && b.is_ty_var()); // else other would've been instantiated
|
// both must be type variables, or the other would've been instantiated
|
||||||
|
assert!(a.is_ty_var() && b.is_ty_var());
|
||||||
self.need_type_info(obligation, a);
|
self.need_type_info(obligation, a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
src/test/run-pass/issue-40951.rs
Normal file
20
src/test/run-pass/issue-40951.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Regression test for #40951.
|
||||||
|
|
||||||
|
const FOO: [&'static str; 1] = ["foo"];
|
||||||
|
|
||||||
|
fn find<T: PartialEq>(t: &[T], element: &T) { }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = format!("hi");
|
||||||
|
find(&FOO, &&*x);
|
||||||
|
}
|
60
src/test/run-pass/type-infer-generalize-ty-var.rs
Normal file
60
src/test/run-pass/type-infer-generalize-ty-var.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Test a scenario where we generate a constraint like `?1 <: &?2`.
|
||||||
|
// In such a case, it is important that we instantiate `?1` with `&?3`
|
||||||
|
// where `?3 <: ?2`, and not with `&?2`. This is a regression test for
|
||||||
|
// #18653. The important thing is that we build.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
enum Wrap<A> {
|
||||||
|
WrapSome(A),
|
||||||
|
WrapNone
|
||||||
|
}
|
||||||
|
|
||||||
|
use Wrap::*;
|
||||||
|
|
||||||
|
struct T;
|
||||||
|
struct U;
|
||||||
|
|
||||||
|
trait Get<T: ?Sized> {
|
||||||
|
fn get(&self) -> &T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Get<MyShow + 'static> for Wrap<T> {
|
||||||
|
fn get(&self) -> &(MyShow + 'static) {
|
||||||
|
static x: usize = 42;
|
||||||
|
&x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Get<usize> for Wrap<U> {
|
||||||
|
fn get(&self) -> &usize {
|
||||||
|
static x: usize = 55;
|
||||||
|
&x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MyShow { fn dummy(&self) { } }
|
||||||
|
impl<'a> MyShow for &'a (MyShow + 'a) { }
|
||||||
|
impl MyShow for usize { }
|
||||||
|
fn constrain<'a>(rc: RefCell<&'a (MyShow + 'a)>) { }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut collection: Wrap<_> = WrapNone;
|
||||||
|
|
||||||
|
{
|
||||||
|
let __arg0 = Get::get(&collection);
|
||||||
|
let __args_cell = RefCell::new(__arg0);
|
||||||
|
constrain(__args_cell);
|
||||||
|
}
|
||||||
|
collection = WrapSome(T);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user