Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle subtyping in inference through obligations #40570

Merged
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ impl_stable_hash_for!(enum ty::Visibility {
impl_stable_hash_for!(struct ty::TraitRef<'tcx> { def_id, substs });
impl_stable_hash_for!(struct ty::TraitPredicate<'tcx> { trait_ref });
impl_stable_hash_for!(tuple_struct ty::EquatePredicate<'tcx> { t1, t2 });
impl_stable_hash_for!(struct ty::SubtypePredicate<'tcx> { a_is_expected, a, b });

impl<'a, 'tcx, A, B> HashStable<StableHashingContext<'a, 'tcx>> for ty::OutlivesPredicate<A, B>
where A: HashStable<StableHashingContext<'a, 'tcx>>,
Expand Down Expand Up @@ -200,6 +201,9 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for ty::Predicate<'tcx
ty::Predicate::Equate(ref pred) => {
pred.hash_stable(hcx, hasher);
}
ty::Predicate::Subtype(ref pred) => {
pred.hash_stable(hcx, hasher);
}
ty::Predicate::RegionOutlives(ref pred) => {
pred.hash_stable(hcx, hasher);
}
Expand Down
123 changes: 0 additions & 123 deletions src/librustc/infer/bivariate.rs

This file was deleted.

68 changes: 44 additions & 24 deletions src/librustc/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,12 @@
// is also useful to track which value is the "expected" value in
// terms of error reporting.

use super::bivariate::Bivariate;
use super::equate::Equate;
use super::glb::Glb;
use super::lub::Lub;
use super::sub::Sub;
use super::InferCtxt;
use super::{MiscVariable, TypeTrace};
use super::type_variable::{RelationDir, BiTo, EqTo, SubtypeOf, SupertypeOf};

use ty::{IntType, UintType};
use ty::{self, Ty, TyCtxt};
Expand All @@ -60,6 +58,11 @@ pub struct CombineFields<'infcx, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> {
pub obligations: PredicateObligations<'tcx>,
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum RelationDir {
SubtypeOf, SupertypeOf, EqTo
}

impl<'infcx, 'gcx, 'tcx> InferCtxt<'infcx, 'gcx, 'tcx> {
pub fn super_combine_tys<R>(&self,
relation: &mut R,
Expand Down Expand Up @@ -159,10 +162,6 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
Equate::new(self, a_is_expected)
}

pub fn bivariate<'a>(&'a mut self, a_is_expected: bool) -> Bivariate<'a, 'infcx, 'gcx, 'tcx> {
Bivariate::new(self, a_is_expected)
}

pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'gcx, 'tcx> {
Sub::new(self, a_is_expected)
}
Expand All @@ -182,6 +181,8 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
a_is_expected: bool)
-> RelateResult<'tcx, ()>
{
use self::RelationDir::*;

// We use SmallVector here instead of Vec because this code is hot and
// it's rare that the stack length exceeds 1.
let mut stack = SmallVector::new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: do we even need the stack and the loop here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yeah, I think I meant to remove that! Thanks.

Expand Down Expand Up @@ -223,22 +224,24 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
// Check whether `vid` has been instantiated yet. If not,
// make a generalized form of `ty` and instantiate with
// that.
//
// FIXME(#18653) -- we need to generalize nested type
// variables too.
let b_ty = match b_ty {
Some(t) => t, // ...already instantiated.
None => { // ...not yet instantiated:
// Generalize type if necessary.
let generalized_ty = match dir {
EqTo => self.generalize(a_ty, b_vid, false),
BiTo | SupertypeOf | SubtypeOf => self.generalize(a_ty, b_vid, true),
SupertypeOf | SubtypeOf => self.generalize(a_ty, b_vid, true),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a FIXME(#18653) to generalize?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not needed anymore.

}?;
debug!("instantiate(a_ty={:?}, dir={:?}, \
b_vid={:?}, generalized_ty={:?})",
a_ty, dir, b_vid,
generalized_ty);
self.infcx.type_variables
.borrow_mut()
.instantiate_and_push(
b_vid, generalized_ty, &mut stack);
.instantiate(b_vid, generalized_ty);
generalized_ty
}
};
Expand All @@ -251,7 +254,6 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
// to associate causes/spans with each of the relations in
// the stack to get this right.
match dir {
BiTo => self.bivariate(a_is_expected).relate(&a_ty, &b_ty),
EqTo => self.equate(a_is_expected).relate(&a_ty, &b_ty),
SubtypeOf => self.sub(a_is_expected).relate(&a_ty, &b_ty),
SupertypeOf => self.sub(a_is_expected).relate_with_variance(
Expand All @@ -262,20 +264,27 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
Ok(())
}

/// Attempts to generalize `ty` for the type variable `for_vid`. This checks for cycle -- that
/// is, whether the type `ty` references `for_vid`. If `make_region_vars` is true, it will also
/// replace all regions with fresh variables. Returns `TyError` in the case of a cycle, `Ok`
/// otherwise.
/// Attempts to generalize `ty` for the type variable `for_vid`.
/// This checks for cycle -- that is, whether the type `ty`
/// references `for_vid`. If `make_region_vars` is true, it will
/// 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,
ty: Ty<'tcx>,
for_vid: ty::TyVid,
make_region_vars: bool)
-> RelateResult<'tcx, Ty<'tcx>>
{
debug_assert!(self.infcx.type_variables.borrow_mut().root_var(for_vid) == for_vid);

let mut generalize = Generalizer {
infcx: self.infcx,
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,
cycle_detected: false
};
Expand All @@ -291,7 +300,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
span: Span,
for_vid: ty::TyVid,
for_vid_sub_root: ty::TyVid,
make_region_vars: bool,
cycle_detected: bool,
}
Expand All @@ -303,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> {
// Check to see whether the type we are genealizing references
// `vid`. At the same time, also update any type variables to
// the values that they are bound to. This is needed to truly
// check for cycles, but also just makes things readable.
//
// (In particular, you could have something like `$0 = Box<$1>`
// where `$1` has already been instantiated with `Box<$0>`)
// any other type variable related to `vid` via
// subtyping. This is basically our "occurs check", preventing
// us from creating infinitely sized types.
match t.sty {
ty::TyInfer(ty::TyVar(vid)) => {
let mut variables = self.infcx.type_variables.borrow_mut();
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.tcx().types.err
} else {
Expand All @@ -322,7 +331,18 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx
drop(variables);
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
}
}
}
}
}
Expand Down
9 changes: 4 additions & 5 deletions src/librustc/infer/equate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::combine::CombineFields;
use super::combine::{CombineFields, RelationDir};
use super::{Subtype};
use super::type_variable::{EqTo};

use ty::{self, Ty, TyCtxt};
use ty::TyVar;
Expand Down Expand Up @@ -58,17 +57,17 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
let b = infcx.type_variables.borrow_mut().replace_if_possible(b);
match (&a.sty, &b.sty) {
(&ty::TyInfer(TyVar(a_id)), &ty::TyInfer(TyVar(b_id))) => {
infcx.type_variables.borrow_mut().relate_vars(a_id, EqTo, b_id);
infcx.type_variables.borrow_mut().equate(a_id, b_id);
Ok(a)
}

(&ty::TyInfer(TyVar(a_id)), _) => {
self.fields.instantiate(b, EqTo, a_id, self.a_is_expected)?;
self.fields.instantiate(b, RelationDir::EqTo, a_id, self.a_is_expected)?;
Ok(a)
}

(_, &ty::TyInfer(TyVar(b_id))) => {
self.fields.instantiate(a, EqTo, b_id, self.a_is_expected)?;
self.fields.instantiate(a, RelationDir::EqTo, b_id, self.a_is_expected)?;
Ok(a)
}

Expand Down
Loading