Implement proper subtyping for region fn types (part of #2263)
This commit is contained in:
parent
f04a6fc213
commit
1a3a70760b
20 changed files with 877 additions and 417 deletions
|
@ -136,12 +136,23 @@ pub pure fn from_elem<T: Copy>(n_elts: uint, t: T) -> @[T] {
|
|||
#[cfg(notest)]
|
||||
pub mod traits {
|
||||
#[legacy_exports];
|
||||
|
||||
#[cfg(stage0)]
|
||||
pub impl<T: Copy> @[T] : Add<&[const T],@[T]> {
|
||||
#[inline(always)]
|
||||
pure fn add(rhs: & &[const T]) -> @[T] {
|
||||
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)]
|
||||
|
|
|
@ -226,12 +226,21 @@ impl<T> *const T : Ord {
|
|||
}
|
||||
|
||||
// Equality for region pointers
|
||||
#[cfg(stage0)]
|
||||
impl<T:Eq> &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<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
|
||||
#[cfg(stage0)]
|
||||
impl<T:Ord> &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<T:Ord> &const T : Ord {
|
|||
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]
|
||||
pub fn test() {
|
||||
unsafe {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -736,6 +736,27 @@ pub pure fn filter<T: Copy>(v: &[T], f: fn(t: &T) -> bool) -> ~[T] {
|
|||
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.
|
||||
*
|
||||
|
@ -759,14 +780,17 @@ pub pure fn connect<T: Copy>(v: &[~[T]], sep: &T) -> ~[T] {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
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<T, U>(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<T: Eq>(a: &[T], b: &[T]) -> bool {
|
|||
return true;
|
||||
}
|
||||
|
||||
#[cfg(stage0)]
|
||||
impl<T: Eq> &[T] : Eq {
|
||||
#[inline(always)]
|
||||
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) }
|
||||
}
|
||||
|
||||
#[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 {
|
||||
#[inline(always)]
|
||||
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 gt<T: Ord>(a: &[T], b: &[T]) -> bool { lt(b, a) }
|
||||
|
||||
#[cfg(stage0)]
|
||||
impl<T: Ord> &[T] : Ord {
|
||||
#[inline(always)]
|
||||
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)) }
|
||||
}
|
||||
|
||||
#[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 {
|
||||
#[inline(always)]
|
||||
pure fn lt(other: &~[T]) -> bool { lt(self, (*other)) }
|
||||
|
@ -1370,6 +1419,7 @@ impl<T: Ord> @[T] : Ord {
|
|||
|
||||
#[cfg(notest)]
|
||||
pub mod traits {
|
||||
#[cfg(stage0)]
|
||||
impl<T: Copy> ~[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: 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]> {
|
||||
#[inline(always)]
|
||||
pure fn add(rhs: & &[const T]) -> ~[mut T] {
|
||||
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)]
|
||||
|
@ -1590,6 +1659,7 @@ pub trait MutableVector<T> {
|
|||
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<T: Copy> {
|
||||
|
@ -1631,6 +1701,10 @@ impl<T> ~[T]: MutableVector<T> {
|
|||
fn truncate(&mut self, newlen: uint) {
|
||||
truncate(self, newlen);
|
||||
}
|
||||
|
||||
fn retain(&mut self, f: pure fn(t: &T) -> bool) {
|
||||
retain(self, f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> ~[T]: MutableCopyableVector<T> {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<vstore>),
|
||||
terr_in_field(@type_err, ast::ident),
|
||||
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 {
|
||||
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<ast::def_id>) -> 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ty::region_variance>,
|
||||
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.
|
||||
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.
|
||||
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)));
|
||||
}
|
||||
}
|
||||
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<ty::region_variance>,
|
||||
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}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
12
src/rustc/middle/typeck/infer/macros.rs
Normal file
12
src/rustc/middle/typeck/infer/macros.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
|
||||
macro_rules! if_ok(
|
||||
($inp: expr) => (
|
||||
match $inp {
|
||||
Ok(move v) => { move v }
|
||||
Err(move e) => { return Err(e); }
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
}
|
|
@ -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>(&a/T) <: fn<b>(&b/T)? Yes: a -> b
|
||||
2. fn<a>(&a/T) <: fn(&b/T)? Yes: a -> b
|
||||
3. fn(&a/T) <: fn<b>(&b/T)? No!
|
||||
4. fn(&a/T) <: fn(&b/T)? No!
|
||||
5. fn(&a/T) <: fn(&a)? Yes!
|
||||
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>(&a/int)` to indicate that `a` is a lifetime
|
||||
parameter.
|
||||
|
||||
In case one, the two function types are equivalent because both
|
||||
reference a bound region, just with different names.
|
||||
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"):
|
||||
|
||||
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).
|
||||
fn<a>(&a/int) <: fn(&self/int)? (Yes, a => self)
|
||||
|
||||
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`.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
So, what if we reverse the order of the two function types, like this:
|
||||
|
||||
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:
|
||||
|
||||
|
@ -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>(&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
|
||||
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 `<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
|
||||
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>(&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
|
||||
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<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
|
||||
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<TwoRegions, RegionVid>;
|
|||
struct RegionVarBindings {
|
||||
tcx: ty::ctxt,
|
||||
var_spans: DVec<span>,
|
||||
values: Cell<~[ty::Region]>,
|
||||
values: Cell<~[Region]>,
|
||||
constraints: HashMap<Constraint, span>,
|
||||
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<UndoLogEntry>
|
||||
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<Region> {
|
||||
fn lub_regions(&self, span: span, a: Region, b: Region) -> cres<Region> {
|
||||
// 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<Region> {
|
||||
fn glb_regions(&self, span: span, a: Region, b: Region) -> cres<Region> {
|
||||
// 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<Region> {
|
||||
|
||||
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<Region> {
|
||||
fn glb_concrete_regions(&self, +a: Region, +b: Region) -> cres<Region> {
|
||||
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));
|
||||
}
|
||||
|
||||
(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_static, r) | (r, re_static) => {
|
||||
// static lives longer than everything else
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
(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_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));
|
||||
}
|
||||
|
||||
(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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
NoValue => {
|
||||
assert a_node.classification == Contracting;
|
||||
a_node.value = Value(b_region);
|
||||
true // changed
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 {
|
||||
false
|
||||
} else {
|
||||
debug!("Contracting value of %? from %? to %?",
|
||||
a_vid, a_region, glb);
|
||||
a_node.value = Value(glb);
|
||||
true
|
||||
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
|
||||
}
|
||||
}
|
||||
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) {
|
|
@ -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));
|
||||
}
|
||||
_ => { }
|
||||
|
|
|
@ -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<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`
|
||||
// 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):
|
||||
|
|
|
@ -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 {
|
||||
fn to_str(cx: infer_ctxt) -> ~str {
|
||||
match self {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T>() -> @fn(T) { fail; }
|
||||
fn subtype<T>(x: @fn(T)) { fail; }
|
||||
|
||||
fn test_fn<T>(_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::<T1>(of::<T2>()) will typecheck
|
||||
// iff T1 <: T2.
|
||||
|
||||
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))) {
|
||||
// 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() {
|
||||
}
|
||||
|
||||
|
||||
fn main() {}
|
Loading…
Add table
Reference in a new issue