-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
bors
merged 19 commits into
rust-lang:master
from
nikomatsakis:inference-subtype-through-obligation
Apr 13, 2017
Merged
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
4a0a0e9
remove type variable defaults code
nikomatsakis 18ea55f
remove bivariance
nikomatsakis 58609ef
add Subtype predicate
nikomatsakis 4e4bdea
propagate sub-obligations better
nikomatsakis 105ec7e
use obligations to propagate sub-typing instead of the TV code
nikomatsakis e58e2b4
remove the subtyping relations from TypeVariable
nikomatsakis e4b762b
add regression test for #30225
nikomatsakis d1033d0
add FIXME to #18653
nikomatsakis 77d9e38
add FIXME for bivariant lub/glb
nikomatsakis 14f1e34
fix a bug in compiletest JSON parsing for duplicate errors
nikomatsakis 3a5bbf8
avoid unneeded subtype obligations in lub/glb
nikomatsakis bca56e8
generalize type variables too
nikomatsakis 1c138ed
update various test cases that generate slightly different output
nikomatsakis 761808e
just panic in rustdoc if we encounter a subtype predicate
nikomatsakis 59babd8
add some comments and `debug!` calls to "obligation forest"
nikomatsakis 0fae332
add some debug! to coercion
nikomatsakis 7832db8
fix long line
nikomatsakis fa437f4
do not consult union-find during `fudge_regions_if_ok`
nikomatsakis 1cc7621
simplify code to remove now unused "stack" and fix comments
nikomatsakis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}; | ||
|
@@ -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, | ||
|
@@ -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) | ||
} | ||
|
@@ -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(); | ||
|
@@ -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), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
} | ||
}; | ||
|
@@ -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( | ||
|
@@ -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 | ||
}; | ||
|
@@ -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, | ||
} | ||
|
@@ -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 { | ||
|
@@ -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 | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.