1
Fork 0

Implement proper subtyping for region fn types (part of #2263)

This commit is contained in:
Niko Matsakis 2012-10-19 06:01:01 -07:00
commit 1a3a70760b
20 changed files with 877 additions and 417 deletions

View file

@ -136,12 +136,23 @@ pub pure fn from_elem<T: Copy>(n_elts: uint, t: T) -> @[T] {
#[cfg(notest)] #[cfg(notest)]
pub mod traits { pub mod traits {
#[legacy_exports]; #[legacy_exports];
#[cfg(stage0)]
pub impl<T: Copy> @[T] : Add<&[const T],@[T]> { pub impl<T: Copy> @[T] : Add<&[const T],@[T]> {
#[inline(always)] #[inline(always)]
pure fn add(rhs: & &[const T]) -> @[T] { pure fn add(rhs: & &[const T]) -> @[T] {
append(self, (*rhs)) append(self, (*rhs))
} }
} }
#[cfg(stage1)]
#[cfg(stage2)]
pub impl<T: Copy> @[T] : Add<&[const T],@[T]> {
#[inline(always)]
pure fn add(rhs: & &self/[const T]) -> @[T] {
append(self, (*rhs))
}
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -226,12 +226,21 @@ impl<T> *const T : Ord {
} }
// Equality for region pointers // Equality for region pointers
#[cfg(stage0)]
impl<T:Eq> &const T : Eq { impl<T:Eq> &const T : Eq {
pure fn eq(other: & &const T) -> bool { return *self == *(*other); } pure fn eq(other: & &const T) -> bool { return *self == *(*other); }
pure fn ne(other: & &const T) -> bool { return *self != *(*other); } pure fn ne(other: & &const T) -> bool { return *self != *(*other); }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl<T:Eq> &const T : Eq {
pure fn eq(other: & &self/const T) -> bool { return *self == *(*other); }
pure fn ne(other: & &self/const T) -> bool { return *self != *(*other); }
}
// Comparison for region pointers // Comparison for region pointers
#[cfg(stage0)]
impl<T:Ord> &const T : Ord { impl<T:Ord> &const T : Ord {
pure fn lt(other: & &const T) -> bool { *self < *(*other) } pure fn lt(other: & &const T) -> bool { *self < *(*other) }
pure fn le(other: & &const T) -> bool { *self <= *(*other) } pure fn le(other: & &const T) -> bool { *self <= *(*other) }
@ -239,6 +248,15 @@ impl<T:Ord> &const T : Ord {
pure fn gt(other: & &const T) -> bool { *self > *(*other) } pure fn gt(other: & &const T) -> bool { *self > *(*other) }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl<T:Ord> &const T : Ord {
pure fn lt(other: & &self/const T) -> bool { *self < *(*other) }
pure fn le(other: & &self/const T) -> bool { *self <= *(*other) }
pure fn ge(other: & &self/const T) -> bool { *self >= *(*other) }
pure fn gt(other: & &self/const T) -> bool { *self > *(*other) }
}
#[test] #[test]
pub fn test() { pub fn test() {
unsafe { unsafe {

View file

@ -735,6 +735,7 @@ pure fn gt(a: &str, b: &str) -> bool {
!le(a, b) !le(a, b)
} }
#[cfg(stage0)]
impl &str : Eq { impl &str : Eq {
#[inline(always)] #[inline(always)]
pure fn eq(other: & &str) -> bool { pure fn eq(other: & &str) -> bool {
@ -744,6 +745,17 @@ impl &str : Eq {
pure fn ne(other: & &str) -> bool { !self.eq(other) } pure fn ne(other: & &str) -> bool { !self.eq(other) }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl &str : Eq {
#[inline(always)]
pure fn eq(other: & &self/str) -> bool {
eq_slice(self, (*other))
}
#[inline(always)]
pure fn ne(other: & &self/str) -> bool { !self.eq(other) }
}
impl ~str : Eq { impl ~str : Eq {
#[inline(always)] #[inline(always)]
pure fn eq(other: &~str) -> bool { pure fn eq(other: &~str) -> bool {
@ -773,6 +785,7 @@ impl ~str : Ord {
pure fn gt(other: &~str) -> bool { gt(self, (*other)) } pure fn gt(other: &~str) -> bool { gt(self, (*other)) }
} }
#[cfg(stage0)]
impl &str : Ord { impl &str : Ord {
#[inline(always)] #[inline(always)]
pure fn lt(other: & &str) -> bool { lt(self, (*other)) } pure fn lt(other: & &str) -> bool { lt(self, (*other)) }
@ -784,6 +797,19 @@ impl &str : Ord {
pure fn gt(other: & &str) -> bool { gt(self, (*other)) } pure fn gt(other: & &str) -> bool { gt(self, (*other)) }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl &str : Ord {
#[inline(always)]
pure fn lt(other: & &self/str) -> bool { lt(self, (*other)) }
#[inline(always)]
pure fn le(other: & &self/str) -> bool { le(self, (*other)) }
#[inline(always)]
pure fn ge(other: & &self/str) -> bool { ge(self, (*other)) }
#[inline(always)]
pure fn gt(other: & &self/str) -> bool { gt(self, (*other)) }
}
impl @str : Ord { impl @str : Ord {
#[inline(always)] #[inline(always)]
pure fn lt(other: &@str) -> bool { lt(self, (*other)) } pure fn lt(other: &@str) -> bool { lt(self, (*other)) }
@ -2096,12 +2122,22 @@ impl ~str: Trimmable {
#[cfg(notest)] #[cfg(notest)]
pub mod traits { pub mod traits {
#[cfg(stage0)]
impl ~str : Add<&str,~str> { impl ~str : Add<&str,~str> {
#[inline(always)] #[inline(always)]
pure fn add(rhs: & &str) -> ~str { pure fn add(rhs: & &str) -> ~str {
append(copy self, (*rhs)) append(copy self, (*rhs))
} }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl ~str : Add<&str,~str> {
#[inline(always)]
pure fn add(rhs: & &self/str) -> ~str {
append(copy self, (*rhs))
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -2558,7 +2594,7 @@ mod tests {
assert find_str_between(data, ~"ab", 2u, 4u).is_none(); assert find_str_between(data, ~"ab", 2u, 4u).is_none();
let mut data = ~"ประเทศไทย中华Việt Nam"; let mut data = ~"ประเทศไทย中华Việt Nam";
data += data; data = data + data;
assert find_str_between(data, ~"", 0u, 43u) == Some(0u); assert find_str_between(data, ~"", 0u, 43u) == Some(0u);
assert find_str_between(data, ~"", 6u, 43u) == Some(6u); assert find_str_between(data, ~"", 6u, 43u) == Some(6u);

View file

@ -736,6 +736,27 @@ pub pure fn filter<T: Copy>(v: &[T], f: fn(t: &T) -> bool) -> ~[T] {
move result move result
} }
/**
* Like `filter()`, but in place. Preserves order of `v`. Linear time.
*/
pub fn retain<T>(v: &mut ~[T], f: pure fn(t: &T) -> bool) {
let len = v.len();
let mut deleted: uint = 0;
for uint::range(0, len) |i| {
if !f(&v[i]) {
deleted += 1;
} else if deleted > 0 {
v[i - deleted] <-> v[i];
}
}
while deleted > 0 {
v.pop();
deleted -= 1;
}
}
/** /**
* Concatenate a vector of vectors. * Concatenate a vector of vectors.
* *
@ -759,14 +780,17 @@ pub pure fn connect<T: Copy>(v: &[~[T]], sep: &T) -> ~[T] {
} }
/// Reduce a vector from left to right /// Reduce a vector from left to right
pub pure fn foldl<T: Copy, U>(z: T, v: &[U], p: fn(t: T, u: &U) -> T) -> T { pub pure fn foldl<T, U>(z: T, v: &[U], p: fn(t: T, u: &U) -> T) -> T {
let mut accum = z; let mut accum = move z;
for each(v) |elt| { let mut i = 0;
// it should be possible to move accum in, but the liveness analysis let l = v.len();
// is not smart enough. while i < l {
accum = p(accum, elt); // Use a while loop so that liveness analysis can handle moving
// the accumulator.
accum = p(move accum, &v[i]);
i += 1;
} }
return accum; return move accum;
} }
/// Reduce a vector from right to left /// Reduce a vector from right to left
@ -1293,6 +1317,7 @@ pure fn eq<T: Eq>(a: &[T], b: &[T]) -> bool {
return true; return true;
} }
#[cfg(stage0)]
impl<T: Eq> &[T] : Eq { impl<T: Eq> &[T] : Eq {
#[inline(always)] #[inline(always)]
pure fn eq(other: & &[T]) -> bool { eq(self, (*other)) } pure fn eq(other: & &[T]) -> bool { eq(self, (*other)) }
@ -1300,6 +1325,16 @@ impl<T: Eq> &[T] : Eq {
pure fn ne(other: & &[T]) -> bool { !self.eq(other) } pure fn ne(other: & &[T]) -> bool { !self.eq(other) }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl<T: Eq> &[T] : Eq {
#[inline(always)]
pure fn eq(other: & &self/[T]) -> bool { eq(self, (*other)) }
#[inline(always)]
pure fn ne(other: & &self/[T]) -> bool { !self.eq(other) }
}
impl<T: Eq> ~[T] : Eq { impl<T: Eq> ~[T] : Eq {
#[inline(always)] #[inline(always)]
pure fn eq(other: &~[T]) -> bool { eq(self, (*other)) } pure fn eq(other: &~[T]) -> bool { eq(self, (*other)) }
@ -1335,6 +1370,7 @@ pure fn le<T: Ord>(a: &[T], b: &[T]) -> bool { !lt(b, a) }
pure fn ge<T: Ord>(a: &[T], b: &[T]) -> bool { !lt(a, b) } pure fn ge<T: Ord>(a: &[T], b: &[T]) -> bool { !lt(a, b) }
pure fn gt<T: Ord>(a: &[T], b: &[T]) -> bool { lt(b, a) } pure fn gt<T: Ord>(a: &[T], b: &[T]) -> bool { lt(b, a) }
#[cfg(stage0)]
impl<T: Ord> &[T] : Ord { impl<T: Ord> &[T] : Ord {
#[inline(always)] #[inline(always)]
pure fn lt(other: & &[T]) -> bool { lt(self, (*other)) } pure fn lt(other: & &[T]) -> bool { lt(self, (*other)) }
@ -1346,6 +1382,19 @@ impl<T: Ord> &[T] : Ord {
pure fn gt(other: & &[T]) -> bool { gt(self, (*other)) } pure fn gt(other: & &[T]) -> bool { gt(self, (*other)) }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl<T: Ord> &[T] : Ord {
#[inline(always)]
pure fn lt(other: & &self/[T]) -> bool { lt(self, (*other)) }
#[inline(always)]
pure fn le(other: & &self/[T]) -> bool { le(self, (*other)) }
#[inline(always)]
pure fn ge(other: & &self/[T]) -> bool { ge(self, (*other)) }
#[inline(always)]
pure fn gt(other: & &self/[T]) -> bool { gt(self, (*other)) }
}
impl<T: Ord> ~[T] : Ord { impl<T: Ord> ~[T] : Ord {
#[inline(always)] #[inline(always)]
pure fn lt(other: &~[T]) -> bool { lt(self, (*other)) } pure fn lt(other: &~[T]) -> bool { lt(self, (*other)) }
@ -1370,6 +1419,7 @@ impl<T: Ord> @[T] : Ord {
#[cfg(notest)] #[cfg(notest)]
pub mod traits { pub mod traits {
#[cfg(stage0)]
impl<T: Copy> ~[T] : Add<&[const T],~[T]> { impl<T: Copy> ~[T] : Add<&[const T],~[T]> {
#[inline(always)] #[inline(always)]
pure fn add(rhs: & &[const T]) -> ~[T] { pure fn add(rhs: & &[const T]) -> ~[T] {
@ -1377,12 +1427,31 @@ pub mod traits {
} }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl<T: Copy> ~[T] : Add<&[const T],~[T]> {
#[inline(always)]
pure fn add(rhs: & &self/[const T]) -> ~[T] {
append(copy self, (*rhs))
}
}
#[cfg(stage0)]
impl<T: Copy> ~[mut T] : Add<&[const T],~[mut T]> { impl<T: Copy> ~[mut T] : Add<&[const T],~[mut T]> {
#[inline(always)] #[inline(always)]
pure fn add(rhs: & &[const T]) -> ~[mut T] { pure fn add(rhs: & &[const T]) -> ~[mut T] {
append_mut(copy self, (*rhs)) append_mut(copy self, (*rhs))
} }
} }
#[cfg(stage1)]
#[cfg(stage2)]
impl<T: Copy> ~[mut T] : Add<&[const T],~[mut T]> {
#[inline(always)]
pure fn add(rhs: & &self/[const T]) -> ~[mut T] {
append_mut(copy self, (*rhs))
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -1590,6 +1659,7 @@ pub trait MutableVector<T> {
fn unshift(&mut self, x: T); fn unshift(&mut self, x: T);
fn swap_remove(&mut self, index: uint) -> T; fn swap_remove(&mut self, index: uint) -> T;
fn truncate(&mut self, newlen: uint); fn truncate(&mut self, newlen: uint);
fn retain(&mut self, f: pure fn(t: &T) -> bool);
} }
pub trait MutableCopyableVector<T: Copy> { pub trait MutableCopyableVector<T: Copy> {
@ -1631,6 +1701,10 @@ impl<T> ~[T]: MutableVector<T> {
fn truncate(&mut self, newlen: uint) { fn truncate(&mut self, newlen: uint) {
truncate(self, newlen); truncate(self, newlen);
} }
fn retain(&mut self, f: pure fn(t: &T) -> bool) {
retain(self, f);
}
} }
impl<T: Copy> ~[T]: MutableCopyableVector<T> { impl<T: Copy> ~[T]: MutableCopyableVector<T> {

View file

@ -147,7 +147,7 @@ fn enc_region(w: io::Writer, cx: @ctxt, r: ty::Region) {
ty::re_static => { ty::re_static => {
w.write_char('t'); w.write_char('t');
} }
ty::re_var(_) => { ty::re_infer(_) => {
// these should not crop up after typeck // these should not crop up after typeck
cx.diag.handler().bug(~"Cannot encode region variables"); cx.diag.handler().bug(~"Cannot encode region variables");
} }

View file

@ -387,7 +387,7 @@ impl ty::Region: tr {
ty::re_bound(br) => ty::re_bound(br.tr(xcx)), ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)), ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)),
ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)), ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
ty::re_static | ty::re_var(*) => self, ty::re_static | ty::re_infer(*) => self,
} }
} }
} }

View file

@ -580,7 +580,7 @@ fn check_cast_for_escaping_regions(
match target_substs.self_r { match target_substs.self_r {
Some(ty::re_scope(*)) => { return; /* case (1) */ } Some(ty::re_scope(*)) => { return; /* case (1) */ }
None | Some(ty::re_static) | Some(ty::re_free(*)) => {} None | Some(ty::re_static) | Some(ty::re_free(*)) => {}
Some(ty::re_bound(*)) | Some(ty::re_var(*)) => { Some(ty::re_bound(*)) | Some(ty::re_infer(*)) => {
cx.tcx.sess.span_bug( cx.tcx.sess.span_bug(
source.span, source.span,
fmt!("bad region found in kind: %?", target_substs.self_r)); fmt!("bad region found in kind: %?", target_substs.self_r));

View file

@ -112,18 +112,18 @@ fn is_subregion_of(region_map: region_map,
super_region: ty::Region) -> bool { super_region: ty::Region) -> bool {
sub_region == super_region || sub_region == super_region ||
match (sub_region, super_region) { match (sub_region, super_region) {
(_, ty::re_static) => { (_, ty::re_static) => {
true true
} }
(ty::re_scope(sub_scope), ty::re_scope(super_scope)) | (ty::re_scope(sub_scope), ty::re_scope(super_scope)) |
(ty::re_scope(sub_scope), ty::re_free(super_scope, _)) => { (ty::re_scope(sub_scope), ty::re_free(super_scope, _)) => {
scope_contains(region_map, super_scope, sub_scope) scope_contains(region_map, super_scope, sub_scope)
} }
_ => { _ => {
false false
} }
} }
} }

View file

@ -12,7 +12,7 @@ use syntax::ast_util::{is_local, local_def};
use syntax::codemap::span; use syntax::codemap::span;
use metadata::csearch; use metadata::csearch;
use util::ppaux::{region_to_str, explain_region, vstore_to_str, use util::ppaux::{region_to_str, explain_region, vstore_to_str,
note_and_explain_region}; note_and_explain_region, bound_region_to_str};
use middle::lint; use middle::lint;
use middle::lint::{get_lint_level, allow}; use middle::lint::{get_lint_level, allow};
use syntax::ast::*; use syntax::ast::*;
@ -107,7 +107,8 @@ export InferTy, TyVar, IntVar;
export ty_self, mk_self, type_has_self; export ty_self, mk_self, type_has_self;
export ty_class; export ty_class;
export Region, bound_region, encl_region; export Region, bound_region, encl_region;
export re_bound, re_free, re_scope, re_static, re_var; export re_bound, re_free, re_scope, re_static, re_infer;
export ReVar, ReSkolemized;
export br_self, br_anon, br_named, br_cap_avoid; export br_self, br_anon, br_named, br_cap_avoid;
export get, type_has_params, type_needs_infer, type_has_regions; export get, type_has_params, type_needs_infer, type_has_regions;
export type_is_region_ptr; export type_is_region_ptr;
@ -179,6 +180,8 @@ export terr_in_field, terr_record_fields, terr_vstores_differ, terr_arg_count;
export terr_sorts, terr_vec, terr_str, terr_record_size, terr_tuple_size; export terr_sorts, terr_vec, terr_str, terr_record_size, terr_tuple_size;
export terr_regions_does_not_outlive, terr_mutability, terr_purity_mismatch; export terr_regions_does_not_outlive, terr_mutability, terr_purity_mismatch;
export terr_regions_not_same, terr_regions_no_overlap; export terr_regions_not_same, terr_regions_no_overlap;
export terr_regions_insufficiently_polymorphic;
export terr_regions_overly_polymorphic;
export terr_proto_mismatch; export terr_proto_mismatch;
export terr_ret_style_mismatch; export terr_ret_style_mismatch;
export terr_fn, terr_trait; export terr_fn, terr_trait;
@ -555,7 +558,7 @@ enum Region {
re_static, re_static,
/// A region variable. Should not exist after typeck. /// A region variable. Should not exist after typeck.
re_var(RegionVid) re_infer(InferRegion)
} }
#[auto_serialize] #[auto_serialize]
@ -671,6 +674,8 @@ enum type_err {
terr_regions_does_not_outlive(Region, Region), terr_regions_does_not_outlive(Region, Region),
terr_regions_not_same(Region, Region), terr_regions_not_same(Region, Region),
terr_regions_no_overlap(Region, Region), terr_regions_no_overlap(Region, Region),
terr_regions_insufficiently_polymorphic(bound_region, Region),
terr_regions_overly_polymorphic(bound_region, Region),
terr_vstores_differ(terr_vstore_kind, expected_found<vstore>), terr_vstores_differ(terr_vstore_kind, expected_found<vstore>),
terr_in_field(@type_err, ast::ident), terr_in_field(@type_err, ast::ident),
terr_sorts(expected_found<t>), terr_sorts(expected_found<t>),
@ -707,6 +712,39 @@ impl InferTy : to_bytes::IterBytes {
} }
} }
#[auto_serialize]
#[auto_deserialize]
enum InferRegion {
ReVar(RegionVid),
ReSkolemized(uint, bound_region)
}
impl InferRegion : to_bytes::IterBytes {
pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
match self {
ReVar(ref rv) => to_bytes::iter_bytes_2(&0u8, rv, lsb0, f),
ReSkolemized(ref v, _) => to_bytes::iter_bytes_2(&1u8, v, lsb0, f)
}
}
}
impl InferRegion : cmp::Eq {
pure fn eq(other: &InferRegion) -> bool {
match (self, *other) {
(ReVar(rva), ReVar(rvb)) => {
rva == rvb
}
(ReSkolemized(rva, _), ReSkolemized(rvb, _)) => {
rva == rvb
}
_ => false
}
}
pure fn ne(other: &InferRegion) -> bool {
!(self == (*other))
}
}
impl param_bound : to_bytes::IterBytes { impl param_bound : to_bytes::IterBytes {
pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) { pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
match self { match self {
@ -923,7 +961,7 @@ fn mk_t_with_id(cx: ctxt, +st: sty, o_def_id: Option<ast::def_id>) -> t {
fn rflags(r: Region) -> uint { fn rflags(r: Region) -> uint {
(has_regions as uint) | { (has_regions as uint) | {
match r { match r {
ty::re_var(_) => needs_infer as uint, ty::re_infer(_) => needs_infer as uint,
_ => 0u _ => 0u
} }
} }
@ -2591,7 +2629,7 @@ impl Region : to_bytes::IterBytes {
re_scope(ref id) => re_scope(ref id) =>
to_bytes::iter_bytes_2(&2u8, id, lsb0, f), to_bytes::iter_bytes_2(&2u8, id, lsb0, f),
re_var(ref id) => re_infer(ref id) =>
to_bytes::iter_bytes_2(&3u8, id, lsb0, f), to_bytes::iter_bytes_2(&3u8, id, lsb0, f),
re_static => 4u8.iter_bytes(lsb0, f) re_static => 4u8.iter_bytes(lsb0, f)
@ -3253,92 +3291,103 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
} }
match *err { match *err {
terr_mismatch => ~"types differ", terr_mismatch => ~"types differ",
terr_ret_style_mismatch(values) => { terr_ret_style_mismatch(values) => {
fn to_str(s: ast::ret_style) -> ~str { fn to_str(s: ast::ret_style) -> ~str {
match s { match s {
ast::noreturn => ~"non-returning", ast::noreturn => ~"non-returning",
ast::return_val => ~"return-by-value" ast::return_val => ~"return-by-value"
}
} }
fmt!("expected %s function, found %s function",
to_str(values.expected),
to_str(values.expected))
}
terr_purity_mismatch(values) => {
fmt!("expected %s fn but found %s fn",
purity_to_str(values.expected),
purity_to_str(values.found))
}
terr_proto_mismatch(values) => {
fmt!("expected %s closure, found %s closure",
proto_ty_to_str(cx, values.expected),
proto_ty_to_str(cx, values.found))
}
terr_mutability => ~"values differ in mutability",
terr_box_mutability => ~"boxed values differ in mutability",
terr_vec_mutability => ~"vectors differ in mutability",
terr_ptr_mutability => ~"pointers differ in mutability",
terr_ref_mutability => ~"references differ in mutability",
terr_ty_param_size(values) => {
fmt!("expected a type with %? type params \
but found one with %? type params",
values.expected, values.found)
}
terr_tuple_size(values) => {
fmt!("expected a tuple with %? elements \
but found one with %? elements",
values.expected, values.found)
}
terr_record_size(values) => {
fmt!("expected a record with %? fields \
but found one with %? fields",
values.expected, values.found)
}
terr_record_mutability => {
~"record elements differ in mutability"
}
terr_record_fields(values) => {
fmt!("expected a record with field `%s` but found one with field \
`%s`",
cx.sess.str_of(values.expected),
cx.sess.str_of(values.found))
}
terr_arg_count => ~"incorrect number of function parameters",
terr_mode_mismatch(values) => {
fmt!("expected argument mode %s, but found %s",
mode_to_str(values.expected), mode_to_str(values.found))
}
terr_regions_does_not_outlive(*) => {
fmt!("lifetime mismatch")
}
terr_regions_not_same(*) => {
fmt!("lifetimes are not the same")
}
terr_regions_no_overlap(*) => {
fmt!("lifetimes do not intersect")
}
terr_regions_insufficiently_polymorphic(br, _) => {
fmt!("expected bound lifetime parameter %s, \
but found concrete lifetime",
bound_region_to_str(cx, br))
}
terr_regions_overly_polymorphic(br, _) => {
fmt!("expected concrete lifetime, \
but found bound lifetime parameter %s",
bound_region_to_str(cx, br))
}
terr_vstores_differ(k, values) => {
fmt!("%s storage differs: expected %s but found %s",
terr_vstore_kind_to_str(k),
vstore_to_str(cx, values.expected),
vstore_to_str(cx, values.found))
}
terr_in_field(err, fname) => {
fmt!("in field `%s`, %s", cx.sess.str_of(fname),
type_err_to_str(cx, err))
}
terr_sorts(values) => {
fmt!("expected %s but found %s",
ty_sort_str(cx, values.expected),
ty_sort_str(cx, values.found))
}
terr_self_substs => {
~"inconsistent self substitution" // XXX this is more of a bug
}
terr_no_integral_type => {
~"couldn't determine an appropriate integral type for integer \
literal"
} }
fmt!("expected %s function, found %s function",
to_str(values.expected),
to_str(values.expected))
}
terr_purity_mismatch(values) => {
fmt!("expected %s fn but found %s fn",
purity_to_str(values.expected),
purity_to_str(values.found))
}
terr_proto_mismatch(values) => {
fmt!("expected %s closure, found %s closure",
proto_ty_to_str(cx, values.expected),
proto_ty_to_str(cx, values.found))
}
terr_mutability => ~"values differ in mutability",
terr_box_mutability => ~"boxed values differ in mutability",
terr_vec_mutability => ~"vectors differ in mutability",
terr_ptr_mutability => ~"pointers differ in mutability",
terr_ref_mutability => ~"references differ in mutability",
terr_ty_param_size(values) => {
fmt!("expected a type with %? type params \
but found one with %? type params",
values.expected, values.found)
}
terr_tuple_size(values) => {
fmt!("expected a tuple with %? elements \
but found one with %? elements",
values.expected, values.found)
}
terr_record_size(values) => {
fmt!("expected a record with %? fields \
but found one with %? fields",
values.expected, values.found)
}
terr_record_mutability => {
~"record elements differ in mutability"
}
terr_record_fields(values) => {
fmt!("expected a record with field `%s` but found one with field \
`%s`",
cx.sess.str_of(values.expected), cx.sess.str_of(values.found))
}
terr_arg_count => ~"incorrect number of function parameters",
terr_mode_mismatch(values) => {
fmt!("expected argument mode %s, but found %s",
mode_to_str(values.expected), mode_to_str(values.found))
}
terr_regions_does_not_outlive(*) => {
fmt!("lifetime mismatch")
}
terr_regions_not_same(*) => {
fmt!("lifetimes are not the same")
}
terr_regions_no_overlap(*) => {
fmt!("lifetimes do not intersect")
}
terr_vstores_differ(k, values) => {
fmt!("%s storage differs: expected %s but found %s",
terr_vstore_kind_to_str(k),
vstore_to_str(cx, values.expected),
vstore_to_str(cx, values.found))
}
terr_in_field(err, fname) => {
fmt!("in field `%s`, %s", cx.sess.str_of(fname),
type_err_to_str(cx, err))
}
terr_sorts(values) => {
fmt!("expected %s but found %s",
ty_sort_str(cx, values.expected),
ty_sort_str(cx, values.found))
}
terr_self_substs => {
~"inconsistent self substitution" // XXX this is more of a bug
}
terr_no_integral_type => {
~"couldn't determine an appropriate integral type for integer \
literal"
}
} }
} }
@ -3359,6 +3408,16 @@ fn note_and_explain_type_err(cx: ctxt, err: &type_err) {
note_and_explain_region(cx, ~"...does not overlap ", note_and_explain_region(cx, ~"...does not overlap ",
region2, ~""); region2, ~"");
} }
terr_regions_insufficiently_polymorphic(_, conc_region) => {
note_and_explain_region(cx,
~"concrete lifetime that was found is ",
conc_region, ~"");
}
terr_regions_overly_polymorphic(_, conc_region) => {
note_and_explain_region(cx,
~"expected concrete lifetime is ",
conc_region, ~"");
}
_ => {} _ => {}
} }
} }
@ -4182,9 +4241,9 @@ impl Region : cmp::Eq {
_ => false _ => false
} }
} }
re_var(e0a) => { re_infer(e0a) => {
match (*other) { match (*other) {
re_var(e0b) => e0a == e0b, re_infer(e0b) => e0a == e0b,
_ => false _ => false
} }
} }

View file

@ -97,7 +97,7 @@ fn replace_bound_regions_in_fn_ty(
r: ty::Region) -> isr_alist { r: ty::Region) -> isr_alist {
match r { match r {
ty::re_free(_, _) | ty::re_static | ty::re_scope(_) | ty::re_free(_, _) | ty::re_static | ty::re_scope(_) |
ty::re_var(_) => { ty::re_infer(_) => {
isr isr
} }
ty::re_bound(br) => { ty::re_bound(br) => {
@ -165,7 +165,7 @@ fn replace_bound_regions_in_fn_ty(
ty::re_static | ty::re_static |
ty::re_scope(_) | ty::re_scope(_) |
ty::re_free(_, _) | ty::re_free(_, _) |
ty::re_var(_) => r ty::re_infer(_) => r
} }
} }
} }

View file

@ -272,46 +272,60 @@ fn ensure_supertraits(ccx: @crate_ctxt,
* *
* - impl_m: the method in the impl * - impl_m: the method in the impl
* - impl_tps: the type params declared on the impl itself (not the method!) * - impl_tps: the type params declared on the impl itself (not the method!)
* - impl_body_id: the id of the method body from the impl
* - trait_m: the method in the trait * - trait_m: the method in the trait
* - trait_substs: the substitutions used on the type of the trait * - trait_substs: the substitutions used on the type of the trait
* - self_ty: the self type of the impl * - self_ty: the self type of the impl
*/ */
fn compare_impl_method(tcx: ty::ctxt, sp: span, fn compare_impl_method(tcx: ty::ctxt,
impl_m: ty::method, impl_tps: uint, impl_tps: uint,
trait_m: ty::method, trait_substs: ty::substs, cm: &ConvertedMethod,
self_ty: ty::t) { trait_m: &ty::method,
trait_substs: &ty::substs,
self_ty: ty::t)
{
debug!("compare_impl_method()");
let _indenter = indenter();
let impl_m = &cm.mty;
if impl_m.fty.meta.purity != trait_m.fty.meta.purity { if impl_m.fty.meta.purity != trait_m.fty.meta.purity {
tcx.sess.span_err( tcx.sess.span_err(
sp, fmt!("method `%s`'s purity does \ cm.span,
not match the trait method's \ fmt!("method `%s`'s purity does \
purity", tcx.sess.str_of(impl_m.ident))); not match the trait method's \
purity", tcx.sess.str_of(impl_m.ident)));
} }
// is this check right? // is this check right?
if impl_m.self_ty != trait_m.self_ty { if impl_m.self_ty != trait_m.self_ty {
tcx.sess.span_err( tcx.sess.span_err(
sp, fmt!("method `%s`'s self type does \ cm.span,
not match the trait method's \ fmt!("method `%s`'s self type does \
self type", tcx.sess.str_of(impl_m.ident))); not match the trait method's \
self type", tcx.sess.str_of(impl_m.ident)));
} }
if impl_m.tps.len() != trait_m.tps.len() { if impl_m.tps.len() != trait_m.tps.len() {
tcx.sess.span_err(sp, fmt!("method `%s` \ tcx.sess.span_err(
has %u type %s, but its trait declaration has %u type %s", cm.span,
tcx.sess.str_of(trait_m.ident), impl_m.tps.len(), fmt!("method `%s` has %u type %s, but its trait \
pluralize(impl_m.tps.len(), ~"parameter"), declaration has %u type %s",
trait_m.tps.len(), tcx.sess.str_of(trait_m.ident), impl_m.tps.len(),
pluralize(trait_m.tps.len(), ~"parameter"))); pluralize(impl_m.tps.len(), ~"parameter"),
trait_m.tps.len(),
pluralize(trait_m.tps.len(), ~"parameter")));
return; return;
} }
if vec::len(impl_m.fty.sig.inputs) != vec::len(trait_m.fty.sig.inputs) { if vec::len(impl_m.fty.sig.inputs) != vec::len(trait_m.fty.sig.inputs) {
tcx.sess.span_err(sp,fmt!("method `%s` has %u parameters \ tcx.sess.span_err(
but the trait has %u", cm.span,
tcx.sess.str_of(trait_m.ident), fmt!("method `%s` has %u parameters \
vec::len(impl_m.fty.sig.inputs), but the trait has %u",
vec::len(trait_m.fty.sig.inputs))); tcx.sess.str_of(trait_m.ident),
vec::len(impl_m.fty.sig.inputs),
vec::len(trait_m.fty.sig.inputs)));
return; return;
} }
@ -322,14 +336,16 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
// Would be nice to use the ty param names in the error message, // Would be nice to use the ty param names in the error message,
// but we don't have easy access to them here // but we don't have easy access to them here
if impl_param_bounds.len() != trait_param_bounds.len() { if impl_param_bounds.len() != trait_param_bounds.len() {
tcx.sess.span_err(sp, fmt!("in method `%s`, \ tcx.sess.span_err(
type parameter %u has %u %s, but the same type \ cm.span,
parameter in its trait declaration has %u %s", fmt!("in method `%s`, \
tcx.sess.str_of(trait_m.ident), type parameter %u has %u %s, but the same type \
i, impl_param_bounds.len(), parameter in its trait declaration has %u %s",
pluralize(impl_param_bounds.len(), ~"bound"), tcx.sess.str_of(trait_m.ident),
trait_param_bounds.len(), i, impl_param_bounds.len(),
pluralize(trait_param_bounds.len(), ~"bound"))); pluralize(impl_param_bounds.len(), ~"bound"),
trait_param_bounds.len(),
pluralize(trait_param_bounds.len(), ~"bound")));
return; return;
} }
// tjc: I'm mildly worried that there's something I'm // tjc: I'm mildly worried that there's something I'm
@ -337,35 +353,51 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
// but I can't figure out what. // but I can't figure out what.
} }
// Replace any references to the self region in the self type with
// a free region. So, for example, if the impl type is
// "&self/str", then this would replace the self type with a free
// region `self`.
//
// Note: Ideal would be to use the node-id of the method body here,
// not the node id of the method itself.
let dummy_self_r = ty::re_free(cm.body_id, ty::br_self);
let self_ty = replace_bound_self(tcx, self_ty, dummy_self_r);
// Perform substitutions so that the trait/impl methods are expressed // Perform substitutions so that the trait/impl methods are expressed
// in terms of the same set of type/region parameters: // in terms of the same set of type/region parameters:
// - replace trait type parameters with those from `trait_substs` // - replace trait type parameters with those from `trait_substs`,
// except with any reference to bound self replaced with `dummy_self_r`
// - replace method parameters on the trait with fresh, dummy parameters // - replace method parameters on the trait with fresh, dummy parameters
// that correspond to the parameters we will find on the impl // that correspond to the parameters we will find on the impl
// - replace self region with a fresh, dummy region // - replace self region with a fresh, dummy region
let dummy_self_r = ty::re_free(0, ty::br_self);
let impl_fty = { let impl_fty = {
let impl_fty = ty::mk_fn(tcx, impl_m.fty); let impl_fty = ty::mk_fn(tcx, impl_m.fty);
debug!("impl_fty (pre-subst): %s", ty_to_str(tcx, impl_fty));
replace_bound_self(tcx, impl_fty, dummy_self_r) replace_bound_self(tcx, impl_fty, dummy_self_r)
}; };
debug!("impl_fty: %s", ty_to_str(tcx, impl_fty));
let trait_fty = { let trait_fty = {
let dummy_tps = do vec::from_fn((*trait_m.tps).len()) |i| { let dummy_tps = do vec::from_fn((*trait_m.tps).len()) |i| {
// hack: we don't know the def id of the impl tp, but it // hack: we don't know the def id of the impl tp, but it
// is not important for unification // is not important for unification
ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0}) ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0})
}; };
let trait_tps = trait_substs.tps.map(
|t| replace_bound_self(tcx, *t, dummy_self_r));
let substs = { let substs = {
self_r: Some(dummy_self_r), self_r: Some(dummy_self_r),
self_ty: Some(self_ty), self_ty: Some(self_ty),
tps: vec::append(trait_substs.tps, dummy_tps) tps: vec::append(trait_tps, dummy_tps)
}; };
let trait_fty = ty::mk_fn(tcx, trait_m.fty); let trait_fty = ty::mk_fn(tcx, trait_m.fty);
debug!("trait_fty (pre-subst): %s", ty_to_str(tcx, trait_fty));
ty::subst(tcx, &substs, trait_fty) ty::subst(tcx, &substs, trait_fty)
}; };
debug!("trait_fty: %s", ty_to_str(tcx, trait_fty));
require_same_types( require_same_types(
tcx, None, false, sp, impl_fty, trait_fty, tcx, None, false, cm.span, impl_fty, trait_fty,
|| ~"method `" + tcx.sess.str_of(trait_m.ident) || fmt!("method `%s` has an incompatible type",
+ ~"` has an incompatible type"); tcx.sess.str_of(trait_m.ident)));
return; return;
// Replaces bound references to the self region with `with_r`. // Replaces bound references to the self region with `with_r`.
@ -382,7 +414,7 @@ fn check_methods_against_trait(ccx: @crate_ctxt,
rp: Option<ty::region_variance>, rp: Option<ty::region_variance>,
selfty: ty::t, selfty: ty::t,
a_trait_ty: @ast::trait_ref, a_trait_ty: @ast::trait_ref,
impl_ms: ~[converted_method]) { impl_ms: ~[ConvertedMethod]) {
let tcx = ccx.tcx; let tcx = ccx.tcx;
let (did, tpt) = instantiate_trait_ref(ccx, a_trait_ty, rp); let (did, tpt) = instantiate_trait_ref(ccx, a_trait_ty, rp);
@ -391,32 +423,32 @@ fn check_methods_against_trait(ccx: @crate_ctxt,
} }
for vec::each(*ty::trait_methods(tcx, did)) |trait_m| { for vec::each(*ty::trait_methods(tcx, did)) |trait_m| {
match vec::find(impl_ms, |impl_m| trait_m.ident == impl_m.mty.ident) { match vec::find(impl_ms, |impl_m| trait_m.ident == impl_m.mty.ident) {
Some({mty: impl_m, span, _}) => { Some(ref cm) => {
compare_impl_method( compare_impl_method(
ccx.tcx, span, impl_m, vec::len(tps), ccx.tcx, vec::len(tps), cm, trait_m,
*trait_m, tpt.substs, selfty); &tpt.substs, selfty);
} }
None => { None => {
// If we couldn't find an implementation for trait_m in // If we couldn't find an implementation for trait_m in
// the impl, then see if there was a default // the impl, then see if there was a default
// implementation in the trait itself. If not, raise a // implementation in the trait itself. If not, raise a
// "missing method" error. // "missing method" error.
let provided_methods = ty::provided_trait_methods(tcx, did); let provided_methods = ty::provided_trait_methods(tcx, did);
match vec::find(provided_methods, |provided_method| match vec::find(provided_methods, |provided_method|
*provided_method == trait_m.ident) { *provided_method == trait_m.ident) {
Some(_) => { Some(_) => {
// If there's a provided method with the name we // If there's a provided method with the name we
// want, then we're fine; nothing else to do. // want, then we're fine; nothing else to do.
}
None => {
tcx.sess.span_err(
a_trait_ty.path.span,
fmt!("missing method `%s`",
tcx.sess.str_of(trait_m.ident)));
}
} }
None => { }
tcx.sess.span_err(
a_trait_ty.path.span,
fmt!("missing method `%s`",
tcx.sess.str_of(trait_m.ident)));
}
}
}
} // match } // match
} // |trait_m| } // |trait_m|
} // fn } // fn
@ -434,12 +466,17 @@ fn convert_field(ccx: @crate_ctxt,
ty: tt}); ty: tt});
} }
type converted_method = {mty: ty::method, id: ast::node_id, span: span}; struct ConvertedMethod {
mty: ty::method,
id: ast::node_id,
span: span,
body_id: ast::node_id
}
fn convert_methods(ccx: @crate_ctxt, fn convert_methods(ccx: @crate_ctxt,
ms: ~[@ast::method], ms: ~[@ast::method],
rp: Option<ty::region_variance>, rp: Option<ty::region_variance>,
rcvr_bounds: @~[ty::param_bounds]) -> ~[converted_method] { rcvr_bounds: @~[ty::param_bounds]) -> ~[ConvertedMethod] {
let tcx = ccx.tcx; let tcx = ccx.tcx;
do vec::map(ms) |m| { do vec::map(ms) |m| {
@ -455,7 +492,8 @@ fn convert_methods(ccx: @crate_ctxt,
region_param: rp, region_param: rp,
ty: fty}); ty: fty});
write_ty_to_tcx(tcx, m.id, fty); write_ty_to_tcx(tcx, m.id, fty);
{mty: mty, id: m.id, span: m.span} ConvertedMethod {mty: mty, id: m.id,
span: m.span, body_id: m.body.node.id}
} }
} }

View file

@ -262,7 +262,7 @@ use util::common::{indent, indenter};
use ast::{unsafe_fn, impure_fn, pure_fn, extern_fn}; use ast::{unsafe_fn, impure_fn, pure_fn, extern_fn};
use ast::{m_const, m_imm, m_mutbl}; use ast::{m_const, m_imm, m_mutbl};
use dvec::DVec; use dvec::DVec;
use region_var_bindings::{RegionVarBindings}; use region_inference::{RegionVarBindings};
use ast_util::dummy_sp; use ast_util::dummy_sp;
use cmp::Eq; use cmp::Eq;
@ -628,7 +628,7 @@ impl infer_ctxt {
} }
fn next_region_var_nb(span: span) -> ty::Region { fn next_region_var_nb(span: span) -> ty::Region {
ty::re_var(self.region_vars.new_region_var(span)) ty::re_infer(ty::ReVar(self.region_vars.new_region_var(span)))
} }
fn next_region_var_with_lb(span: span, fn next_region_var_with_lb(span: span,

View file

@ -0,0 +1,12 @@
{
macro_rules! if_ok(
($inp: expr) => (
match $inp {
Ok(move v) => { move v }
Err(move e) => { return Err(e); }
}
)
);
}

View file

@ -134,45 +134,66 @@ to read the whole thing):
http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/ http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
NOTE--for the most part, we do not yet handle these cases correctly! Although my explanation will never compete with SPJ's (for one thing,
his is approximately 100 pages), I will attempt to explain the basic
problem and also how we solve it. Note that the paper only discusses
subtyping, not the computation of LUB/GLB.
## Subtyping and bound regions The problem we are addressing is that there is a kind of subtyping
between functions with bound region parameters. Consider, for
example, whether the following relation holds:
### Simple examples fn(&a/int) <: fn(&b/int)? (Yes, a => b)
The situation is well-summarized by these examples (here I am omitting The answer is that of course it does. These two types are basically
the types as they are not interesting, and I am writing binding the same, except that in one we used the name `a` and one we used
explicitly): the name `b`.
1. fn<a>(&a/T) <: fn<b>(&b/T)? Yes: a -> b In the examples that follow, it becomes very important to know whether
2. fn<a>(&a/T) <: fn(&b/T)? Yes: a -> b a lifetime is bound in a function type (that is, is a lifetime
3. fn(&a/T) <: fn<b>(&b/T)? No! parameter) or appears free (is defined in some outer scope).
4. fn(&a/T) <: fn(&b/T)? No! Therefore, from now on I will write the bindings explicitly, using a
5. fn(&a/T) <: fn(&a)? Yes! notation like `fn<a>(&a/int)` to indicate that `a` is a lifetime
parameter.
In case one, the two function types are equivalent because both Now let's consider two more function types. Here, we assume that the
reference a bound region, just with different names. `self` lifetime is defined somewhere outside and hence is not a
lifetime parameter bound by the function type (it "appears free"):
In case two, the subtyping relationship is valid because the subtyping fn<a>(&a/int) <: fn(&self/int)? (Yes, a => self)
function accepts a pointer in *any* region, whereas the supertype
function accepts a pointer *only in the region `b`*. Therefore, it is
safe to use the subtype wherever the supertype is expected, as the
supertype can only be passed pointers in region `b`, and the subtype
can handle `b` (but also others).
Case three is the opposite: here the subtype requires the region `a`, This subtyping relation does in fact hold. To see why, you have to
but the supertype must accept pointers in any region. That means that consider what subtyping means. One way to look at `T1 <: T2` is to
it is not safe to use the subtype where the supertype is expected: the say that it means that it is always ok to treat an instance of `T1` as
supertype can be passed pointers in any region, but the subtype can if it had the type `T2`. So, with our functions, it is always ok to
only handle pointers in the region `a`. treat a function that can take pointers with any lifetime as if it
were a function that can only take a pointer with the specific
lifetime `&self`. After all, `&self` is a lifetime, after all, and
the function can take values of any lifetime.
Case four is fairly simple. The subtype expects region `a` but the supertype You can also look at subtyping as the *is a* relationship. This amounts
expects region `b`. These two regions are not the same. Therefore, not to the same thing: a function that accepts pointers with any lifetime
a subtype. *is a* function that accepts pointers with some specific lifetime.
Case five is similar to four, except that the subtype and supertype So, what if we reverse the order of the two function types, like this:
expect the same region, so in fact they are the same type. That's
fine. fn(&self/int) <: fn<a>(&a/int)? (No)
Does the subtyping relationship still hold? The answer of course is
no. In this case, the function accepts *only the lifetime `&self`*,
so it is not reasonable to treat it as if it were a function that
accepted any lifetime.
What about these two examples:
fn<a,b>(&a/int, &b/int) <: fn<a>(&a/int, &a/int)? (Yes)
fn<a>(&a/int, &a/int) <: fn<a,b>(&a/int, &b/int)? (No)
Here, it is true that functions which take two pointers with any two
lifetimes can be treated as if they only accepted two pointers with
the same lifetime, but not the reverse.
## The algorithm
Here is the algorithm we use to perform the subtyping check: Here is the algorithm we use to perform the subtyping check:
@ -184,15 +205,13 @@ Here is the algorithm we use to perform the subtyping check:
4. Ensure that no skolemized regions 'leak' into region variables 4. Ensure that no skolemized regions 'leak' into region variables
visible from "the outside" visible from "the outside"
I'll walk briefly through how this works with the examples above. Let's walk through some examples and see how this algorithm plays out.
I'll ignore the last step for now, it'll come up in the complex
examples below.
#### First example #### First example
Let's look first at the first example, which was: We'll start with the first example, which was:
1. fn<a>(&a/T) <: fn<b>(&b/T/T)? Yes: a -> x 1. fn<a>(&a/T) <: fn<b>(&b/T)? Yes: a -> b
After steps 1 and 2 of the algorithm we will have replaced the types After steps 1 and 2 of the algorithm we will have replaced the types
like so: like so:
@ -204,7 +223,7 @@ region whose value is being inferred by the system. I also replaced
`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`) `&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
to indicate skolemized region names. We can assume they don't appear to indicate skolemized region names. We can assume they don't appear
elsewhere. Note that neither the sub- nor the supertype bind any elsewhere. Note that neither the sub- nor the supertype bind any
region names anymore (that is, the `<a>` and `<b>` have been removed). region names anymore (as indicated by the absence of `<` and `>`).
The next step is to check that the parameter types match. Because The next step is to check that the parameter types match. Because
parameters are contravariant, this means that we check whether: parameters are contravariant, this means that we check whether:
@ -226,25 +245,25 @@ So far we have encountered no error, so the subtype check succeeds.
Now let's look first at the third example, which was: Now let's look first at the third example, which was:
3. fn(&a/T) <: fn<b>(&b/T)? No! 3. fn(&self/T) <: fn<b>(&b/T)? No!
After steps 1 and 2 of the algorithm we will have replaced the types After steps 1 and 2 of the algorithm we will have replaced the types
like so: like so:
3. fn(&a/T) <: fn(&x/T)? 3. fn(&self/T) <: fn(&x/T)?
This looks pretty much the same as before, except that on the LHS `&a` This looks pretty much the same as before, except that on the LHS
was not bound, and hence was left as-is and not replaced with a `&self` was not bound, and hence was left as-is and not replaced with
variable. The next step is again to check that the parameter types a variable. The next step is again to check that the parameter types
match. This will ultimately require (as before) that `&a` <= `&x` match. This will ultimately require (as before) that `&self` <= `&x`
must hold: but this does not hold. `a` and `x` are both distinct free must hold: but this does not hold. `self` and `x` are both distinct
regions. So the subtype check fails. free regions. So the subtype check fails.
#### Checking for skolemization leaks #### Checking for skolemization leaks
You may be wondering about that mysterious last step. So far it has not You may be wondering about that mysterious last step in the algorithm.
been relevant. The purpose of that last step is to catch something like So far it has not been relevant. The purpose of that last step is to
*this*: catch something like *this*:
fn<a>() -> fn(&a/T) <: fn() -> fn<b>(&b/T)? No. fn<a>() -> fn(&a/T) <: fn() -> fn<b>(&b/T)? No.
@ -295,10 +314,11 @@ rule.
So the way we solve this is to add a fourth step that examines the So the way we solve this is to add a fourth step that examines the
constraints that refer to skolemized names. Basically, consider a constraints that refer to skolemized names. Basically, consider a
non-directed verison of the constraint graph. The only things non-directed verison of the constraint graph. Let `Tainted(x)` be the
reachable from a skolemized region ought to be the region variables set of all things reachable from a skolemized variable `x`.
that were created at the same time. So this case here would fail `Tainted(x)` should not contain any regions that existed before the
because `&x` was created alone, but is relatable to `&A`. step at which the skolemization was performed. So this case here
would fail because `&x` was created alone, but is relatable to `&A`.
*/ */
@ -313,7 +333,8 @@ use std::cell::{Cell, empty_cell};
use std::list::{List, Nil, Cons}; use std::list::{List, Nil, Cons};
use region::is_subregion_of; use region::is_subregion_of;
use ty::{Region, RegionVid}; use ty::{Region, RegionVid, re_static, re_infer, re_free, re_bound,
re_scope, ReVar, ReSkolemized};
use syntax::codemap; use syntax::codemap;
use to_str::ToStr; use to_str::ToStr;
use util::ppaux::note_and_explain_region; use util::ppaux::note_and_explain_region;
@ -350,7 +371,7 @@ impl Constraint : cmp::Eq {
} }
impl Constraint : to_bytes::IterBytes { impl Constraint : to_bytes::IterBytes {
pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) { pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
match self { match self {
ConstrainVarSubVar(ref v0, ref v1) => ConstrainVarSubVar(ref v0, ref v1) =>
to_bytes::iter_bytes_3(&0u8, v0, v1, lsb0, f), to_bytes::iter_bytes_3(&0u8, v0, v1, lsb0, f),
@ -394,10 +415,11 @@ type CombineMap = HashMap<TwoRegions, RegionVid>;
struct RegionVarBindings { struct RegionVarBindings {
tcx: ty::ctxt, tcx: ty::ctxt,
var_spans: DVec<span>, var_spans: DVec<span>,
values: Cell<~[ty::Region]>, values: Cell<~[Region]>,
constraints: HashMap<Constraint, span>, constraints: HashMap<Constraint, span>,
lubs: CombineMap, lubs: CombineMap,
glbs: CombineMap, glbs: CombineMap,
mut skolemization_count: uint,
// The undo log records actions that might later be undone. // The undo log records actions that might later be undone.
// //
@ -407,7 +429,7 @@ struct RegionVarBindings {
// actively snapshotting. The reason for this is that otherwise // actively snapshotting. The reason for this is that otherwise
// we end up adding entries for things like the lower bound on // we end up adding entries for things like the lower bound on
// a variable and so forth, which can never be rolled back. // a variable and so forth, which can never be rolled back.
undo_log: DVec<UndoLogEntry> mut undo_log: ~[UndoLogEntry]
} }
fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings { fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
@ -418,7 +440,8 @@ fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
constraints: HashMap(), constraints: HashMap(),
lubs: CombineMap(), lubs: CombineMap(),
glbs: CombineMap(), glbs: CombineMap(),
undo_log: DVec() skolemization_count: 0,
undo_log: ~[]
} }
} }
@ -430,11 +453,11 @@ fn CombineMap() -> CombineMap {
} }
impl RegionVarBindings { impl RegionVarBindings {
fn in_snapshot() -> bool { fn in_snapshot(&self) -> bool {
self.undo_log.len() > 0 self.undo_log.len() > 0
} }
fn start_snapshot() -> uint { fn start_snapshot(&self) -> uint {
debug!("RegionVarBindings: snapshot()=%u", self.undo_log.len()); debug!("RegionVarBindings: snapshot()=%u", self.undo_log.len());
if self.in_snapshot() { if self.in_snapshot() {
self.undo_log.len() self.undo_log.len()
@ -444,14 +467,14 @@ impl RegionVarBindings {
} }
} }
fn commit() { fn commit(&self) {
debug!("RegionVarBindings: commit()"); debug!("RegionVarBindings: commit()");
while self.undo_log.len() > 0 { while self.undo_log.len() > 0 {
self.undo_log.pop(); self.undo_log.pop();
} }
} }
fn rollback_to(snapshot: uint) { fn rollback_to(&self, snapshot: uint) {
debug!("RegionVarBindings: rollback_to(%u)", snapshot); debug!("RegionVarBindings: rollback_to(%u)", snapshot);
while self.undo_log.len() > snapshot { while self.undo_log.len() > snapshot {
let undo_item = self.undo_log.pop(); let undo_item = self.undo_log.pop();
@ -472,11 +495,11 @@ impl RegionVarBindings {
} }
} }
fn num_vars() -> uint { fn num_vars(&self) -> uint {
self.var_spans.len() self.var_spans.len()
} }
fn new_region_var(span: span) -> RegionVid { fn new_region_var(&self, span: span) -> RegionVid {
let id = self.num_vars(); let id = self.num_vars();
self.var_spans.push(span); self.var_spans.push(span);
let vid = RegionVid(id); let vid = RegionVid(id);
@ -488,7 +511,13 @@ impl RegionVarBindings {
return vid; return vid;
} }
fn add_constraint(+constraint: Constraint, span: span) { fn new_skolemized(&self, br: ty::bound_region) -> Region {
let sc = self.skolemization_count;
self.skolemization_count += 1;
re_infer(ReSkolemized(sc, br))
}
fn add_constraint(&self, +constraint: Constraint, span: span) {
// cannot add constraints once regions are resolved // cannot add constraints once regions are resolved
assert self.values.is_empty(); assert self.values.is_empty();
@ -501,21 +530,22 @@ impl RegionVarBindings {
} }
} }
fn make_subregion(span: span, sub: Region, sup: Region) -> cres<()> { fn make_subregion(&self, span: span,
sub: Region, sup: Region) -> cres<()> {
// cannot add constraints once regions are resolved // cannot add constraints once regions are resolved
assert self.values.is_empty(); assert self.values.is_empty();
debug!("RegionVarBindings: make_subregion(%?, %?)", sub, sup); debug!("RegionVarBindings: make_subregion(%?, %?)", sub, sup);
match (sub, sup) { match (sub, sup) {
(ty::re_var (sub_id), ty::re_var(sup_id)) => { (re_infer(ReVar(sub_id)), re_infer(ReVar(sup_id))) => {
self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), span); self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), span);
Ok(()) Ok(())
} }
(r, ty::re_var(sup_id)) => { (r, re_infer(ReVar(sup_id))) => {
self.add_constraint(ConstrainRegSubVar(r, sup_id), span); self.add_constraint(ConstrainRegSubVar(r, sup_id), span);
Ok(()) Ok(())
} }
(ty::re_var(sub_id), r) => { (re_infer(ReVar(sub_id)), r) => {
self.add_constraint(ConstrainVarSubReg(sub_id, r), span); self.add_constraint(ConstrainVarSubReg(sub_id, r), span);
Ok(()) Ok(())
} }
@ -529,17 +559,17 @@ impl RegionVarBindings {
} }
} }
fn lub_regions(span: span, a: Region, b: Region) -> cres<Region> { fn lub_regions(&self, span: span, a: Region, b: Region) -> cres<Region> {
// cannot add constraints once regions are resolved // cannot add constraints once regions are resolved
assert self.values.is_empty(); assert self.values.is_empty();
debug!("RegionVarBindings: lub_regions(%?, %?)", a, b); debug!("RegionVarBindings: lub_regions(%?, %?)", a, b);
match (a, b) { match (a, b) {
(ty::re_static, _) | (_, ty::re_static) => { (re_static, _) | (_, re_static) => {
Ok(ty::re_static) // nothing lives longer than static Ok(re_static) // nothing lives longer than static
} }
(ty::re_var(*), _) | (_, ty::re_var(*)) => { (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => {
self.combine_vars( self.combine_vars(
self.lubs, a, b, span, self.lubs, a, b, span,
|old_r, new_r| self.make_subregion(span, old_r, new_r)) |old_r, new_r| self.make_subregion(span, old_r, new_r))
@ -551,18 +581,18 @@ impl RegionVarBindings {
} }
} }
fn glb_regions(span: span, a: Region, b: Region) -> cres<Region> { fn glb_regions(&self, span: span, a: Region, b: Region) -> cres<Region> {
// cannot add constraints once regions are resolved // cannot add constraints once regions are resolved
assert self.values.is_empty(); assert self.values.is_empty();
debug!("RegionVarBindings: glb_regions(%?, %?)", a, b); debug!("RegionVarBindings: glb_regions(%?, %?)", a, b);
match (a, b) { match (a, b) {
(ty::re_static, r) | (r, ty::re_static) => { (re_static, r) | (r, re_static) => {
// static lives longer than everything else // static lives longer than everything else
Ok(r) Ok(r)
} }
(ty::re_var(*), _) | (_, ty::re_var(*)) => { (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => {
self.combine_vars( self.combine_vars(
self.glbs, a, b, span, self.glbs, a, b, span,
|old_r, new_r| self.make_subregion(span, new_r, old_r)) |old_r, new_r| self.make_subregion(span, new_r, old_r))
@ -574,7 +604,7 @@ impl RegionVarBindings {
} }
} }
fn resolve_var(rid: RegionVid) -> ty::Region { fn resolve_var(&self, rid: RegionVid) -> ty::Region {
debug!("RegionVarBindings: resolve_var(%?=%u)", rid, *rid); debug!("RegionVarBindings: resolve_var(%?=%u)", rid, *rid);
if self.values.is_empty() { if self.values.is_empty() {
self.tcx.sess.span_bug( self.tcx.sess.span_bug(
@ -586,29 +616,129 @@ impl RegionVarBindings {
self.values.with_ref(|values| values[*rid]) self.values.with_ref(|values| values[*rid])
} }
fn combine_vars(combines: CombineMap, a: Region, b: Region, span: span, fn combine_vars(&self,
combines: CombineMap,
a: Region,
b: Region,
span: span,
relate: fn(old_r: Region, new_r: Region) -> cres<()>) relate: fn(old_r: Region, new_r: Region) -> cres<()>)
-> cres<Region> { -> cres<Region> {
let vars = TwoRegions { a: a, b: b }; let vars = TwoRegions { a: a, b: b };
match combines.find(vars) { match combines.find(vars) {
Some(c) => Ok(ty::re_var(c)), Some(c) => Ok(re_infer(ReVar(c))),
None => { None => {
let c = self.new_region_var(span); let c = self.new_region_var(span);
combines.insert(vars, c); combines.insert(vars, c);
if self.in_snapshot() { if self.in_snapshot() {
self.undo_log.push(AddCombination(combines, vars)); self.undo_log.push(AddCombination(combines, vars));
} }
do relate(a, ty::re_var(c)).then { do relate(a, re_infer(ReVar(c))).then {
do relate(b, ty::re_var(c)).then { do relate(b, re_infer(ReVar(c))).then {
debug!("combine_vars() c=%?", ty::re_var(c)); debug!("combine_vars() c=%?", c);
Ok(ty::re_var(c)) Ok(re_infer(ReVar(c)))
} }
} }
} }
} }
} }
fn tainted(&self, snapshot: uint, r0: Region) -> ~[Region] {
/*!
*
* Computes all regions that have been related to `r0` in any
* way since the snapshot `snapshot` was taken---excluding
* `r0` itself and any region variables added as part of the
* snapshot. This is used when checking whether skolemized
* regions are being improperly related to other regions.
*/
debug!("tainted(snapshot=%u, r0=%?)", snapshot, r0);
let _indenter = indenter();
let undo_len = self.undo_log.len();
// collect variables added since the snapshot was taken
let new_vars = do vec::build |push| {
for uint::range(snapshot, undo_len) |i| {
match self.undo_log[i] {
AddVar(vid) => push(vid),
_ => ()
}
}
};
// `result_set` acts as a worklist: we explore all outgoing
// edges and add any new regions we find to result_set. This
// is not a terribly efficient implementation.
let mut result_set = ~[r0], result_index = 0;
while result_index < result_set.len() {
// nb: can't use uint::range() here because result_set grows
let r = result_set[result_index];
debug!("result_index=%u, r=%?", result_index, r);
let mut undo_index = snapshot;
while undo_index < undo_len {
// nb: can't use uint::range() here as we move result_set
let regs = match self.undo_log[undo_index] {
AddConstraint(ConstrainVarSubVar(ref a, ref b)) => {
Some((re_infer(ReVar(*a)),
re_infer(ReVar(*b))))
}
AddConstraint(ConstrainRegSubVar(ref a, ref b)) => {
Some((*a, re_infer(ReVar(*b))))
}
AddConstraint(ConstrainVarSubReg(ref a, ref b)) => {
Some((re_infer(ReVar(*a)), *b))
}
_ => {
None
}
};
match regs {
None => {}
Some((ref r1, ref r2)) => {
result_set =
consider_adding_edge(move result_set, &r, r1, r2);
result_set =
consider_adding_edge(move result_set, &r, r2, r1);
}
}
undo_index += 1;
}
result_index += 1;
}
// Drop `r0` itself and any region variables that were created
// since the snapshot.
result_set.retain(|r| {
match *r {
re_infer(ReVar(ref vid)) => !new_vars.contains(vid),
_ => *r != r0
}
});
return result_set;
fn consider_adding_edge(+result_set: ~[Region],
r: &Region,
r1: &Region,
r2: &Region) -> ~[Region]
{
let mut result_set = move result_set;
if *r == *r1 { // Clearly, this is potentially inefficient.
if !result_set.contains(r2) {
result_set.push(*r2);
}
}
return move result_set;
}
}
/** /**
This function performs the actual region resolution. It must be This function performs the actual region resolution. It must be
called after all constraints have been added. It performs a called after all constraints have been added. It performs a
@ -616,32 +746,32 @@ impl RegionVarBindings {
constraints, assuming such values can be found; if they cannot, constraints, assuming such values can be found; if they cannot,
errors are reported. errors are reported.
*/ */
fn resolve_regions() { fn resolve_regions(&self) {
debug!("RegionVarBindings: resolve_regions()"); debug!("RegionVarBindings: resolve_regions()");
self.values.put_back(self.infer_variable_values()); self.values.put_back(self.infer_variable_values());
} }
} }
priv impl RegionVarBindings { priv impl RegionVarBindings {
fn is_subregion_of(sub: Region, sup: Region) -> bool { fn is_subregion_of(&self, sub: Region, sup: Region) -> bool {
is_subregion_of(self.tcx.region_map, sub, sup) is_subregion_of(self.tcx.region_map, sub, sup)
} }
fn lub_concrete_regions(+a: Region, +b: Region) -> Region { fn lub_concrete_regions(&self, +a: Region, +b: Region) -> Region {
match (a, b) { match (a, b) {
(ty::re_static, _) | (_, ty::re_static) => { (re_static, _) | (_, re_static) => {
ty::re_static // nothing lives longer than static re_static // nothing lives longer than static
} }
(ty::re_var(v_id), _) | (_, ty::re_var(v_id)) => { (re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => {
self.tcx.sess.span_bug( self.tcx.sess.span_bug(
self.var_spans[*v_id], self.var_spans[*v_id],
fmt!("lub_concrete_regions invoked with \ fmt!("lub_concrete_regions invoked with \
non-concrete regions: %?, %?", a, b)); non-concrete regions: %?, %?", a, b));
} }
(f @ ty::re_free(f_id, _), ty::re_scope(s_id)) | (f @ re_free(f_id, _), re_scope(s_id)) |
(ty::re_scope(s_id), f @ ty::re_free(f_id, _)) => { (re_scope(s_id), f @ re_free(f_id, _)) => {
// A "free" region can be interpreted as "some region // A "free" region can be interpreted as "some region
// at least as big as the block f_id". So, we can // at least as big as the block f_id". So, we can
// reasonably compare free regions and scopes: // reasonably compare free regions and scopes:
@ -654,98 +784,103 @@ priv impl RegionVarBindings {
// otherwise, we don't know what the free region is, // otherwise, we don't know what the free region is,
// so we must conservatively say the LUB is static: // so we must conservatively say the LUB is static:
_ => ty::re_static _ => re_static
} }
} }
(ty::re_scope(a_id), ty::re_scope(b_id)) => { (re_scope(a_id), re_scope(b_id)) => {
// The region corresponding to an outer block is a // The region corresponding to an outer block is a
// subtype of the region corresponding to an inner // subtype of the region corresponding to an inner
// block. // block.
let rm = self.tcx.region_map; let rm = self.tcx.region_map;
match region::nearest_common_ancestor(rm, a_id, b_id) { match region::nearest_common_ancestor(rm, a_id, b_id) {
Some(r_id) => ty::re_scope(r_id), Some(r_id) => re_scope(r_id),
_ => ty::re_static _ => re_static
} }
} }
// For these types, we cannot define any additional // For these types, we cannot define any additional
// relationship: // relationship:
(ty::re_free(_, _), ty::re_free(_, _)) | (re_infer(ReSkolemized(*)), _) |
(ty::re_bound(_), ty::re_bound(_)) | (_, re_infer(ReSkolemized(*))) |
(ty::re_bound(_), ty::re_free(_, _)) | (re_free(_, _), re_free(_, _)) |
(ty::re_bound(_), ty::re_scope(_)) | (re_bound(_), re_bound(_)) |
(ty::re_free(_, _), ty::re_bound(_)) | (re_bound(_), re_free(_, _)) |
(ty::re_scope(_), ty::re_bound(_)) => { (re_bound(_), re_scope(_)) |
if a == b {a} else {ty::re_static} (re_free(_, _), re_bound(_)) |
(re_scope(_), re_bound(_)) => {
if a == b {a} else {re_static}
} }
} }
} }
fn glb_concrete_regions(+a: Region, +b: Region) -> cres<Region> { fn glb_concrete_regions(&self, +a: Region, +b: Region) -> cres<Region> {
match (a, b) { match (a, b) {
(ty::re_static, r) | (r, ty::re_static) => { (re_static, r) | (r, re_static) => {
// static lives longer than everything else // static lives longer than everything else
Ok(r) Ok(r)
}
(ty::re_var(v_id), _) | (_, ty::re_var(v_id)) => {
self.tcx.sess.span_bug(
self.var_spans[*v_id],
fmt!("glb_concrete_regions invoked with \
non-concrete regions: %?, %?", a, b));
}
(ty::re_free(f_id, _), s @ ty::re_scope(s_id)) |
(s @ ty::re_scope(s_id), ty::re_free(f_id, _)) => {
// Free region is something "at least as big as
// `f_id`." If we find that the scope `f_id` is bigger
// than the scope `s_id`, then we can say that the GLB
// is the scope `s_id`. Otherwise, as we do not know
// big the free region is precisely, the GLB is undefined.
let rm = self.tcx.region_map;
match region::nearest_common_ancestor(rm, f_id, s_id) {
Some(r_id) if r_id == f_id => Ok(s),
_ => Err(ty::terr_regions_no_overlap(b, a))
} }
}
(ty::re_scope(a_id), ty::re_scope(b_id)) | (re_infer(ReVar(v_id)), _) |
(ty::re_free(a_id, _), ty::re_free(b_id, _)) => { (_, re_infer(ReVar(v_id))) => {
if a == b { self.tcx.sess.span_bug(
// Same scope or same free identifier, easy case. self.var_spans[*v_id],
Ok(a) fmt!("glb_concrete_regions invoked with \
} else { non-concrete regions: %?, %?", a, b));
// We want to generate the intersection of two }
// scopes or two free regions. So, if one of
// these scopes is a subscope of the other, return (re_free(f_id, _), s @ re_scope(s_id)) |
// it. Otherwise fail. (s @ re_scope(s_id), re_free(f_id, _)) => {
// Free region is something "at least as big as
// `f_id`." If we find that the scope `f_id` is bigger
// than the scope `s_id`, then we can say that the GLB
// is the scope `s_id`. Otherwise, as we do not know
// big the free region is precisely, the GLB is undefined.
let rm = self.tcx.region_map; let rm = self.tcx.region_map;
match region::nearest_common_ancestor(rm, a_id, b_id) { match region::nearest_common_ancestor(rm, f_id, s_id) {
Some(r_id) if a_id == r_id => Ok(ty::re_scope(b_id)), Some(r_id) if r_id == f_id => Ok(s),
Some(r_id) if b_id == r_id => Ok(ty::re_scope(a_id)), _ => Err(ty::terr_regions_no_overlap(b, a))
_ => Err(ty::terr_regions_no_overlap(b, a))
} }
} }
}
// For these types, we cannot define any additional (re_scope(a_id), re_scope(b_id)) |
// relationship: (re_free(a_id, _), re_free(b_id, _)) => {
(ty::re_bound(_), ty::re_bound(_)) | if a == b {
(ty::re_bound(_), ty::re_free(_, _)) | // Same scope or same free identifier, easy case.
(ty::re_bound(_), ty::re_scope(_)) | Ok(a)
(ty::re_free(_, _), ty::re_bound(_)) | } else {
(ty::re_scope(_), ty::re_bound(_)) => { // We want to generate the intersection of two
if a == b { // scopes or two free regions. So, if one of
Ok(a) // these scopes is a subscope of the other, return
} else { // it. Otherwise fail.
Err(ty::terr_regions_no_overlap(b, a)) let rm = self.tcx.region_map;
match region::nearest_common_ancestor(rm, a_id, b_id) {
Some(r_id) if a_id == r_id => Ok(re_scope(b_id)),
Some(r_id) if b_id == r_id => Ok(re_scope(a_id)),
_ => Err(ty::terr_regions_no_overlap(b, a))
}
}
}
// For these types, we cannot define any additional
// relationship:
(re_infer(ReSkolemized(*)), _) |
(_, re_infer(ReSkolemized(*))) |
(re_bound(_), re_bound(_)) |
(re_bound(_), re_free(_, _)) |
(re_bound(_), re_scope(_)) |
(re_free(_, _), re_bound(_)) |
(re_scope(_), re_bound(_)) => {
if a == b {
Ok(a)
} else {
Err(ty::terr_regions_no_overlap(b, a))
}
} }
}
} }
} }
fn report_type_error(span: span, terr: &ty::type_err) { fn report_type_error(&self, span: span, terr: &ty::type_err) {
let terr_str = ty::type_err_to_str(self.tcx, terr); let terr_str = ty::type_err_to_str(self.tcx, terr);
self.tcx.sess.span_err(span, terr_str); self.tcx.sess.span_err(span, terr_str);
} }
@ -803,14 +938,14 @@ fn TwoRegionsMap() -> TwoRegionsMap {
} }
impl RegionVarBindings { impl RegionVarBindings {
fn infer_variable_values() -> ~[Region] { fn infer_variable_values(&self) -> ~[Region] {
let graph = self.construct_graph(); let graph = self.construct_graph();
self.expansion(&graph); self.expansion(&graph);
self.contraction(&graph); self.contraction(&graph);
self.extract_regions_and_report_errors(&graph) self.extract_regions_and_report_errors(&graph)
} }
fn construct_graph() -> Graph { fn construct_graph(&self) -> Graph {
let num_vars = self.num_vars(); let num_vars = self.num_vars();
let num_edges = self.constraints.size(); let num_edges = self.constraints.size();
@ -871,7 +1006,7 @@ impl RegionVarBindings {
} }
} }
fn expansion(graph: &Graph) { fn expansion(&self, graph: &Graph) {
do self.iterate_until_fixed_point(~"Expansion", graph) |edge| { do self.iterate_until_fixed_point(~"Expansion", graph) |edge| {
match edge.constraint { match edge.constraint {
ConstrainRegSubVar(copy a_region, copy b_vid) => { ConstrainRegSubVar(copy a_region, copy b_vid) => {
@ -895,7 +1030,8 @@ impl RegionVarBindings {
} }
} }
fn expand_node(a_region: Region, fn expand_node(&self,
a_region: Region,
b_vid: RegionVid, b_vid: RegionVid,
b_node: &GraphNode) -> bool { b_node: &GraphNode) -> bool {
debug!("expand_node(%?, %? == %?)", debug!("expand_node(%?, %? == %?)",
@ -929,7 +1065,7 @@ impl RegionVarBindings {
} }
} }
fn contraction(graph: &Graph) { fn contraction(&self, graph: &Graph) {
do self.iterate_until_fixed_point(~"Contraction", graph) |edge| { do self.iterate_until_fixed_point(~"Contraction", graph) |edge| {
match edge.constraint { match edge.constraint {
ConstrainRegSubVar(*) => { ConstrainRegSubVar(*) => {
@ -953,33 +1089,34 @@ impl RegionVarBindings {
} }
} }
fn contract_node(a_vid: RegionVid, fn contract_node(&self,
a_vid: RegionVid,
a_node: &GraphNode, a_node: &GraphNode,
b_region: Region) -> bool { b_region: Region) -> bool {
debug!("contract_node(%? == %?/%?, %?)", debug!("contract_node(%? == %?/%?, %?)",
a_vid, a_node.value, a_node.classification, b_region); a_vid, a_node.value, a_node.classification, b_region);
return match a_node.value { return match a_node.value {
NoValue => { NoValue => {
assert a_node.classification == Contracting; assert a_node.classification == Contracting;
a_node.value = Value(b_region); a_node.value = Value(b_region);
true // changed true // changed
} }
ErrorValue => { ErrorValue => {
false // no change false // no change
} }
Value(copy a_region) => { Value(copy a_region) => {
match a_node.classification { match a_node.classification {
Expanding => { Expanding => {
check_node(&self, a_vid, a_node, a_region, b_region) check_node(self, a_vid, a_node, a_region, b_region)
} }
Contracting => { Contracting => {
adjust_node(&self, a_vid, a_node, a_region, b_region) adjust_node(self, a_vid, a_node, a_region, b_region)
} }
}
} }
}
}; };
fn check_node(self: &RegionVarBindings, fn check_node(self: &RegionVarBindings,
@ -1001,25 +1138,26 @@ impl RegionVarBindings {
a_region: Region, a_region: Region,
b_region: Region) -> bool { b_region: Region) -> bool {
match self.glb_concrete_regions(a_region, b_region) { match self.glb_concrete_regions(a_region, b_region) {
Ok(glb) => { Ok(glb) => {
if glb == a_region { if glb == a_region {
false false
} else { } else {
debug!("Contracting value of %? from %? to %?", debug!("Contracting value of %? from %? to %?",
a_vid, a_region, glb); a_vid, a_region, glb);
a_node.value = Value(glb); a_node.value = Value(glb);
true true
}
}
Err(_) => {
a_node.value = ErrorValue;
false
} }
}
Err(_) => {
a_node.value = ErrorValue;
false
}
} }
} }
} }
fn iterate_until_fixed_point( fn iterate_until_fixed_point(
&self,
tag: ~str, tag: ~str,
graph: &Graph, graph: &Graph,
body: fn(edge: &GraphEdge) -> bool) body: fn(edge: &GraphEdge) -> bool)
@ -1040,7 +1178,7 @@ impl RegionVarBindings {
debug!("---- %s Complete after %u iteration(s)", tag, iteration); debug!("---- %s Complete after %u iteration(s)", tag, iteration);
} }
fn extract_regions_and_report_errors(graph: &Graph) -> ~[Region] { fn extract_regions_and_report_errors(&self, graph: &Graph) -> ~[Region] {
let dup_map = TwoRegionsMap(); let dup_map = TwoRegionsMap();
graph.nodes.mapi(|idx, node| { graph.nodes.mapi(|idx, node| {
match node.value { match node.value {
@ -1050,7 +1188,7 @@ impl RegionVarBindings {
self.tcx.sess.span_err( self.tcx.sess.span_err(
node.span, node.span,
fmt!("Unconstrained region variable #%u", idx)); fmt!("Unconstrained region variable #%u", idx));
ty::re_static re_static
} }
ErrorValue => { ErrorValue => {
@ -1065,21 +1203,23 @@ impl RegionVarBindings {
graph, dup_map, node_vid); graph, dup_map, node_vid);
} }
} }
ty::re_static re_static
} }
} }
}) })
} }
// Used to suppress reporting the same basic error over and over // Used to suppress reporting the same basic error over and over
fn is_reported(dup_map: TwoRegionsMap, fn is_reported(&self,
dup_map: TwoRegionsMap,
r_a: Region, r_a: Region,
r_b: Region) -> bool { r_b: Region) -> bool {
let key = TwoRegions { a: r_a, b: r_b }; let key = TwoRegions { a: r_a, b: r_b };
!dup_map.insert(key, ()) !dup_map.insert(key, ())
} }
fn report_error_for_expanding_node(graph: &Graph, fn report_error_for_expanding_node(&self,
graph: &Graph,
dup_map: TwoRegionsMap, dup_map: TwoRegionsMap,
node_idx: RegionVid) { node_idx: RegionVid) {
// Errors in expanding nodes result from a lower-bound that is // Errors in expanding nodes result from a lower-bound that is
@ -1131,7 +1271,8 @@ impl RegionVarBindings {
} }
} }
fn report_error_for_contracting_node(graph: &Graph, fn report_error_for_contracting_node(&self,
graph: &Graph,
dup_map: TwoRegionsMap, dup_map: TwoRegionsMap,
node_idx: RegionVid) { node_idx: RegionVid) {
// Errors in contracting nodes result from two upper-bounds // Errors in contracting nodes result from two upper-bounds
@ -1184,7 +1325,8 @@ impl RegionVarBindings {
} }
} }
fn collect_concrete_regions(graph: &Graph, fn collect_concrete_regions(&self,
graph: &Graph,
orig_node_idx: RegionVid, orig_node_idx: RegionVid,
dir: Direction) -> ~[SpannedRegion] { dir: Direction) -> ~[SpannedRegion] {
let set = HashMap(); let set = HashMap();
@ -1226,7 +1368,8 @@ impl RegionVarBindings {
return result; return result;
} }
fn each_edge(graph: &Graph, fn each_edge(&self,
graph: &Graph,
node_idx: RegionVid, node_idx: RegionVid,
dir: Direction, dir: Direction,
op: fn(edge: &GraphEdge) -> bool) { op: fn(edge: &GraphEdge) -> bool) {

View file

@ -148,21 +148,21 @@ impl resolve_state {
fn resolve_region(orig: ty::Region) -> ty::Region { fn resolve_region(orig: ty::Region) -> ty::Region {
debug!("Resolve_region(%s)", orig.to_str(self.infcx)); debug!("Resolve_region(%s)", orig.to_str(self.infcx));
match orig { match orig {
ty::re_var(rid) => self.resolve_region_var(rid), ty::re_infer(ty::ReVar(rid)) => self.resolve_region_var(rid),
_ => orig _ => orig
} }
} }
fn resolve_region_var(rid: RegionVid) -> ty::Region { fn resolve_region_var(rid: RegionVid) -> ty::Region {
if !self.should(resolve_rvar) { if !self.should(resolve_rvar) {
return ty::re_var(rid) return ty::re_infer(ty::ReVar(rid));
} }
self.infcx.region_vars.resolve_var(rid) self.infcx.region_vars.resolve_var(rid)
} }
fn assert_not_rvar(rid: RegionVid, r: ty::Region) { fn assert_not_rvar(rid: RegionVid, r: ty::Region) {
match r { match r {
ty::re_var(rid2) => { ty::re_infer(ty::ReVar(rid2)) => {
self.err = Some(region_var_bound_by_region_var(rid, rid2)); self.err = Some(region_var_bound_by_region_var(rid, rid2));
} }
_ => { } _ => { }

View file

@ -1,6 +1,9 @@
use combine::*; use combine::*;
use unify::*; use unify::*;
use to_str::ToStr; use to_str::ToStr;
use std::list;
fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
enum Sub = combine_fields; // "subtype", "subregion" etc enum Sub = combine_fields; // "subtype", "subregion" etc
@ -125,10 +128,21 @@ impl Sub: combine {
} }
fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres<ty::FnTy> { fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres<ty::FnTy> {
debug!("fns(a=%s, b=%s)", a.to_str(self.infcx), b.to_str(self.infcx));
let _indenter = indenter();
// Rather than checking the subtype relationship between `a` and `b` // Rather than checking the subtype relationship between `a` and `b`
// as-is, we need to do some extra work here in order to make sure // as-is, we need to do some extra work here in order to make sure
// that function subtyping works correctly with respect to regions // that function subtyping works correctly with respect to regions
// (issue #2263). //
// A rather detailed discussion of what's going on here can be
// found in the region_inference.rs module.
// Take a snapshot. We'll never roll this back, but in later
// phases we do want to be able to examine "all bindings that
// were created as part of this type comparison", and making a
// snapshot is a convenient way to do that.
let snapshot = self.infcx.region_vars.start_snapshot();
// First, we instantiate each bound region in the subtype with a fresh // First, we instantiate each bound region in the subtype with a fresh
// region variable. // region variable.
@ -140,26 +154,50 @@ impl Sub: combine {
// for it. The only thing we're doing with `br` here is // for it. The only thing we're doing with `br` here is
// using it in the debug message. // using it in the debug message.
let rvar = self.infcx.next_region_var_nb(self.span); let rvar = self.infcx.next_region_var_nb(self.span);
debug!("Bound region %s maps to %s", debug!("Bound region %s maps to %?",
bound_region_to_str(self.infcx.tcx, br), bound_region_to_str(self.infcx.tcx, br),
region_to_str(self.infcx.tcx, rvar)); rvar);
rvar rvar
} }
}; };
// Second, we instantiate each bound region in the supertype with a // Second, we instantiate each bound region in the supertype with a
// fresh concrete region. // fresh concrete region.
let {fn_ty: b_fn_ty, _} = { let {fn_ty: b_fn_ty, isr: skol_isr, _} = {
do replace_bound_regions_in_fn_ty(self.infcx.tcx, @Nil, do replace_bound_regions_in_fn_ty(self.infcx.tcx, @Nil,
None, b) |br| { None, b) |br| {
// FIXME: eventually re_skolemized (issue #2263) let skol = self.infcx.region_vars.new_skolemized(br);
ty::re_bound(br) debug!("Bound region %s skolemized to %?",
bound_region_to_str(self.infcx.tcx, br),
skol);
skol
} }
}; };
// Try to compare the supertype and subtype now that they've been debug!("a_fn_ty=%s", a_fn_ty.to_str(self.infcx));
// instantiated. debug!("b_fn_ty=%s", b_fn_ty.to_str(self.infcx));
super_fns(&self, &a_fn_ty, &b_fn_ty)
// Compare types now that bound regions have been replaced.
let fn_ty = if_ok!(super_fns(&self, &a_fn_ty, &b_fn_ty));
// Presuming type comparison succeeds, we need to check
// that the skolemized regions do not "leak".
for list::each(skol_isr) |pair| {
let (skol_br, skol) = *pair;
let tainted = self.infcx.region_vars.tainted(snapshot, skol);
for tainted.each |tainted_region| {
// A is not as polymorphic as B:
if self.a_is_expected {
return Err(ty::terr_regions_insufficiently_polymorphic(
skol_br, *tainted_region));
} else {
return Err(ty::terr_regions_overly_polymorphic(
skol_br, *tainted_region));
}
}
}
return Ok(fn_ty)
} }
// Traits please (FIXME: #2794): // Traits please (FIXME: #2794):

View file

@ -23,6 +23,12 @@ impl ty::Region: ToStr {
} }
} }
impl ty::FnTy: ToStr {
fn to_str(cx: infer_ctxt) -> ~str {
ty::mk_fn(cx.tcx, self).to_str(cx)
}
}
impl<V:Copy ToStr> bound<V>: ToStr { impl<V:Copy ToStr> bound<V>: ToStr {
fn to_str(cx: infer_ctxt) -> ~str { fn to_str(cx: infer_ctxt) -> ~str {
match self { match self {

View file

@ -133,7 +133,7 @@ mod middle {
#[legacy_exports] #[legacy_exports]
mod lub; mod lub;
#[legacy_exports] #[legacy_exports]
mod region_var_bindings; mod region_inference;
#[legacy_exports] #[legacy_exports]
mod resolve; mod resolve;
#[legacy_exports] #[legacy_exports]

View file

@ -6,7 +6,8 @@ use middle::ty::{bound_copy, bound_const, bound_owned, bound_send,
use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid}; use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid};
use middle::ty::{ck_block, ck_box, ck_uniq, ctxt, field, method}; use middle::ty::{ck_block, ck_box, ck_uniq, ctxt, field, method};
use middle::ty::{mt, t, param_bound}; use middle::ty::{mt, t, param_bound};
use middle::ty::{re_bound, re_free, re_scope, re_var, re_static, Region}; use middle::ty::{re_bound, re_free, re_scope, re_infer, re_static, Region};
use middle::ty::{ReSkolemized, ReVar};
use middle::ty::{ty_bool, ty_bot, ty_box, ty_class, ty_enum}; use middle::ty::{ty_bool, ty_bot, ty_box, ty_class, ty_enum};
use middle::ty::{ty_estr, ty_evec, ty_float, ty_fn, ty_trait, ty_int}; use middle::ty::{ty_estr, ty_evec, ty_float, ty_fn, ty_trait, ty_int};
use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param}; use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param};
@ -95,7 +96,7 @@ fn explain_region_and_span(cx: ctxt, region: ty::Region)
// I believe these cases should not occur (except when debugging, // I believe these cases should not occur (except when debugging,
// perhaps) // perhaps)
re_var(_) | re_bound(_) => { re_infer(_) | re_bound(_) => {
(fmt!("lifetime %?", region), None) (fmt!("lifetime %?", region), None)
} }
}; };
@ -184,8 +185,9 @@ fn region_to_str(cx: ctxt, region: Region) -> ~str {
re_scope(_) => ~"&", re_scope(_) => ~"&",
re_bound(br) => bound_region_to_str(cx, br), re_bound(br) => bound_region_to_str(cx, br),
re_free(_, br) => bound_region_to_str(cx, br), re_free(_, br) => bound_region_to_str(cx, br),
re_var(_) => ~"&", re_infer(ReSkolemized(_, br)) => bound_region_to_str(cx, br),
re_static => ~"&static" re_infer(ReVar(_)) => ~"&",
re_static => ~"&static"
} }
} }

View file

@ -1,24 +1,47 @@
// Here, `f` is a function that takes a pointer `x` and a function fn of<T>() -> @fn(T) { fail; }
// `g`, where `g` requires its argument `y` to be in the same region fn subtype<T>(x: @fn(T)) { fail; }
// that `x` is in.
fn has_same_region(f: fn(x: &a/int, g: fn(y: &a/int))) { fn test_fn<T>(_x: &x/T, _y: &y/T, _z: &z/T) {
// Somewhat counterintuitively, this fails because, in // Here, x, y, and z are free. Other letters
// `wants_two_regions`, the `g` argument needs to be able to // are bound. Note that the arrangement
// accept any region. That is, the type that `has_same_region` // subtype::<T1>(of::<T2>()) will typecheck
// expects is *not* a subtype of the type that `wants_two_regions` // iff T1 <: T2.
// expects.
wants_two_regions(f); //~ ERROR mismatched types subtype::<fn(&a/T)>(
of::<fn(&a/T)>());
subtype::<fn(&a/T)>(
of::<fn(&b/T)>());
subtype::<fn(&b/T)>(
of::<fn(&x/T)>());
subtype::<fn(&x/T)>(
of::<fn(&b/T)>()); //~ ERROR mismatched types
subtype::<fn(&a/T, &b/T)>(
of::<fn(&a/T, &a/T)>());
subtype::<fn(&a/T, &a/T)>(
of::<fn(&a/T, &b/T)>()); //~ ERROR mismatched types
subtype::<fn(&a/T, &b/T)>(
of::<fn(&x/T, &y/T)>());
subtype::<fn(&x/T, &y/T)>(
of::<fn(&a/T, &b/T)>()); //~ ERROR mismatched types
subtype::<fn(&x/T) -> @fn(&a/T)>(
of::<fn(&x/T) -> @fn(&a/T)>());
subtype::<fn(&a/T) -> @fn(&a/T)>(
of::<fn(&a/T) -> @fn(&b/T)>()); //~ ERROR mismatched types
subtype::<fn(&a/T) -> @fn(&a/T)>(
of::<fn(&x/T) -> @fn(&b/T)>()); //~ ERROR mismatched types
subtype::<fn(&a/T) -> @fn(&b/T)>(
of::<fn(&a/T) -> @fn(&a/T)>());
} }
fn wants_two_regions(_f: fn(x: &int, g: fn(y: &int))) { fn main() {}
// Suppose we were to write code here that passed some arbitrary
// &int and some arbitrary fn(&int) to whatever's passed in as _f.
// This would be fine as far as the type annotation on the formal
// parameter _f goes, but if _f were `f` we'd be in trouble since
// `f` can't handle those arguments.
}
fn main() {
}