From 1a3a70760b4dfe03e135f28b5456d61752d3e677 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 19 Oct 2012 06:01:01 -0700 Subject: [PATCH] Implement proper subtyping for region fn types (part of #2263) --- src/libcore/at_vec.rs | 11 + src/libcore/ptr.rs | 18 + src/libcore/str.rs | 38 +- src/libcore/vec.rs | 88 ++- src/rustc/metadata/tyencode.rs | 2 +- src/rustc/middle/astencode.rs | 2 +- src/rustc/middle/kind.rs | 2 +- src/rustc/middle/region.rs | 20 +- src/rustc/middle/ty.rs | 241 +++++--- src/rustc/middle/typeck/check/regionmanip.rs | 4 +- src/rustc/middle/typeck/collect.rs | 166 +++--- src/rustc/middle/typeck/infer.rs | 4 +- src/rustc/middle/typeck/infer/macros.rs | 12 + ...on_var_bindings.rs => region_inference.rs} | 543 +++++++++++------- src/rustc/middle/typeck/infer/resolve.rs | 6 +- src/rustc/middle/typeck/infer/sub.rs | 56 +- src/rustc/middle/typeck/infer/to_str.rs | 6 + src/rustc/rustc.rc | 2 +- src/rustc/util/ppaux.rs | 10 +- src/test/compile-fail/regions-fn-subtyping.rs | 63 +- 20 files changed, 877 insertions(+), 417 deletions(-) create mode 100644 src/rustc/middle/typeck/infer/macros.rs rename src/rustc/middle/typeck/infer/{region_var_bindings.rs => region_inference.rs} (70%) diff --git a/src/libcore/at_vec.rs b/src/libcore/at_vec.rs index ce3dec89e4185..83ed0f0d315fd 100644 --- a/src/libcore/at_vec.rs +++ b/src/libcore/at_vec.rs @@ -136,12 +136,23 @@ pub pure fn from_elem(n_elts: uint, t: T) -> @[T] { #[cfg(notest)] pub mod traits { #[legacy_exports]; + + #[cfg(stage0)] pub impl @[T] : Add<&[const T],@[T]> { #[inline(always)] pure fn add(rhs: & &[const T]) -> @[T] { append(self, (*rhs)) } } + + #[cfg(stage1)] + #[cfg(stage2)] + pub impl @[T] : Add<&[const T],@[T]> { + #[inline(always)] + pure fn add(rhs: & &self/[const T]) -> @[T] { + append(self, (*rhs)) + } + } } #[cfg(test)] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index ffa11dcfc754b..d9e6cd4685955 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -226,12 +226,21 @@ impl *const T : Ord { } // Equality for region pointers +#[cfg(stage0)] impl &const T : Eq { pure fn eq(other: & &const T) -> bool { return *self == *(*other); } pure fn ne(other: & &const T) -> bool { return *self != *(*other); } } +#[cfg(stage1)] +#[cfg(stage2)] +impl &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 +#[cfg(stage0)] impl &const T : Ord { pure fn lt(other: & &const T) -> bool { *self < *(*other) } pure fn le(other: & &const T) -> bool { *self <= *(*other) } @@ -239,6 +248,15 @@ impl &const T : Ord { pure fn gt(other: & &const T) -> bool { *self > *(*other) } } +#[cfg(stage1)] +#[cfg(stage2)] +impl &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] pub fn test() { unsafe { diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 8031a46431562..4f078a5e482c1 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -735,6 +735,7 @@ pure fn gt(a: &str, b: &str) -> bool { !le(a, b) } +#[cfg(stage0)] impl &str : Eq { #[inline(always)] pure fn eq(other: & &str) -> bool { @@ -744,6 +745,17 @@ impl &str : Eq { 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 { #[inline(always)] pure fn eq(other: &~str) -> bool { @@ -773,6 +785,7 @@ impl ~str : Ord { pure fn gt(other: &~str) -> bool { gt(self, (*other)) } } +#[cfg(stage0)] impl &str : Ord { #[inline(always)] 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)) } } +#[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 { #[inline(always)] pure fn lt(other: &@str) -> bool { lt(self, (*other)) } @@ -2096,12 +2122,22 @@ impl ~str: Trimmable { #[cfg(notest)] pub mod traits { + #[cfg(stage0)] impl ~str : Add<&str,~str> { #[inline(always)] pure fn add(rhs: & &str) -> ~str { 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)] @@ -2558,7 +2594,7 @@ mod tests { assert find_str_between(data, ~"ab", 2u, 4u).is_none(); 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, ~"", 6u, 43u) == Some(6u); diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index efed497651e87..24a44c591c1b6 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -736,6 +736,27 @@ pub pure fn filter(v: &[T], f: fn(t: &T) -> bool) -> ~[T] { move result } +/** + * Like `filter()`, but in place. Preserves order of `v`. Linear time. + */ +pub fn retain(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. * @@ -759,14 +780,17 @@ pub pure fn connect(v: &[~[T]], sep: &T) -> ~[T] { } /// Reduce a vector from left to right -pub pure fn foldl(z: T, v: &[U], p: fn(t: T, u: &U) -> T) -> T { - let mut accum = z; - for each(v) |elt| { - // it should be possible to move accum in, but the liveness analysis - // is not smart enough. - accum = p(accum, elt); +pub pure fn foldl(z: T, v: &[U], p: fn(t: T, u: &U) -> T) -> T { + let mut accum = move z; + let mut i = 0; + let l = v.len(); + while i < l { + // 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 @@ -1293,6 +1317,7 @@ pure fn eq(a: &[T], b: &[T]) -> bool { return true; } +#[cfg(stage0)] impl &[T] : Eq { #[inline(always)] pure fn eq(other: & &[T]) -> bool { eq(self, (*other)) } @@ -1300,6 +1325,16 @@ impl &[T] : Eq { pure fn ne(other: & &[T]) -> bool { !self.eq(other) } } +#[cfg(stage1)] +#[cfg(stage2)] +impl &[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 { #[inline(always)] pure fn eq(other: &~[T]) -> bool { eq(self, (*other)) } @@ -1335,6 +1370,7 @@ pure fn le(a: &[T], b: &[T]) -> bool { !lt(b, a) } pure fn ge(a: &[T], b: &[T]) -> bool { !lt(a, b) } pure fn gt(a: &[T], b: &[T]) -> bool { lt(b, a) } +#[cfg(stage0)] impl &[T] : Ord { #[inline(always)] pure fn lt(other: & &[T]) -> bool { lt(self, (*other)) } @@ -1346,6 +1382,19 @@ impl &[T] : Ord { pure fn gt(other: & &[T]) -> bool { gt(self, (*other)) } } +#[cfg(stage1)] +#[cfg(stage2)] +impl &[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 { #[inline(always)] pure fn lt(other: &~[T]) -> bool { lt(self, (*other)) } @@ -1370,6 +1419,7 @@ impl @[T] : Ord { #[cfg(notest)] pub mod traits { + #[cfg(stage0)] impl ~[T] : Add<&[const T],~[T]> { #[inline(always)] pure fn add(rhs: & &[const T]) -> ~[T] { @@ -1377,12 +1427,31 @@ pub mod traits { } } + #[cfg(stage1)] + #[cfg(stage2)] + impl ~[T] : Add<&[const T],~[T]> { + #[inline(always)] + pure fn add(rhs: & &self/[const T]) -> ~[T] { + append(copy self, (*rhs)) + } + } + + #[cfg(stage0)] impl ~[mut T] : Add<&[const T],~[mut T]> { #[inline(always)] pure fn add(rhs: & &[const T]) -> ~[mut T] { append_mut(copy self, (*rhs)) } } + + #[cfg(stage1)] + #[cfg(stage2)] + impl ~[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)] @@ -1590,6 +1659,7 @@ pub trait MutableVector { fn unshift(&mut self, x: T); fn swap_remove(&mut self, index: uint) -> T; fn truncate(&mut self, newlen: uint); + fn retain(&mut self, f: pure fn(t: &T) -> bool); } pub trait MutableCopyableVector { @@ -1631,6 +1701,10 @@ impl ~[T]: MutableVector { fn truncate(&mut self, newlen: uint) { truncate(self, newlen); } + + fn retain(&mut self, f: pure fn(t: &T) -> bool) { + retain(self, f); + } } impl ~[T]: MutableCopyableVector { diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs index 941dd35bdf0b4..e69815379737c 100644 --- a/src/rustc/metadata/tyencode.rs +++ b/src/rustc/metadata/tyencode.rs @@ -147,7 +147,7 @@ fn enc_region(w: io::Writer, cx: @ctxt, r: ty::Region) { ty::re_static => { w.write_char('t'); } - ty::re_var(_) => { + ty::re_infer(_) => { // these should not crop up after typeck cx.diag.handler().bug(~"Cannot encode region variables"); } diff --git a/src/rustc/middle/astencode.rs b/src/rustc/middle/astencode.rs index d264188e65d34..e10966a6c2292 100644 --- a/src/rustc/middle/astencode.rs +++ b/src/rustc/middle/astencode.rs @@ -387,7 +387,7 @@ impl ty::Region: tr { 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_scope(id) => ty::re_scope(xcx.tr_id(id)), - ty::re_static | ty::re_var(*) => self, + ty::re_static | ty::re_infer(*) => self, } } } diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs index f8eee89f18a97..089381d390446 100644 --- a/src/rustc/middle/kind.rs +++ b/src/rustc/middle/kind.rs @@ -580,7 +580,7 @@ fn check_cast_for_escaping_regions( match target_substs.self_r { Some(ty::re_scope(*)) => { return; /* case (1) */ } 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( source.span, fmt!("bad region found in kind: %?", target_substs.self_r)); diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs index 95280032ae0ab..415cd12536c05 100644 --- a/src/rustc/middle/region.rs +++ b/src/rustc/middle/region.rs @@ -112,18 +112,18 @@ fn is_subregion_of(region_map: region_map, super_region: ty::Region) -> bool { sub_region == super_region || match (sub_region, super_region) { - (_, ty::re_static) => { - true - } + (_, ty::re_static) => { + true + } - (ty::re_scope(sub_scope), ty::re_scope(super_scope)) | - (ty::re_scope(sub_scope), ty::re_free(super_scope, _)) => { - scope_contains(region_map, super_scope, sub_scope) - } + (ty::re_scope(sub_scope), ty::re_scope(super_scope)) | + (ty::re_scope(sub_scope), ty::re_free(super_scope, _)) => { + scope_contains(region_map, super_scope, sub_scope) + } - _ => { - false - } + _ => { + false + } } } diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index e065fdfe0bc79..2daf40dbf841f 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -12,7 +12,7 @@ use syntax::ast_util::{is_local, local_def}; use syntax::codemap::span; use metadata::csearch; 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::{get_lint_level, allow}; use syntax::ast::*; @@ -107,7 +107,8 @@ export InferTy, TyVar, IntVar; export ty_self, mk_self, type_has_self; export ty_class; 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 get, type_has_params, type_needs_infer, type_has_regions; 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_regions_does_not_outlive, terr_mutability, terr_purity_mismatch; 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_ret_style_mismatch; export terr_fn, terr_trait; @@ -555,7 +558,7 @@ enum Region { re_static, /// A region variable. Should not exist after typeck. - re_var(RegionVid) + re_infer(InferRegion) } #[auto_serialize] @@ -671,6 +674,8 @@ enum type_err { terr_regions_does_not_outlive(Region, Region), terr_regions_not_same(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), terr_in_field(@type_err, ast::ident), terr_sorts(expected_found), @@ -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 { pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) { match self { @@ -923,7 +961,7 @@ fn mk_t_with_id(cx: ctxt, +st: sty, o_def_id: Option) -> t { fn rflags(r: Region) -> uint { (has_regions as uint) | { match r { - ty::re_var(_) => needs_infer as uint, + ty::re_infer(_) => needs_infer as uint, _ => 0u } } @@ -2591,7 +2629,7 @@ impl Region : to_bytes::IterBytes { re_scope(ref id) => 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), re_static => 4u8.iter_bytes(lsb0, f) @@ -3253,92 +3291,103 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str { } match *err { - terr_mismatch => ~"types differ", - terr_ret_style_mismatch(values) => { - fn to_str(s: ast::ret_style) -> ~str { - match s { - ast::noreturn => ~"non-returning", - ast::return_val => ~"return-by-value" + terr_mismatch => ~"types differ", + terr_ret_style_mismatch(values) => { + fn to_str(s: ast::ret_style) -> ~str { + match s { + ast::noreturn => ~"non-returning", + 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 ", 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 } } - re_var(e0a) => { + re_infer(e0a) => { match (*other) { - re_var(e0b) => e0a == e0b, + re_infer(e0b) => e0a == e0b, _ => false } } diff --git a/src/rustc/middle/typeck/check/regionmanip.rs b/src/rustc/middle/typeck/check/regionmanip.rs index 806b234540cdc..d163dd4b3cfa6 100644 --- a/src/rustc/middle/typeck/check/regionmanip.rs +++ b/src/rustc/middle/typeck/check/regionmanip.rs @@ -97,7 +97,7 @@ fn replace_bound_regions_in_fn_ty( r: ty::Region) -> isr_alist { match r { ty::re_free(_, _) | ty::re_static | ty::re_scope(_) | - ty::re_var(_) => { + ty::re_infer(_) => { isr } ty::re_bound(br) => { @@ -165,7 +165,7 @@ fn replace_bound_regions_in_fn_ty( ty::re_static | ty::re_scope(_) | ty::re_free(_, _) | - ty::re_var(_) => r + ty::re_infer(_) => r } } } diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index c8dcde52ebb94..6a11cccd5d762 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -272,46 +272,60 @@ fn ensure_supertraits(ccx: @crate_ctxt, * * - impl_m: the method in the impl * - 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_substs: the substitutions used on the type of the trait * - self_ty: the self type of the impl */ -fn compare_impl_method(tcx: ty::ctxt, sp: span, - impl_m: ty::method, impl_tps: uint, - trait_m: ty::method, trait_substs: ty::substs, - self_ty: ty::t) { +fn compare_impl_method(tcx: ty::ctxt, + impl_tps: uint, + cm: &ConvertedMethod, + 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 { tcx.sess.span_err( - sp, fmt!("method `%s`'s purity does \ - not match the trait method's \ - purity", tcx.sess.str_of(impl_m.ident))); + cm.span, + fmt!("method `%s`'s purity does \ + not match the trait method's \ + purity", tcx.sess.str_of(impl_m.ident))); } // is this check right? if impl_m.self_ty != trait_m.self_ty { tcx.sess.span_err( - sp, fmt!("method `%s`'s self type does \ - not match the trait method's \ - self type", tcx.sess.str_of(impl_m.ident))); + cm.span, + fmt!("method `%s`'s self type does \ + not match the trait method's \ + self type", tcx.sess.str_of(impl_m.ident))); } if impl_m.tps.len() != trait_m.tps.len() { - tcx.sess.span_err(sp, fmt!("method `%s` \ - has %u type %s, but its trait declaration has %u type %s", - tcx.sess.str_of(trait_m.ident), impl_m.tps.len(), - pluralize(impl_m.tps.len(), ~"parameter"), - trait_m.tps.len(), - pluralize(trait_m.tps.len(), ~"parameter"))); + tcx.sess.span_err( + cm.span, + fmt!("method `%s` has %u type %s, but its trait \ + declaration has %u type %s", + tcx.sess.str_of(trait_m.ident), impl_m.tps.len(), + pluralize(impl_m.tps.len(), ~"parameter"), + trait_m.tps.len(), + pluralize(trait_m.tps.len(), ~"parameter"))); return; } 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 \ - but the trait has %u", - tcx.sess.str_of(trait_m.ident), - vec::len(impl_m.fty.sig.inputs), - vec::len(trait_m.fty.sig.inputs))); + tcx.sess.span_err( + cm.span, + fmt!("method `%s` has %u parameters \ + but the trait has %u", + tcx.sess.str_of(trait_m.ident), + vec::len(impl_m.fty.sig.inputs), + vec::len(trait_m.fty.sig.inputs))); 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, // but we don't have easy access to them here if impl_param_bounds.len() != trait_param_bounds.len() { - tcx.sess.span_err(sp, fmt!("in method `%s`, \ - type parameter %u has %u %s, but the same type \ - parameter in its trait declaration has %u %s", - tcx.sess.str_of(trait_m.ident), - i, impl_param_bounds.len(), - pluralize(impl_param_bounds.len(), ~"bound"), - trait_param_bounds.len(), - pluralize(trait_param_bounds.len(), ~"bound"))); + tcx.sess.span_err( + cm.span, + fmt!("in method `%s`, \ + type parameter %u has %u %s, but the same type \ + parameter in its trait declaration has %u %s", + tcx.sess.str_of(trait_m.ident), + i, impl_param_bounds.len(), + pluralize(impl_param_bounds.len(), ~"bound"), + trait_param_bounds.len(), + pluralize(trait_param_bounds.len(), ~"bound"))); return; } // 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. } + // 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 // 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 // that correspond to the parameters we will find on the impl // - 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 = 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) }; + debug!("impl_fty: %s", ty_to_str(tcx, impl_fty)); let trait_fty = { 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 // is not important for unification 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 = { self_r: Some(dummy_self_r), 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); + debug!("trait_fty (pre-subst): %s", ty_to_str(tcx, trait_fty)); ty::subst(tcx, &substs, trait_fty) }; + debug!("trait_fty: %s", ty_to_str(tcx, trait_fty)); require_same_types( - tcx, None, false, sp, impl_fty, trait_fty, - || ~"method `" + tcx.sess.str_of(trait_m.ident) - + ~"` has an incompatible type"); + tcx, None, false, cm.span, impl_fty, trait_fty, + || fmt!("method `%s` has an incompatible type", + tcx.sess.str_of(trait_m.ident))); return; // Replaces bound references to the self region with `with_r`. @@ -382,7 +414,7 @@ fn check_methods_against_trait(ccx: @crate_ctxt, rp: Option, selfty: ty::t, a_trait_ty: @ast::trait_ref, - impl_ms: ~[converted_method]) { + impl_ms: ~[ConvertedMethod]) { let tcx = ccx.tcx; 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| { match vec::find(impl_ms, |impl_m| trait_m.ident == impl_m.mty.ident) { - Some({mty: impl_m, span, _}) => { - compare_impl_method( - ccx.tcx, span, impl_m, vec::len(tps), - *trait_m, tpt.substs, selfty); - } - None => { - // If we couldn't find an implementation for trait_m in - // the impl, then see if there was a default - // implementation in the trait itself. If not, raise a - // "missing method" error. - - let provided_methods = ty::provided_trait_methods(tcx, did); - match vec::find(provided_methods, |provided_method| - *provided_method == trait_m.ident) { - Some(_) => { - // If there's a provided method with the name we - // 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))); + Some(ref cm) => { + compare_impl_method( + ccx.tcx, vec::len(tps), cm, trait_m, + &tpt.substs, selfty); + } + None => { + // If we couldn't find an implementation for trait_m in + // the impl, then see if there was a default + // implementation in the trait itself. If not, raise a + // "missing method" error. + + let provided_methods = ty::provided_trait_methods(tcx, did); + match vec::find(provided_methods, |provided_method| + *provided_method == trait_m.ident) { + Some(_) => { + // If there's a provided method with the name we + // 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))); + } } - } - } + } } // match } // |trait_m| } // fn @@ -434,12 +466,17 @@ fn convert_field(ccx: @crate_ctxt, 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, ms: ~[@ast::method], rp: Option, - rcvr_bounds: @~[ty::param_bounds]) -> ~[converted_method] { + rcvr_bounds: @~[ty::param_bounds]) -> ~[ConvertedMethod] { let tcx = ccx.tcx; do vec::map(ms) |m| { @@ -455,7 +492,8 @@ fn convert_methods(ccx: @crate_ctxt, region_param: rp, ty: 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} } } diff --git a/src/rustc/middle/typeck/infer.rs b/src/rustc/middle/typeck/infer.rs index 7619f25be4d2f..61dc10ee09e05 100644 --- a/src/rustc/middle/typeck/infer.rs +++ b/src/rustc/middle/typeck/infer.rs @@ -262,7 +262,7 @@ use util::common::{indent, indenter}; use ast::{unsafe_fn, impure_fn, pure_fn, extern_fn}; use ast::{m_const, m_imm, m_mutbl}; use dvec::DVec; -use region_var_bindings::{RegionVarBindings}; +use region_inference::{RegionVarBindings}; use ast_util::dummy_sp; use cmp::Eq; @@ -628,7 +628,7 @@ impl infer_ctxt { } 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, diff --git a/src/rustc/middle/typeck/infer/macros.rs b/src/rustc/middle/typeck/infer/macros.rs new file mode 100644 index 0000000000000..faac3d9ac55c4 --- /dev/null +++ b/src/rustc/middle/typeck/infer/macros.rs @@ -0,0 +1,12 @@ +{ + +macro_rules! if_ok( + ($inp: expr) => ( + match $inp { + Ok(move v) => { move v } + Err(move e) => { return Err(e); } + } + ) +); + +} \ No newline at end of file diff --git a/src/rustc/middle/typeck/infer/region_var_bindings.rs b/src/rustc/middle/typeck/infer/region_inference.rs similarity index 70% rename from src/rustc/middle/typeck/infer/region_var_bindings.rs rename to src/rustc/middle/typeck/infer/region_inference.rs index 86a872341f561..bdc764a8d0b09 100644 --- a/src/rustc/middle/typeck/infer/region_var_bindings.rs +++ b/src/rustc/middle/typeck/infer/region_inference.rs @@ -134,45 +134,66 @@ to read the whole thing): 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 types as they are not interesting, and I am writing binding -explicitly): +The answer is that of course it does. These two types are basically +the same, except that in one we used the name `a` and one we used +the name `b`. - 1. fn(&a/T) <: fn(&b/T)? Yes: a -> b - 2. fn(&a/T) <: fn(&b/T)? Yes: a -> b - 3. fn(&a/T) <: fn(&b/T)? No! - 4. fn(&a/T) <: fn(&b/T)? No! - 5. fn(&a/T) <: fn(&a)? Yes! - -In case one, the two function types are equivalent because both -reference a bound region, just with different names. - -In case two, the subtyping relationship is valid because the subtyping -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`, -but the supertype must accept pointers in any region. That means that -it is not safe to use the subtype where the supertype is expected: the -supertype can be passed pointers in any region, but the subtype can -only handle pointers in the region `a`. - -Case four is fairly simple. The subtype expects region `a` but the supertype -expects region `b`. These two regions are not the same. Therefore, not -a subtype. - -Case five is similar to four, except that the subtype and supertype -expect the same region, so in fact they are the same type. That's -fine. +In the examples that follow, it becomes very important to know whether +a lifetime is bound in a function type (that is, is a lifetime +parameter) or appears free (is defined in some outer scope). +Therefore, from now on I will write the bindings explicitly, using a +notation like `fn(&a/int)` to indicate that `a` is a lifetime +parameter. + +Now let's consider two more function types. Here, we assume that the +`self` lifetime is defined somewhere outside and hence is not a +lifetime parameter bound by the function type (it "appears free"): + + fn(&a/int) <: fn(&self/int)? (Yes, a => self) + +This subtyping relation does in fact hold. To see why, you have to +consider what subtyping means. One way to look at `T1 <: T2` is to +say that it means that it is always ok to treat an instance of `T1` as +if it had the type `T2`. So, with our functions, it is always ok to +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. + +You can also look at subtyping as the *is a* relationship. This amounts +to the same thing: a function that accepts pointers with any lifetime +*is a* function that accepts pointers with some specific lifetime. + +So, what if we reverse the order of the two function types, like this: + + fn(&self/int) <: fn(&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/int, &b/int) <: fn(&a/int, &a/int)? (Yes) + fn(&a/int, &a/int) <: fn(&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: @@ -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 visible from "the outside" -I'll walk briefly through how this works with the examples above. -I'll ignore the last step for now, it'll come up in the complex -examples below. +Let's walk through some examples and see how this algorithm plays out. #### First example -Let's look first at the first example, which was: +We'll start with the first example, which was: - 1. fn(&a/T) <: fn(&b/T/T)? Yes: a -> x + 1. fn(&a/T) <: fn(&b/T)? Yes: a -> b After steps 1 and 2 of the algorithm we will have replaced the types 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`) to indicate skolemized region names. We can assume they don't appear elsewhere. Note that neither the sub- nor the supertype bind any -region names anymore (that is, the `` and `` 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 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: - 3. fn(&a/T) <: fn(&b/T)? No! + 3. fn(&self/T) <: fn(&b/T)? No! After steps 1 and 2 of the algorithm we will have replaced the types 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` -was not bound, and hence was left as-is and not replaced with a -variable. The next step is again to check that the parameter types -match. This will ultimately require (as before) that `&a` <= `&x` -must hold: but this does not hold. `a` and `x` are both distinct free -regions. So the subtype check fails. +This looks pretty much the same as before, except that on the LHS +`&self` was not bound, and hence was left as-is and not replaced with +a variable. The next step is again to check that the parameter types +match. This will ultimately require (as before) that `&self` <= `&x` +must hold: but this does not hold. `self` and `x` are both distinct +free regions. So the subtype check fails. #### Checking for skolemization leaks -You may be wondering about that mysterious last step. So far it has not -been relevant. The purpose of that last step is to catch something like -*this*: +You may be wondering about that mysterious last step in the algorithm. +So far it has not been relevant. The purpose of that last step is to +catch something like *this*: fn() -> fn(&a/T) <: fn() -> fn(&b/T)? No. @@ -295,10 +314,11 @@ rule. So the way we solve this is to add a fourth step that examines the constraints that refer to skolemized names. Basically, consider a -non-directed verison of the constraint graph. The only things -reachable from a skolemized region ought to be the region variables -that were created at the same time. So this case here would fail -because `&x` was created alone, but is relatable to `&A`. +non-directed verison of the constraint graph. Let `Tainted(x)` be the +set of all things reachable from a skolemized variable `x`. +`Tainted(x)` should not contain any regions that existed before the +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 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 to_str::ToStr; use util::ppaux::note_and_explain_region; @@ -350,7 +371,7 @@ impl Constraint : cmp::Eq { } 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 { ConstrainVarSubVar(ref v0, ref v1) => to_bytes::iter_bytes_3(&0u8, v0, v1, lsb0, f), @@ -394,10 +415,11 @@ type CombineMap = HashMap; struct RegionVarBindings { tcx: ty::ctxt, var_spans: DVec, - values: Cell<~[ty::Region]>, + values: Cell<~[Region]>, constraints: HashMap, lubs: CombineMap, glbs: CombineMap, + mut skolemization_count: uint, // 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 // we end up adding entries for things like the lower bound on // a variable and so forth, which can never be rolled back. - undo_log: DVec + mut undo_log: ~[UndoLogEntry] } fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings { @@ -418,7 +440,8 @@ fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings { constraints: HashMap(), lubs: CombineMap(), glbs: CombineMap(), - undo_log: DVec() + skolemization_count: 0, + undo_log: ~[] } } @@ -430,11 +453,11 @@ fn CombineMap() -> CombineMap { } impl RegionVarBindings { - fn in_snapshot() -> bool { + fn in_snapshot(&self) -> bool { self.undo_log.len() > 0 } - fn start_snapshot() -> uint { + fn start_snapshot(&self) -> uint { debug!("RegionVarBindings: snapshot()=%u", self.undo_log.len()); if self.in_snapshot() { self.undo_log.len() @@ -444,14 +467,14 @@ impl RegionVarBindings { } } - fn commit() { + fn commit(&self) { debug!("RegionVarBindings: commit()"); while self.undo_log.len() > 0 { self.undo_log.pop(); } } - fn rollback_to(snapshot: uint) { + fn rollback_to(&self, snapshot: uint) { debug!("RegionVarBindings: rollback_to(%u)", snapshot); while self.undo_log.len() > snapshot { 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() } - fn new_region_var(span: span) -> RegionVid { + fn new_region_var(&self, span: span) -> RegionVid { let id = self.num_vars(); self.var_spans.push(span); let vid = RegionVid(id); @@ -488,7 +511,13 @@ impl RegionVarBindings { 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 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 assert self.values.is_empty(); debug!("RegionVarBindings: make_subregion(%?, %?)", 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); Ok(()) } - (r, ty::re_var(sup_id)) => { + (r, re_infer(ReVar(sup_id))) => { self.add_constraint(ConstrainRegSubVar(r, sup_id), span); Ok(()) } - (ty::re_var(sub_id), r) => { + (re_infer(ReVar(sub_id)), r) => { self.add_constraint(ConstrainVarSubReg(sub_id, r), span); Ok(()) } @@ -529,17 +559,17 @@ impl RegionVarBindings { } } - fn lub_regions(span: span, a: Region, b: Region) -> cres { + fn lub_regions(&self, span: span, a: Region, b: Region) -> cres { // cannot add constraints once regions are resolved assert self.values.is_empty(); debug!("RegionVarBindings: lub_regions(%?, %?)", a, b); match (a, b) { - (ty::re_static, _) | (_, ty::re_static) => { - Ok(ty::re_static) // nothing lives longer than static + (re_static, _) | (_, re_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.lubs, a, b, span, |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 { + fn glb_regions(&self, span: span, a: Region, b: Region) -> cres { // cannot add constraints once regions are resolved assert self.values.is_empty(); debug!("RegionVarBindings: glb_regions(%?, %?)", 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 Ok(r) } - (ty::re_var(*), _) | (_, ty::re_var(*)) => { + (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => { self.combine_vars( self.glbs, a, b, span, |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); if self.values.is_empty() { self.tcx.sess.span_bug( @@ -586,29 +616,129 @@ impl RegionVarBindings { 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<()>) -> cres { let vars = TwoRegions { a: a, b: b }; match combines.find(vars) { - Some(c) => Ok(ty::re_var(c)), + Some(c) => Ok(re_infer(ReVar(c))), None => { let c = self.new_region_var(span); combines.insert(vars, c); if self.in_snapshot() { self.undo_log.push(AddCombination(combines, vars)); } - do relate(a, ty::re_var(c)).then { - do relate(b, ty::re_var(c)).then { - debug!("combine_vars() c=%?", ty::re_var(c)); - Ok(ty::re_var(c)) + do relate(a, re_infer(ReVar(c))).then { + do relate(b, re_infer(ReVar(c))).then { + debug!("combine_vars() c=%?", 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 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, errors are reported. */ - fn resolve_regions() { + fn resolve_regions(&self) { debug!("RegionVarBindings: resolve_regions()"); self.values.put_back(self.infer_variable_values()); } } 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) } - fn lub_concrete_regions(+a: Region, +b: Region) -> Region { + fn lub_concrete_regions(&self, +a: Region, +b: Region) -> Region { match (a, b) { - (ty::re_static, _) | (_, ty::re_static) => { - ty::re_static // nothing lives longer than static + (re_static, _) | (_, re_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.var_spans[*v_id], fmt!("lub_concrete_regions invoked with \ non-concrete regions: %?, %?", a, b)); } - (f @ ty::re_free(f_id, _), ty::re_scope(s_id)) | - (ty::re_scope(s_id), f @ ty::re_free(f_id, _)) => { + (f @ re_free(f_id, _), re_scope(s_id)) | + (re_scope(s_id), f @ re_free(f_id, _)) => { // A "free" region can be interpreted as "some region // at least as big as the block f_id". So, we can // reasonably compare free regions and scopes: @@ -654,98 +784,103 @@ priv impl RegionVarBindings { // otherwise, we don't know what the free region is, // 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 // subtype of the region corresponding to an inner // block. let rm = self.tcx.region_map; match region::nearest_common_ancestor(rm, a_id, b_id) { - Some(r_id) => ty::re_scope(r_id), - _ => ty::re_static + Some(r_id) => re_scope(r_id), + _ => re_static } } // For these types, we cannot define any additional // relationship: - (ty::re_free(_, _), ty::re_free(_, _)) | - (ty::re_bound(_), ty::re_bound(_)) | - (ty::re_bound(_), ty::re_free(_, _)) | - (ty::re_bound(_), ty::re_scope(_)) | - (ty::re_free(_, _), ty::re_bound(_)) | - (ty::re_scope(_), ty::re_bound(_)) => { - if a == b {a} else {ty::re_static} + (re_infer(ReSkolemized(*)), _) | + (_, re_infer(ReSkolemized(*))) | + (re_free(_, _), re_free(_, _)) | + (re_bound(_), re_bound(_)) | + (re_bound(_), re_free(_, _)) | + (re_bound(_), re_scope(_)) | + (re_free(_, _), re_bound(_)) | + (re_scope(_), re_bound(_)) => { + if a == b {a} else {re_static} } } } - fn glb_concrete_regions(+a: Region, +b: Region) -> cres { + fn glb_concrete_regions(&self, +a: Region, +b: Region) -> cres { match (a, b) { - (ty::re_static, r) | (r, ty::re_static) => { - // static lives longer than everything else - 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)); - } + (re_static, r) | (r, re_static) => { + // static lives longer than everything else + Ok(r) + } - (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)) + (re_infer(ReVar(v_id)), _) | + (_, re_infer(ReVar(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_scope(a_id), ty::re_scope(b_id)) | - (ty::re_free(a_id, _), ty::re_free(b_id, _)) => { - if a == b { - // Same scope or same free identifier, easy case. - Ok(a) - } else { - // 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 - // it. Otherwise fail. + (re_free(f_id, _), s @ re_scope(s_id)) | + (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; - match region::nearest_common_ancestor(rm, a_id, b_id) { - Some(r_id) if a_id == r_id => Ok(ty::re_scope(b_id)), - Some(r_id) if b_id == r_id => Ok(ty::re_scope(a_id)), - _ => Err(ty::terr_regions_no_overlap(b, a)) + 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)) } } - } - // For these types, we cannot define any additional - // relationship: - (ty::re_bound(_), ty::re_bound(_)) | - (ty::re_bound(_), ty::re_free(_, _)) | - (ty::re_bound(_), ty::re_scope(_)) | - (ty::re_free(_, _), ty::re_bound(_)) | - (ty::re_scope(_), ty::re_bound(_)) => { - if a == b { - Ok(a) - } else { - Err(ty::terr_regions_no_overlap(b, a)) + (re_scope(a_id), re_scope(b_id)) | + (re_free(a_id, _), re_free(b_id, _)) => { + if a == b { + // Same scope or same free identifier, easy case. + Ok(a) + } else { + // 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 + // it. Otherwise fail. + 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); self.tcx.sess.span_err(span, terr_str); } @@ -803,14 +938,14 @@ fn TwoRegionsMap() -> TwoRegionsMap { } impl RegionVarBindings { - fn infer_variable_values() -> ~[Region] { + fn infer_variable_values(&self) -> ~[Region] { let graph = self.construct_graph(); self.expansion(&graph); self.contraction(&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_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| { match edge.constraint { 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_node: &GraphNode) -> bool { 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| { match edge.constraint { ConstrainRegSubVar(*) => { @@ -953,33 +1089,34 @@ impl RegionVarBindings { } } - fn contract_node(a_vid: RegionVid, + fn contract_node(&self, + a_vid: RegionVid, a_node: &GraphNode, b_region: Region) -> bool { debug!("contract_node(%? == %?/%?, %?)", a_vid, a_node.value, a_node.classification, b_region); return match a_node.value { - NoValue => { - assert a_node.classification == Contracting; - a_node.value = Value(b_region); - true // changed - } + NoValue => { + assert a_node.classification == Contracting; + a_node.value = Value(b_region); + true // changed + } - ErrorValue => { - false // no change - } + ErrorValue => { + false // no change + } - Value(copy a_region) => { - match a_node.classification { - Expanding => { - check_node(&self, a_vid, a_node, a_region, b_region) - } - Contracting => { - adjust_node(&self, a_vid, a_node, a_region, b_region) - } + Value(copy a_region) => { + match a_node.classification { + Expanding => { + check_node(self, a_vid, a_node, a_region, b_region) + } + Contracting => { + adjust_node(self, a_vid, a_node, a_region, b_region) + } + } } - } }; fn check_node(self: &RegionVarBindings, @@ -1001,25 +1138,26 @@ impl RegionVarBindings { a_region: Region, b_region: Region) -> bool { match self.glb_concrete_regions(a_region, b_region) { - Ok(glb) => { - if glb == a_region { + Ok(glb) => { + if glb == a_region { + false + } else { + debug!("Contracting value of %? from %? to %?", + a_vid, a_region, glb); + a_node.value = Value(glb); + true + } + } + Err(_) => { + a_node.value = ErrorValue; false - } else { - debug!("Contracting value of %? from %? to %?", - a_vid, a_region, glb); - a_node.value = Value(glb); - true } - } - Err(_) => { - a_node.value = ErrorValue; - false - } } } } fn iterate_until_fixed_point( + &self, tag: ~str, graph: &Graph, body: fn(edge: &GraphEdge) -> bool) @@ -1040,7 +1178,7 @@ impl RegionVarBindings { 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(); graph.nodes.mapi(|idx, node| { match node.value { @@ -1050,7 +1188,7 @@ impl RegionVarBindings { self.tcx.sess.span_err( node.span, fmt!("Unconstrained region variable #%u", idx)); - ty::re_static + re_static } ErrorValue => { @@ -1065,21 +1203,23 @@ impl RegionVarBindings { graph, dup_map, node_vid); } } - ty::re_static + re_static } } }) } // 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_b: Region) -> bool { let key = TwoRegions { a: r_a, b: r_b }; !dup_map.insert(key, ()) } - fn report_error_for_expanding_node(graph: &Graph, + fn report_error_for_expanding_node(&self, + graph: &Graph, dup_map: TwoRegionsMap, node_idx: RegionVid) { // 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, node_idx: RegionVid) { // 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, dir: Direction) -> ~[SpannedRegion] { let set = HashMap(); @@ -1226,7 +1368,8 @@ impl RegionVarBindings { return result; } - fn each_edge(graph: &Graph, + fn each_edge(&self, + graph: &Graph, node_idx: RegionVid, dir: Direction, op: fn(edge: &GraphEdge) -> bool) { diff --git a/src/rustc/middle/typeck/infer/resolve.rs b/src/rustc/middle/typeck/infer/resolve.rs index 5a55fbf9a5dfc..f0794bf752e98 100644 --- a/src/rustc/middle/typeck/infer/resolve.rs +++ b/src/rustc/middle/typeck/infer/resolve.rs @@ -148,21 +148,21 @@ impl resolve_state { fn resolve_region(orig: ty::Region) -> ty::Region { debug!("Resolve_region(%s)", orig.to_str(self.infcx)); match orig { - ty::re_var(rid) => self.resolve_region_var(rid), + ty::re_infer(ty::ReVar(rid)) => self.resolve_region_var(rid), _ => orig } } fn resolve_region_var(rid: RegionVid) -> ty::Region { if !self.should(resolve_rvar) { - return ty::re_var(rid) + return ty::re_infer(ty::ReVar(rid)); } self.infcx.region_vars.resolve_var(rid) } fn assert_not_rvar(rid: RegionVid, r: ty::Region) { match r { - ty::re_var(rid2) => { + ty::re_infer(ty::ReVar(rid2)) => { self.err = Some(region_var_bound_by_region_var(rid, rid2)); } _ => { } diff --git a/src/rustc/middle/typeck/infer/sub.rs b/src/rustc/middle/typeck/infer/sub.rs index 0aba993512bb4..3677911b5eaa1 100644 --- a/src/rustc/middle/typeck/infer/sub.rs +++ b/src/rustc/middle/typeck/infer/sub.rs @@ -1,6 +1,9 @@ use combine::*; use unify::*; use to_str::ToStr; +use std::list; + +fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export. enum Sub = combine_fields; // "subtype", "subregion" etc @@ -125,10 +128,21 @@ impl Sub: combine { } fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres { + 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` // as-is, we need to do some extra work here in order to make sure // 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 // region variable. @@ -140,26 +154,50 @@ impl Sub: combine { // for it. The only thing we're doing with `br` here is // using it in the debug message. 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), - region_to_str(self.infcx.tcx, rvar)); + rvar); rvar } }; // Second, we instantiate each bound region in the supertype with a // 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, None, b) |br| { - // FIXME: eventually re_skolemized (issue #2263) - ty::re_bound(br) + let skol = self.infcx.region_vars.new_skolemized(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 - // instantiated. - super_fns(&self, &a_fn_ty, &b_fn_ty) + debug!("a_fn_ty=%s", a_fn_ty.to_str(self.infcx)); + debug!("b_fn_ty=%s", b_fn_ty.to_str(self.infcx)); + + // 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): diff --git a/src/rustc/middle/typeck/infer/to_str.rs b/src/rustc/middle/typeck/infer/to_str.rs index c98a217a7464e..3c9b22ff66f4d 100644 --- a/src/rustc/middle/typeck/infer/to_str.rs +++ b/src/rustc/middle/typeck/infer/to_str.rs @@ -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 bound: ToStr { fn to_str(cx: infer_ctxt) -> ~str { match self { diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc index 10fd378ed1711..0e6f6536bf57e 100644 --- a/src/rustc/rustc.rc +++ b/src/rustc/rustc.rc @@ -133,7 +133,7 @@ mod middle { #[legacy_exports] mod lub; #[legacy_exports] - mod region_var_bindings; + mod region_inference; #[legacy_exports] mod resolve; #[legacy_exports] diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index 8207082cf20e6..75e21a5297b51 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -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::{ck_block, ck_box, ck_uniq, ctxt, field, method}; 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_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}; @@ -95,7 +96,7 @@ fn explain_region_and_span(cx: ctxt, region: ty::Region) // I believe these cases should not occur (except when debugging, // perhaps) - re_var(_) | re_bound(_) => { + re_infer(_) | re_bound(_) => { (fmt!("lifetime %?", region), None) } }; @@ -184,8 +185,9 @@ fn region_to_str(cx: ctxt, region: Region) -> ~str { re_scope(_) => ~"&", re_bound(br) => bound_region_to_str(cx, br), re_free(_, br) => bound_region_to_str(cx, br), - re_var(_) => ~"&", - re_static => ~"&static" + re_infer(ReSkolemized(_, br)) => bound_region_to_str(cx, br), + re_infer(ReVar(_)) => ~"&", + re_static => ~"&static" } } diff --git a/src/test/compile-fail/regions-fn-subtyping.rs b/src/test/compile-fail/regions-fn-subtyping.rs index 09456b1ecaa1e..8dc4fd2f6e282 100644 --- a/src/test/compile-fail/regions-fn-subtyping.rs +++ b/src/test/compile-fail/regions-fn-subtyping.rs @@ -1,24 +1,47 @@ -// Here, `f` is a function that takes a pointer `x` and a function -// `g`, where `g` requires its argument `y` to be in the same region -// that `x` is in. -fn has_same_region(f: fn(x: &a/int, g: fn(y: &a/int))) { - // Somewhat counterintuitively, this fails because, in - // `wants_two_regions`, the `g` argument needs to be able to - // accept any region. That is, the type that `has_same_region` - // expects is *not* a subtype of the type that `wants_two_regions` - // expects. - wants_two_regions(f); //~ ERROR mismatched types -} +fn of() -> @fn(T) { fail; } +fn subtype(x: @fn(T)) { fail; } -fn wants_two_regions(_f: fn(x: &int, g: fn(y: &int))) { - // 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 test_fn(_x: &x/T, _y: &y/T, _z: &z/T) { + // Here, x, y, and z are free. Other letters + // are bound. Note that the arrangement + // subtype::(of::()) will typecheck + // iff T1 <: T2. -fn main() { -} + subtype::( + of::()); + + subtype::( + of::()); + + subtype::( + of::()); + + subtype::( + of::()); //~ ERROR mismatched types + + subtype::( + of::()); + subtype::( + of::()); //~ ERROR mismatched types + + subtype::( + of::()); + + subtype::( + of::()); //~ ERROR mismatched types + + subtype:: @fn(&a/T)>( + of:: @fn(&a/T)>()); + + subtype:: @fn(&a/T)>( + of:: @fn(&b/T)>()); //~ ERROR mismatched types + + subtype:: @fn(&a/T)>( + of:: @fn(&b/T)>()); //~ ERROR mismatched types + + subtype:: @fn(&b/T)>( + of:: @fn(&a/T)>()); +} +fn main() {} \ No newline at end of file