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)]
|
#[cfg(notest)]
|
||||||
pub mod traits {
|
pub mod traits {
|
||||||
#[legacy_exports];
|
#[legacy_exports];
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
pub impl<T: Copy> @[T] : Add<&[const T],@[T]> {
|
pub impl<T: Copy> @[T] : Add<&[const T],@[T]> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn add(rhs: & &[const T]) -> @[T] {
|
pure fn add(rhs: & &[const T]) -> @[T] {
|
||||||
append(self, (*rhs))
|
append(self, (*rhs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
pub impl<T: Copy> @[T] : Add<&[const T],@[T]> {
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn add(rhs: & &self/[const T]) -> @[T] {
|
||||||
|
append(self, (*rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -226,12 +226,21 @@ impl<T> *const T : Ord {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equality for region pointers
|
// Equality for region pointers
|
||||||
|
#[cfg(stage0)]
|
||||||
impl<T:Eq> &const T : Eq {
|
impl<T:Eq> &const T : Eq {
|
||||||
pure fn eq(other: & &const T) -> bool { return *self == *(*other); }
|
pure fn eq(other: & &const T) -> bool { return *self == *(*other); }
|
||||||
pure fn ne(other: & &const T) -> bool { return *self != *(*other); }
|
pure fn ne(other: & &const T) -> bool { return *self != *(*other); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl<T:Eq> &const T : Eq {
|
||||||
|
pure fn eq(other: & &self/const T) -> bool { return *self == *(*other); }
|
||||||
|
pure fn ne(other: & &self/const T) -> bool { return *self != *(*other); }
|
||||||
|
}
|
||||||
|
|
||||||
// Comparison for region pointers
|
// Comparison for region pointers
|
||||||
|
#[cfg(stage0)]
|
||||||
impl<T:Ord> &const T : Ord {
|
impl<T:Ord> &const T : Ord {
|
||||||
pure fn lt(other: & &const T) -> bool { *self < *(*other) }
|
pure fn lt(other: & &const T) -> bool { *self < *(*other) }
|
||||||
pure fn le(other: & &const T) -> bool { *self <= *(*other) }
|
pure fn le(other: & &const T) -> bool { *self <= *(*other) }
|
||||||
|
@ -239,6 +248,15 @@ impl<T:Ord> &const T : Ord {
|
||||||
pure fn gt(other: & &const T) -> bool { *self > *(*other) }
|
pure fn gt(other: & &const T) -> bool { *self > *(*other) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl<T:Ord> &const T : Ord {
|
||||||
|
pure fn lt(other: & &self/const T) -> bool { *self < *(*other) }
|
||||||
|
pure fn le(other: & &self/const T) -> bool { *self <= *(*other) }
|
||||||
|
pure fn ge(other: & &self/const T) -> bool { *self >= *(*other) }
|
||||||
|
pure fn gt(other: & &self/const T) -> bool { *self > *(*other) }
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test() {
|
pub fn test() {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -735,6 +735,7 @@ pure fn gt(a: &str, b: &str) -> bool {
|
||||||
!le(a, b)
|
!le(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
impl &str : Eq {
|
impl &str : Eq {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn eq(other: & &str) -> bool {
|
pure fn eq(other: & &str) -> bool {
|
||||||
|
@ -744,6 +745,17 @@ impl &str : Eq {
|
||||||
pure fn ne(other: & &str) -> bool { !self.eq(other) }
|
pure fn ne(other: & &str) -> bool { !self.eq(other) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl &str : Eq {
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn eq(other: & &self/str) -> bool {
|
||||||
|
eq_slice(self, (*other))
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn ne(other: & &self/str) -> bool { !self.eq(other) }
|
||||||
|
}
|
||||||
|
|
||||||
impl ~str : Eq {
|
impl ~str : Eq {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn eq(other: &~str) -> bool {
|
pure fn eq(other: &~str) -> bool {
|
||||||
|
@ -773,6 +785,7 @@ impl ~str : Ord {
|
||||||
pure fn gt(other: &~str) -> bool { gt(self, (*other)) }
|
pure fn gt(other: &~str) -> bool { gt(self, (*other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
impl &str : Ord {
|
impl &str : Ord {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn lt(other: & &str) -> bool { lt(self, (*other)) }
|
pure fn lt(other: & &str) -> bool { lt(self, (*other)) }
|
||||||
|
@ -784,6 +797,19 @@ impl &str : Ord {
|
||||||
pure fn gt(other: & &str) -> bool { gt(self, (*other)) }
|
pure fn gt(other: & &str) -> bool { gt(self, (*other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl &str : Ord {
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn lt(other: & &self/str) -> bool { lt(self, (*other)) }
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn le(other: & &self/str) -> bool { le(self, (*other)) }
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn ge(other: & &self/str) -> bool { ge(self, (*other)) }
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn gt(other: & &self/str) -> bool { gt(self, (*other)) }
|
||||||
|
}
|
||||||
|
|
||||||
impl @str : Ord {
|
impl @str : Ord {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn lt(other: &@str) -> bool { lt(self, (*other)) }
|
pure fn lt(other: &@str) -> bool { lt(self, (*other)) }
|
||||||
|
@ -2096,12 +2122,22 @@ impl ~str: Trimmable {
|
||||||
|
|
||||||
#[cfg(notest)]
|
#[cfg(notest)]
|
||||||
pub mod traits {
|
pub mod traits {
|
||||||
|
#[cfg(stage0)]
|
||||||
impl ~str : Add<&str,~str> {
|
impl ~str : Add<&str,~str> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn add(rhs: & &str) -> ~str {
|
pure fn add(rhs: & &str) -> ~str {
|
||||||
append(copy self, (*rhs))
|
append(copy self, (*rhs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl ~str : Add<&str,~str> {
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn add(rhs: & &self/str) -> ~str {
|
||||||
|
append(copy self, (*rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -2558,7 +2594,7 @@ mod tests {
|
||||||
assert find_str_between(data, ~"ab", 2u, 4u).is_none();
|
assert find_str_between(data, ~"ab", 2u, 4u).is_none();
|
||||||
|
|
||||||
let mut data = ~"ประเทศไทย中华Việt Nam";
|
let mut data = ~"ประเทศไทย中华Việt Nam";
|
||||||
data += data;
|
data = data + data;
|
||||||
assert find_str_between(data, ~"", 0u, 43u) == Some(0u);
|
assert find_str_between(data, ~"", 0u, 43u) == Some(0u);
|
||||||
assert find_str_between(data, ~"", 6u, 43u) == Some(6u);
|
assert find_str_between(data, ~"", 6u, 43u) == Some(6u);
|
||||||
|
|
||||||
|
|
|
@ -736,6 +736,27 @@ pub pure fn filter<T: Copy>(v: &[T], f: fn(t: &T) -> bool) -> ~[T] {
|
||||||
move result
|
move result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `filter()`, but in place. Preserves order of `v`. Linear time.
|
||||||
|
*/
|
||||||
|
pub fn retain<T>(v: &mut ~[T], f: pure fn(t: &T) -> bool) {
|
||||||
|
let len = v.len();
|
||||||
|
let mut deleted: uint = 0;
|
||||||
|
|
||||||
|
for uint::range(0, len) |i| {
|
||||||
|
if !f(&v[i]) {
|
||||||
|
deleted += 1;
|
||||||
|
} else if deleted > 0 {
|
||||||
|
v[i - deleted] <-> v[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while deleted > 0 {
|
||||||
|
v.pop();
|
||||||
|
deleted -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenate a vector of vectors.
|
* Concatenate a vector of vectors.
|
||||||
*
|
*
|
||||||
|
@ -759,14 +780,17 @@ pub pure fn connect<T: Copy>(v: &[~[T]], sep: &T) -> ~[T] {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reduce a vector from left to right
|
/// Reduce a vector from left to right
|
||||||
pub pure fn foldl<T: Copy, U>(z: T, v: &[U], p: fn(t: T, u: &U) -> T) -> T {
|
pub pure fn foldl<T, U>(z: T, v: &[U], p: fn(t: T, u: &U) -> T) -> T {
|
||||||
let mut accum = z;
|
let mut accum = move z;
|
||||||
for each(v) |elt| {
|
let mut i = 0;
|
||||||
// it should be possible to move accum in, but the liveness analysis
|
let l = v.len();
|
||||||
// is not smart enough.
|
while i < l {
|
||||||
accum = p(accum, elt);
|
// Use a while loop so that liveness analysis can handle moving
|
||||||
|
// the accumulator.
|
||||||
|
accum = p(move accum, &v[i]);
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
return accum;
|
return move accum;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reduce a vector from right to left
|
/// Reduce a vector from right to left
|
||||||
|
@ -1293,6 +1317,7 @@ pure fn eq<T: Eq>(a: &[T], b: &[T]) -> bool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
impl<T: Eq> &[T] : Eq {
|
impl<T: Eq> &[T] : Eq {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn eq(other: & &[T]) -> bool { eq(self, (*other)) }
|
pure fn eq(other: & &[T]) -> bool { eq(self, (*other)) }
|
||||||
|
@ -1300,6 +1325,16 @@ impl<T: Eq> &[T] : Eq {
|
||||||
pure fn ne(other: & &[T]) -> bool { !self.eq(other) }
|
pure fn ne(other: & &[T]) -> bool { !self.eq(other) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl<T: Eq> &[T] : Eq {
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn eq(other: & &self/[T]) -> bool { eq(self, (*other)) }
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn ne(other: & &self/[T]) -> bool { !self.eq(other) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T: Eq> ~[T] : Eq {
|
impl<T: Eq> ~[T] : Eq {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn eq(other: &~[T]) -> bool { eq(self, (*other)) }
|
pure fn eq(other: &~[T]) -> bool { eq(self, (*other)) }
|
||||||
|
@ -1335,6 +1370,7 @@ pure fn le<T: Ord>(a: &[T], b: &[T]) -> bool { !lt(b, a) }
|
||||||
pure fn ge<T: Ord>(a: &[T], b: &[T]) -> bool { !lt(a, b) }
|
pure fn ge<T: Ord>(a: &[T], b: &[T]) -> bool { !lt(a, b) }
|
||||||
pure fn gt<T: Ord>(a: &[T], b: &[T]) -> bool { lt(b, a) }
|
pure fn gt<T: Ord>(a: &[T], b: &[T]) -> bool { lt(b, a) }
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
impl<T: Ord> &[T] : Ord {
|
impl<T: Ord> &[T] : Ord {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn lt(other: & &[T]) -> bool { lt(self, (*other)) }
|
pure fn lt(other: & &[T]) -> bool { lt(self, (*other)) }
|
||||||
|
@ -1346,6 +1382,19 @@ impl<T: Ord> &[T] : Ord {
|
||||||
pure fn gt(other: & &[T]) -> bool { gt(self, (*other)) }
|
pure fn gt(other: & &[T]) -> bool { gt(self, (*other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl<T: Ord> &[T] : Ord {
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn lt(other: & &self/[T]) -> bool { lt(self, (*other)) }
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn le(other: & &self/[T]) -> bool { le(self, (*other)) }
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn ge(other: & &self/[T]) -> bool { ge(self, (*other)) }
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn gt(other: & &self/[T]) -> bool { gt(self, (*other)) }
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Ord> ~[T] : Ord {
|
impl<T: Ord> ~[T] : Ord {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn lt(other: &~[T]) -> bool { lt(self, (*other)) }
|
pure fn lt(other: &~[T]) -> bool { lt(self, (*other)) }
|
||||||
|
@ -1370,6 +1419,7 @@ impl<T: Ord> @[T] : Ord {
|
||||||
|
|
||||||
#[cfg(notest)]
|
#[cfg(notest)]
|
||||||
pub mod traits {
|
pub mod traits {
|
||||||
|
#[cfg(stage0)]
|
||||||
impl<T: Copy> ~[T] : Add<&[const T],~[T]> {
|
impl<T: Copy> ~[T] : Add<&[const T],~[T]> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn add(rhs: & &[const T]) -> ~[T] {
|
pure fn add(rhs: & &[const T]) -> ~[T] {
|
||||||
|
@ -1377,12 +1427,31 @@ pub mod traits {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl<T: Copy> ~[T] : Add<&[const T],~[T]> {
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn add(rhs: & &self/[const T]) -> ~[T] {
|
||||||
|
append(copy self, (*rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
impl<T: Copy> ~[mut T] : Add<&[const T],~[mut T]> {
|
impl<T: Copy> ~[mut T] : Add<&[const T],~[mut T]> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pure fn add(rhs: & &[const T]) -> ~[mut T] {
|
pure fn add(rhs: & &[const T]) -> ~[mut T] {
|
||||||
append_mut(copy self, (*rhs))
|
append_mut(copy self, (*rhs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage1)]
|
||||||
|
#[cfg(stage2)]
|
||||||
|
impl<T: Copy> ~[mut T] : Add<&[const T],~[mut T]> {
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn add(rhs: & &self/[const T]) -> ~[mut T] {
|
||||||
|
append_mut(copy self, (*rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1590,6 +1659,7 @@ pub trait MutableVector<T> {
|
||||||
fn unshift(&mut self, x: T);
|
fn unshift(&mut self, x: T);
|
||||||
fn swap_remove(&mut self, index: uint) -> T;
|
fn swap_remove(&mut self, index: uint) -> T;
|
||||||
fn truncate(&mut self, newlen: uint);
|
fn truncate(&mut self, newlen: uint);
|
||||||
|
fn retain(&mut self, f: pure fn(t: &T) -> bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MutableCopyableVector<T: Copy> {
|
pub trait MutableCopyableVector<T: Copy> {
|
||||||
|
@ -1631,6 +1701,10 @@ impl<T> ~[T]: MutableVector<T> {
|
||||||
fn truncate(&mut self, newlen: uint) {
|
fn truncate(&mut self, newlen: uint) {
|
||||||
truncate(self, newlen);
|
truncate(self, newlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn retain(&mut self, f: pure fn(t: &T) -> bool) {
|
||||||
|
retain(self, f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy> ~[T]: MutableCopyableVector<T> {
|
impl<T: Copy> ~[T]: MutableCopyableVector<T> {
|
||||||
|
|
|
@ -147,7 +147,7 @@ fn enc_region(w: io::Writer, cx: @ctxt, r: ty::Region) {
|
||||||
ty::re_static => {
|
ty::re_static => {
|
||||||
w.write_char('t');
|
w.write_char('t');
|
||||||
}
|
}
|
||||||
ty::re_var(_) => {
|
ty::re_infer(_) => {
|
||||||
// these should not crop up after typeck
|
// these should not crop up after typeck
|
||||||
cx.diag.handler().bug(~"Cannot encode region variables");
|
cx.diag.handler().bug(~"Cannot encode region variables");
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,7 +387,7 @@ impl ty::Region: tr {
|
||||||
ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
|
ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
|
||||||
ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)),
|
ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)),
|
||||||
ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
|
ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
|
||||||
ty::re_static | ty::re_var(*) => self,
|
ty::re_static | ty::re_infer(*) => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -580,7 +580,7 @@ fn check_cast_for_escaping_regions(
|
||||||
match target_substs.self_r {
|
match target_substs.self_r {
|
||||||
Some(ty::re_scope(*)) => { return; /* case (1) */ }
|
Some(ty::re_scope(*)) => { return; /* case (1) */ }
|
||||||
None | Some(ty::re_static) | Some(ty::re_free(*)) => {}
|
None | Some(ty::re_static) | Some(ty::re_free(*)) => {}
|
||||||
Some(ty::re_bound(*)) | Some(ty::re_var(*)) => {
|
Some(ty::re_bound(*)) | Some(ty::re_infer(*)) => {
|
||||||
cx.tcx.sess.span_bug(
|
cx.tcx.sess.span_bug(
|
||||||
source.span,
|
source.span,
|
||||||
fmt!("bad region found in kind: %?", target_substs.self_r));
|
fmt!("bad region found in kind: %?", target_substs.self_r));
|
||||||
|
|
|
@ -112,18 +112,18 @@ fn is_subregion_of(region_map: region_map,
|
||||||
super_region: ty::Region) -> bool {
|
super_region: ty::Region) -> bool {
|
||||||
sub_region == super_region ||
|
sub_region == super_region ||
|
||||||
match (sub_region, super_region) {
|
match (sub_region, super_region) {
|
||||||
(_, ty::re_static) => {
|
(_, ty::re_static) => {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::re_scope(sub_scope), ty::re_scope(super_scope)) |
|
(ty::re_scope(sub_scope), ty::re_scope(super_scope)) |
|
||||||
(ty::re_scope(sub_scope), ty::re_free(super_scope, _)) => {
|
(ty::re_scope(sub_scope), ty::re_free(super_scope, _)) => {
|
||||||
scope_contains(region_map, super_scope, sub_scope)
|
scope_contains(region_map, super_scope, sub_scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use syntax::ast_util::{is_local, local_def};
|
||||||
use syntax::codemap::span;
|
use syntax::codemap::span;
|
||||||
use metadata::csearch;
|
use metadata::csearch;
|
||||||
use util::ppaux::{region_to_str, explain_region, vstore_to_str,
|
use util::ppaux::{region_to_str, explain_region, vstore_to_str,
|
||||||
note_and_explain_region};
|
note_and_explain_region, bound_region_to_str};
|
||||||
use middle::lint;
|
use middle::lint;
|
||||||
use middle::lint::{get_lint_level, allow};
|
use middle::lint::{get_lint_level, allow};
|
||||||
use syntax::ast::*;
|
use syntax::ast::*;
|
||||||
|
@ -107,7 +107,8 @@ export InferTy, TyVar, IntVar;
|
||||||
export ty_self, mk_self, type_has_self;
|
export ty_self, mk_self, type_has_self;
|
||||||
export ty_class;
|
export ty_class;
|
||||||
export Region, bound_region, encl_region;
|
export Region, bound_region, encl_region;
|
||||||
export re_bound, re_free, re_scope, re_static, re_var;
|
export re_bound, re_free, re_scope, re_static, re_infer;
|
||||||
|
export ReVar, ReSkolemized;
|
||||||
export br_self, br_anon, br_named, br_cap_avoid;
|
export br_self, br_anon, br_named, br_cap_avoid;
|
||||||
export get, type_has_params, type_needs_infer, type_has_regions;
|
export get, type_has_params, type_needs_infer, type_has_regions;
|
||||||
export type_is_region_ptr;
|
export type_is_region_ptr;
|
||||||
|
@ -179,6 +180,8 @@ export terr_in_field, terr_record_fields, terr_vstores_differ, terr_arg_count;
|
||||||
export terr_sorts, terr_vec, terr_str, terr_record_size, terr_tuple_size;
|
export terr_sorts, terr_vec, terr_str, terr_record_size, terr_tuple_size;
|
||||||
export terr_regions_does_not_outlive, terr_mutability, terr_purity_mismatch;
|
export terr_regions_does_not_outlive, terr_mutability, terr_purity_mismatch;
|
||||||
export terr_regions_not_same, terr_regions_no_overlap;
|
export terr_regions_not_same, terr_regions_no_overlap;
|
||||||
|
export terr_regions_insufficiently_polymorphic;
|
||||||
|
export terr_regions_overly_polymorphic;
|
||||||
export terr_proto_mismatch;
|
export terr_proto_mismatch;
|
||||||
export terr_ret_style_mismatch;
|
export terr_ret_style_mismatch;
|
||||||
export terr_fn, terr_trait;
|
export terr_fn, terr_trait;
|
||||||
|
@ -555,7 +558,7 @@ enum Region {
|
||||||
re_static,
|
re_static,
|
||||||
|
|
||||||
/// A region variable. Should not exist after typeck.
|
/// A region variable. Should not exist after typeck.
|
||||||
re_var(RegionVid)
|
re_infer(InferRegion)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[auto_serialize]
|
#[auto_serialize]
|
||||||
|
@ -671,6 +674,8 @@ enum type_err {
|
||||||
terr_regions_does_not_outlive(Region, Region),
|
terr_regions_does_not_outlive(Region, Region),
|
||||||
terr_regions_not_same(Region, Region),
|
terr_regions_not_same(Region, Region),
|
||||||
terr_regions_no_overlap(Region, Region),
|
terr_regions_no_overlap(Region, Region),
|
||||||
|
terr_regions_insufficiently_polymorphic(bound_region, Region),
|
||||||
|
terr_regions_overly_polymorphic(bound_region, Region),
|
||||||
terr_vstores_differ(terr_vstore_kind, expected_found<vstore>),
|
terr_vstores_differ(terr_vstore_kind, expected_found<vstore>),
|
||||||
terr_in_field(@type_err, ast::ident),
|
terr_in_field(@type_err, ast::ident),
|
||||||
terr_sorts(expected_found<t>),
|
terr_sorts(expected_found<t>),
|
||||||
|
@ -707,6 +712,39 @@ impl InferTy : to_bytes::IterBytes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[auto_serialize]
|
||||||
|
#[auto_deserialize]
|
||||||
|
enum InferRegion {
|
||||||
|
ReVar(RegionVid),
|
||||||
|
ReSkolemized(uint, bound_region)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InferRegion : to_bytes::IterBytes {
|
||||||
|
pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
|
||||||
|
match self {
|
||||||
|
ReVar(ref rv) => to_bytes::iter_bytes_2(&0u8, rv, lsb0, f),
|
||||||
|
ReSkolemized(ref v, _) => to_bytes::iter_bytes_2(&1u8, v, lsb0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InferRegion : cmp::Eq {
|
||||||
|
pure fn eq(other: &InferRegion) -> bool {
|
||||||
|
match (self, *other) {
|
||||||
|
(ReVar(rva), ReVar(rvb)) => {
|
||||||
|
rva == rvb
|
||||||
|
}
|
||||||
|
(ReSkolemized(rva, _), ReSkolemized(rvb, _)) => {
|
||||||
|
rva == rvb
|
||||||
|
}
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pure fn ne(other: &InferRegion) -> bool {
|
||||||
|
!(self == (*other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl param_bound : to_bytes::IterBytes {
|
impl param_bound : to_bytes::IterBytes {
|
||||||
pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
|
pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -923,7 +961,7 @@ fn mk_t_with_id(cx: ctxt, +st: sty, o_def_id: Option<ast::def_id>) -> t {
|
||||||
fn rflags(r: Region) -> uint {
|
fn rflags(r: Region) -> uint {
|
||||||
(has_regions as uint) | {
|
(has_regions as uint) | {
|
||||||
match r {
|
match r {
|
||||||
ty::re_var(_) => needs_infer as uint,
|
ty::re_infer(_) => needs_infer as uint,
|
||||||
_ => 0u
|
_ => 0u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2591,7 +2629,7 @@ impl Region : to_bytes::IterBytes {
|
||||||
re_scope(ref id) =>
|
re_scope(ref id) =>
|
||||||
to_bytes::iter_bytes_2(&2u8, id, lsb0, f),
|
to_bytes::iter_bytes_2(&2u8, id, lsb0, f),
|
||||||
|
|
||||||
re_var(ref id) =>
|
re_infer(ref id) =>
|
||||||
to_bytes::iter_bytes_2(&3u8, id, lsb0, f),
|
to_bytes::iter_bytes_2(&3u8, id, lsb0, f),
|
||||||
|
|
||||||
re_static => 4u8.iter_bytes(lsb0, f)
|
re_static => 4u8.iter_bytes(lsb0, f)
|
||||||
|
@ -3253,92 +3291,103 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
|
||||||
}
|
}
|
||||||
|
|
||||||
match *err {
|
match *err {
|
||||||
terr_mismatch => ~"types differ",
|
terr_mismatch => ~"types differ",
|
||||||
terr_ret_style_mismatch(values) => {
|
terr_ret_style_mismatch(values) => {
|
||||||
fn to_str(s: ast::ret_style) -> ~str {
|
fn to_str(s: ast::ret_style) -> ~str {
|
||||||
match s {
|
match s {
|
||||||
ast::noreturn => ~"non-returning",
|
ast::noreturn => ~"non-returning",
|
||||||
ast::return_val => ~"return-by-value"
|
ast::return_val => ~"return-by-value"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fmt!("expected %s function, found %s function",
|
||||||
|
to_str(values.expected),
|
||||||
|
to_str(values.expected))
|
||||||
|
}
|
||||||
|
terr_purity_mismatch(values) => {
|
||||||
|
fmt!("expected %s fn but found %s fn",
|
||||||
|
purity_to_str(values.expected),
|
||||||
|
purity_to_str(values.found))
|
||||||
|
}
|
||||||
|
terr_proto_mismatch(values) => {
|
||||||
|
fmt!("expected %s closure, found %s closure",
|
||||||
|
proto_ty_to_str(cx, values.expected),
|
||||||
|
proto_ty_to_str(cx, values.found))
|
||||||
|
}
|
||||||
|
terr_mutability => ~"values differ in mutability",
|
||||||
|
terr_box_mutability => ~"boxed values differ in mutability",
|
||||||
|
terr_vec_mutability => ~"vectors differ in mutability",
|
||||||
|
terr_ptr_mutability => ~"pointers differ in mutability",
|
||||||
|
terr_ref_mutability => ~"references differ in mutability",
|
||||||
|
terr_ty_param_size(values) => {
|
||||||
|
fmt!("expected a type with %? type params \
|
||||||
|
but found one with %? type params",
|
||||||
|
values.expected, values.found)
|
||||||
|
}
|
||||||
|
terr_tuple_size(values) => {
|
||||||
|
fmt!("expected a tuple with %? elements \
|
||||||
|
but found one with %? elements",
|
||||||
|
values.expected, values.found)
|
||||||
|
}
|
||||||
|
terr_record_size(values) => {
|
||||||
|
fmt!("expected a record with %? fields \
|
||||||
|
but found one with %? fields",
|
||||||
|
values.expected, values.found)
|
||||||
|
}
|
||||||
|
terr_record_mutability => {
|
||||||
|
~"record elements differ in mutability"
|
||||||
|
}
|
||||||
|
terr_record_fields(values) => {
|
||||||
|
fmt!("expected a record with field `%s` but found one with field \
|
||||||
|
`%s`",
|
||||||
|
cx.sess.str_of(values.expected),
|
||||||
|
cx.sess.str_of(values.found))
|
||||||
|
}
|
||||||
|
terr_arg_count => ~"incorrect number of function parameters",
|
||||||
|
terr_mode_mismatch(values) => {
|
||||||
|
fmt!("expected argument mode %s, but found %s",
|
||||||
|
mode_to_str(values.expected), mode_to_str(values.found))
|
||||||
|
}
|
||||||
|
terr_regions_does_not_outlive(*) => {
|
||||||
|
fmt!("lifetime mismatch")
|
||||||
|
}
|
||||||
|
terr_regions_not_same(*) => {
|
||||||
|
fmt!("lifetimes are not the same")
|
||||||
|
}
|
||||||
|
terr_regions_no_overlap(*) => {
|
||||||
|
fmt!("lifetimes do not intersect")
|
||||||
|
}
|
||||||
|
terr_regions_insufficiently_polymorphic(br, _) => {
|
||||||
|
fmt!("expected bound lifetime parameter %s, \
|
||||||
|
but found concrete lifetime",
|
||||||
|
bound_region_to_str(cx, br))
|
||||||
|
}
|
||||||
|
terr_regions_overly_polymorphic(br, _) => {
|
||||||
|
fmt!("expected concrete lifetime, \
|
||||||
|
but found bound lifetime parameter %s",
|
||||||
|
bound_region_to_str(cx, br))
|
||||||
|
}
|
||||||
|
terr_vstores_differ(k, values) => {
|
||||||
|
fmt!("%s storage differs: expected %s but found %s",
|
||||||
|
terr_vstore_kind_to_str(k),
|
||||||
|
vstore_to_str(cx, values.expected),
|
||||||
|
vstore_to_str(cx, values.found))
|
||||||
|
}
|
||||||
|
terr_in_field(err, fname) => {
|
||||||
|
fmt!("in field `%s`, %s", cx.sess.str_of(fname),
|
||||||
|
type_err_to_str(cx, err))
|
||||||
|
}
|
||||||
|
terr_sorts(values) => {
|
||||||
|
fmt!("expected %s but found %s",
|
||||||
|
ty_sort_str(cx, values.expected),
|
||||||
|
ty_sort_str(cx, values.found))
|
||||||
|
}
|
||||||
|
terr_self_substs => {
|
||||||
|
~"inconsistent self substitution" // XXX this is more of a bug
|
||||||
|
}
|
||||||
|
terr_no_integral_type => {
|
||||||
|
~"couldn't determine an appropriate integral type for integer \
|
||||||
|
literal"
|
||||||
}
|
}
|
||||||
fmt!("expected %s function, found %s function",
|
|
||||||
to_str(values.expected),
|
|
||||||
to_str(values.expected))
|
|
||||||
}
|
|
||||||
terr_purity_mismatch(values) => {
|
|
||||||
fmt!("expected %s fn but found %s fn",
|
|
||||||
purity_to_str(values.expected),
|
|
||||||
purity_to_str(values.found))
|
|
||||||
}
|
|
||||||
terr_proto_mismatch(values) => {
|
|
||||||
fmt!("expected %s closure, found %s closure",
|
|
||||||
proto_ty_to_str(cx, values.expected),
|
|
||||||
proto_ty_to_str(cx, values.found))
|
|
||||||
}
|
|
||||||
terr_mutability => ~"values differ in mutability",
|
|
||||||
terr_box_mutability => ~"boxed values differ in mutability",
|
|
||||||
terr_vec_mutability => ~"vectors differ in mutability",
|
|
||||||
terr_ptr_mutability => ~"pointers differ in mutability",
|
|
||||||
terr_ref_mutability => ~"references differ in mutability",
|
|
||||||
terr_ty_param_size(values) => {
|
|
||||||
fmt!("expected a type with %? type params \
|
|
||||||
but found one with %? type params",
|
|
||||||
values.expected, values.found)
|
|
||||||
}
|
|
||||||
terr_tuple_size(values) => {
|
|
||||||
fmt!("expected a tuple with %? elements \
|
|
||||||
but found one with %? elements",
|
|
||||||
values.expected, values.found)
|
|
||||||
}
|
|
||||||
terr_record_size(values) => {
|
|
||||||
fmt!("expected a record with %? fields \
|
|
||||||
but found one with %? fields",
|
|
||||||
values.expected, values.found)
|
|
||||||
}
|
|
||||||
terr_record_mutability => {
|
|
||||||
~"record elements differ in mutability"
|
|
||||||
}
|
|
||||||
terr_record_fields(values) => {
|
|
||||||
fmt!("expected a record with field `%s` but found one with field \
|
|
||||||
`%s`",
|
|
||||||
cx.sess.str_of(values.expected), cx.sess.str_of(values.found))
|
|
||||||
}
|
|
||||||
terr_arg_count => ~"incorrect number of function parameters",
|
|
||||||
terr_mode_mismatch(values) => {
|
|
||||||
fmt!("expected argument mode %s, but found %s",
|
|
||||||
mode_to_str(values.expected), mode_to_str(values.found))
|
|
||||||
}
|
|
||||||
terr_regions_does_not_outlive(*) => {
|
|
||||||
fmt!("lifetime mismatch")
|
|
||||||
}
|
|
||||||
terr_regions_not_same(*) => {
|
|
||||||
fmt!("lifetimes are not the same")
|
|
||||||
}
|
|
||||||
terr_regions_no_overlap(*) => {
|
|
||||||
fmt!("lifetimes do not intersect")
|
|
||||||
}
|
|
||||||
terr_vstores_differ(k, values) => {
|
|
||||||
fmt!("%s storage differs: expected %s but found %s",
|
|
||||||
terr_vstore_kind_to_str(k),
|
|
||||||
vstore_to_str(cx, values.expected),
|
|
||||||
vstore_to_str(cx, values.found))
|
|
||||||
}
|
|
||||||
terr_in_field(err, fname) => {
|
|
||||||
fmt!("in field `%s`, %s", cx.sess.str_of(fname),
|
|
||||||
type_err_to_str(cx, err))
|
|
||||||
}
|
|
||||||
terr_sorts(values) => {
|
|
||||||
fmt!("expected %s but found %s",
|
|
||||||
ty_sort_str(cx, values.expected),
|
|
||||||
ty_sort_str(cx, values.found))
|
|
||||||
}
|
|
||||||
terr_self_substs => {
|
|
||||||
~"inconsistent self substitution" // XXX this is more of a bug
|
|
||||||
}
|
|
||||||
terr_no_integral_type => {
|
|
||||||
~"couldn't determine an appropriate integral type for integer \
|
|
||||||
literal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3359,6 +3408,16 @@ fn note_and_explain_type_err(cx: ctxt, err: &type_err) {
|
||||||
note_and_explain_region(cx, ~"...does not overlap ",
|
note_and_explain_region(cx, ~"...does not overlap ",
|
||||||
region2, ~"");
|
region2, ~"");
|
||||||
}
|
}
|
||||||
|
terr_regions_insufficiently_polymorphic(_, conc_region) => {
|
||||||
|
note_and_explain_region(cx,
|
||||||
|
~"concrete lifetime that was found is ",
|
||||||
|
conc_region, ~"");
|
||||||
|
}
|
||||||
|
terr_regions_overly_polymorphic(_, conc_region) => {
|
||||||
|
note_and_explain_region(cx,
|
||||||
|
~"expected concrete lifetime is ",
|
||||||
|
conc_region, ~"");
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4182,9 +4241,9 @@ impl Region : cmp::Eq {
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
re_var(e0a) => {
|
re_infer(e0a) => {
|
||||||
match (*other) {
|
match (*other) {
|
||||||
re_var(e0b) => e0a == e0b,
|
re_infer(e0b) => e0a == e0b,
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ fn replace_bound_regions_in_fn_ty(
|
||||||
r: ty::Region) -> isr_alist {
|
r: ty::Region) -> isr_alist {
|
||||||
match r {
|
match r {
|
||||||
ty::re_free(_, _) | ty::re_static | ty::re_scope(_) |
|
ty::re_free(_, _) | ty::re_static | ty::re_scope(_) |
|
||||||
ty::re_var(_) => {
|
ty::re_infer(_) => {
|
||||||
isr
|
isr
|
||||||
}
|
}
|
||||||
ty::re_bound(br) => {
|
ty::re_bound(br) => {
|
||||||
|
@ -165,7 +165,7 @@ fn replace_bound_regions_in_fn_ty(
|
||||||
ty::re_static |
|
ty::re_static |
|
||||||
ty::re_scope(_) |
|
ty::re_scope(_) |
|
||||||
ty::re_free(_, _) |
|
ty::re_free(_, _) |
|
||||||
ty::re_var(_) => r
|
ty::re_infer(_) => r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,46 +272,60 @@ fn ensure_supertraits(ccx: @crate_ctxt,
|
||||||
*
|
*
|
||||||
* - impl_m: the method in the impl
|
* - impl_m: the method in the impl
|
||||||
* - impl_tps: the type params declared on the impl itself (not the method!)
|
* - impl_tps: the type params declared on the impl itself (not the method!)
|
||||||
|
* - impl_body_id: the id of the method body from the impl
|
||||||
* - trait_m: the method in the trait
|
* - trait_m: the method in the trait
|
||||||
* - trait_substs: the substitutions used on the type of the trait
|
* - trait_substs: the substitutions used on the type of the trait
|
||||||
* - self_ty: the self type of the impl
|
* - self_ty: the self type of the impl
|
||||||
*/
|
*/
|
||||||
fn compare_impl_method(tcx: ty::ctxt, sp: span,
|
fn compare_impl_method(tcx: ty::ctxt,
|
||||||
impl_m: ty::method, impl_tps: uint,
|
impl_tps: uint,
|
||||||
trait_m: ty::method, trait_substs: ty::substs,
|
cm: &ConvertedMethod,
|
||||||
self_ty: ty::t) {
|
trait_m: &ty::method,
|
||||||
|
trait_substs: &ty::substs,
|
||||||
|
self_ty: ty::t)
|
||||||
|
{
|
||||||
|
debug!("compare_impl_method()");
|
||||||
|
let _indenter = indenter();
|
||||||
|
|
||||||
|
let impl_m = &cm.mty;
|
||||||
|
|
||||||
if impl_m.fty.meta.purity != trait_m.fty.meta.purity {
|
if impl_m.fty.meta.purity != trait_m.fty.meta.purity {
|
||||||
tcx.sess.span_err(
|
tcx.sess.span_err(
|
||||||
sp, fmt!("method `%s`'s purity does \
|
cm.span,
|
||||||
not match the trait method's \
|
fmt!("method `%s`'s purity does \
|
||||||
purity", tcx.sess.str_of(impl_m.ident)));
|
not match the trait method's \
|
||||||
|
purity", tcx.sess.str_of(impl_m.ident)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// is this check right?
|
// is this check right?
|
||||||
if impl_m.self_ty != trait_m.self_ty {
|
if impl_m.self_ty != trait_m.self_ty {
|
||||||
tcx.sess.span_err(
|
tcx.sess.span_err(
|
||||||
sp, fmt!("method `%s`'s self type does \
|
cm.span,
|
||||||
not match the trait method's \
|
fmt!("method `%s`'s self type does \
|
||||||
self type", tcx.sess.str_of(impl_m.ident)));
|
not match the trait method's \
|
||||||
|
self type", tcx.sess.str_of(impl_m.ident)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if impl_m.tps.len() != trait_m.tps.len() {
|
if impl_m.tps.len() != trait_m.tps.len() {
|
||||||
tcx.sess.span_err(sp, fmt!("method `%s` \
|
tcx.sess.span_err(
|
||||||
has %u type %s, but its trait declaration has %u type %s",
|
cm.span,
|
||||||
tcx.sess.str_of(trait_m.ident), impl_m.tps.len(),
|
fmt!("method `%s` has %u type %s, but its trait \
|
||||||
pluralize(impl_m.tps.len(), ~"parameter"),
|
declaration has %u type %s",
|
||||||
trait_m.tps.len(),
|
tcx.sess.str_of(trait_m.ident), impl_m.tps.len(),
|
||||||
pluralize(trait_m.tps.len(), ~"parameter")));
|
pluralize(impl_m.tps.len(), ~"parameter"),
|
||||||
|
trait_m.tps.len(),
|
||||||
|
pluralize(trait_m.tps.len(), ~"parameter")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if vec::len(impl_m.fty.sig.inputs) != vec::len(trait_m.fty.sig.inputs) {
|
if vec::len(impl_m.fty.sig.inputs) != vec::len(trait_m.fty.sig.inputs) {
|
||||||
tcx.sess.span_err(sp,fmt!("method `%s` has %u parameters \
|
tcx.sess.span_err(
|
||||||
but the trait has %u",
|
cm.span,
|
||||||
tcx.sess.str_of(trait_m.ident),
|
fmt!("method `%s` has %u parameters \
|
||||||
vec::len(impl_m.fty.sig.inputs),
|
but the trait has %u",
|
||||||
vec::len(trait_m.fty.sig.inputs)));
|
tcx.sess.str_of(trait_m.ident),
|
||||||
|
vec::len(impl_m.fty.sig.inputs),
|
||||||
|
vec::len(trait_m.fty.sig.inputs)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,14 +336,16 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
|
||||||
// Would be nice to use the ty param names in the error message,
|
// Would be nice to use the ty param names in the error message,
|
||||||
// but we don't have easy access to them here
|
// but we don't have easy access to them here
|
||||||
if impl_param_bounds.len() != trait_param_bounds.len() {
|
if impl_param_bounds.len() != trait_param_bounds.len() {
|
||||||
tcx.sess.span_err(sp, fmt!("in method `%s`, \
|
tcx.sess.span_err(
|
||||||
type parameter %u has %u %s, but the same type \
|
cm.span,
|
||||||
parameter in its trait declaration has %u %s",
|
fmt!("in method `%s`, \
|
||||||
tcx.sess.str_of(trait_m.ident),
|
type parameter %u has %u %s, but the same type \
|
||||||
i, impl_param_bounds.len(),
|
parameter in its trait declaration has %u %s",
|
||||||
pluralize(impl_param_bounds.len(), ~"bound"),
|
tcx.sess.str_of(trait_m.ident),
|
||||||
trait_param_bounds.len(),
|
i, impl_param_bounds.len(),
|
||||||
pluralize(trait_param_bounds.len(), ~"bound")));
|
pluralize(impl_param_bounds.len(), ~"bound"),
|
||||||
|
trait_param_bounds.len(),
|
||||||
|
pluralize(trait_param_bounds.len(), ~"bound")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// tjc: I'm mildly worried that there's something I'm
|
// tjc: I'm mildly worried that there's something I'm
|
||||||
|
@ -337,35 +353,51 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
|
||||||
// but I can't figure out what.
|
// but I can't figure out what.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace any references to the self region in the self type with
|
||||||
|
// a free region. So, for example, if the impl type is
|
||||||
|
// "&self/str", then this would replace the self type with a free
|
||||||
|
// region `self`.
|
||||||
|
//
|
||||||
|
// Note: Ideal would be to use the node-id of the method body here,
|
||||||
|
// not the node id of the method itself.
|
||||||
|
let dummy_self_r = ty::re_free(cm.body_id, ty::br_self);
|
||||||
|
let self_ty = replace_bound_self(tcx, self_ty, dummy_self_r);
|
||||||
|
|
||||||
// Perform substitutions so that the trait/impl methods are expressed
|
// Perform substitutions so that the trait/impl methods are expressed
|
||||||
// in terms of the same set of type/region parameters:
|
// in terms of the same set of type/region parameters:
|
||||||
// - replace trait type parameters with those from `trait_substs`
|
// - replace trait type parameters with those from `trait_substs`,
|
||||||
|
// except with any reference to bound self replaced with `dummy_self_r`
|
||||||
// - replace method parameters on the trait with fresh, dummy parameters
|
// - replace method parameters on the trait with fresh, dummy parameters
|
||||||
// that correspond to the parameters we will find on the impl
|
// that correspond to the parameters we will find on the impl
|
||||||
// - replace self region with a fresh, dummy region
|
// - replace self region with a fresh, dummy region
|
||||||
let dummy_self_r = ty::re_free(0, ty::br_self);
|
|
||||||
let impl_fty = {
|
let impl_fty = {
|
||||||
let impl_fty = ty::mk_fn(tcx, impl_m.fty);
|
let impl_fty = ty::mk_fn(tcx, impl_m.fty);
|
||||||
|
debug!("impl_fty (pre-subst): %s", ty_to_str(tcx, impl_fty));
|
||||||
replace_bound_self(tcx, impl_fty, dummy_self_r)
|
replace_bound_self(tcx, impl_fty, dummy_self_r)
|
||||||
};
|
};
|
||||||
|
debug!("impl_fty: %s", ty_to_str(tcx, impl_fty));
|
||||||
let trait_fty = {
|
let trait_fty = {
|
||||||
let dummy_tps = do vec::from_fn((*trait_m.tps).len()) |i| {
|
let dummy_tps = do vec::from_fn((*trait_m.tps).len()) |i| {
|
||||||
// hack: we don't know the def id of the impl tp, but it
|
// hack: we don't know the def id of the impl tp, but it
|
||||||
// is not important for unification
|
// is not important for unification
|
||||||
ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0})
|
ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0})
|
||||||
};
|
};
|
||||||
|
let trait_tps = trait_substs.tps.map(
|
||||||
|
|t| replace_bound_self(tcx, *t, dummy_self_r));
|
||||||
let substs = {
|
let substs = {
|
||||||
self_r: Some(dummy_self_r),
|
self_r: Some(dummy_self_r),
|
||||||
self_ty: Some(self_ty),
|
self_ty: Some(self_ty),
|
||||||
tps: vec::append(trait_substs.tps, dummy_tps)
|
tps: vec::append(trait_tps, dummy_tps)
|
||||||
};
|
};
|
||||||
let trait_fty = ty::mk_fn(tcx, trait_m.fty);
|
let trait_fty = ty::mk_fn(tcx, trait_m.fty);
|
||||||
|
debug!("trait_fty (pre-subst): %s", ty_to_str(tcx, trait_fty));
|
||||||
ty::subst(tcx, &substs, trait_fty)
|
ty::subst(tcx, &substs, trait_fty)
|
||||||
};
|
};
|
||||||
|
debug!("trait_fty: %s", ty_to_str(tcx, trait_fty));
|
||||||
require_same_types(
|
require_same_types(
|
||||||
tcx, None, false, sp, impl_fty, trait_fty,
|
tcx, None, false, cm.span, impl_fty, trait_fty,
|
||||||
|| ~"method `" + tcx.sess.str_of(trait_m.ident)
|
|| fmt!("method `%s` has an incompatible type",
|
||||||
+ ~"` has an incompatible type");
|
tcx.sess.str_of(trait_m.ident)));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Replaces bound references to the self region with `with_r`.
|
// Replaces bound references to the self region with `with_r`.
|
||||||
|
@ -382,7 +414,7 @@ fn check_methods_against_trait(ccx: @crate_ctxt,
|
||||||
rp: Option<ty::region_variance>,
|
rp: Option<ty::region_variance>,
|
||||||
selfty: ty::t,
|
selfty: ty::t,
|
||||||
a_trait_ty: @ast::trait_ref,
|
a_trait_ty: @ast::trait_ref,
|
||||||
impl_ms: ~[converted_method]) {
|
impl_ms: ~[ConvertedMethod]) {
|
||||||
|
|
||||||
let tcx = ccx.tcx;
|
let tcx = ccx.tcx;
|
||||||
let (did, tpt) = instantiate_trait_ref(ccx, a_trait_ty, rp);
|
let (did, tpt) = instantiate_trait_ref(ccx, a_trait_ty, rp);
|
||||||
|
@ -391,32 +423,32 @@ fn check_methods_against_trait(ccx: @crate_ctxt,
|
||||||
}
|
}
|
||||||
for vec::each(*ty::trait_methods(tcx, did)) |trait_m| {
|
for vec::each(*ty::trait_methods(tcx, did)) |trait_m| {
|
||||||
match vec::find(impl_ms, |impl_m| trait_m.ident == impl_m.mty.ident) {
|
match vec::find(impl_ms, |impl_m| trait_m.ident == impl_m.mty.ident) {
|
||||||
Some({mty: impl_m, span, _}) => {
|
Some(ref cm) => {
|
||||||
compare_impl_method(
|
compare_impl_method(
|
||||||
ccx.tcx, span, impl_m, vec::len(tps),
|
ccx.tcx, vec::len(tps), cm, trait_m,
|
||||||
*trait_m, tpt.substs, selfty);
|
&tpt.substs, selfty);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// If we couldn't find an implementation for trait_m in
|
// If we couldn't find an implementation for trait_m in
|
||||||
// the impl, then see if there was a default
|
// the impl, then see if there was a default
|
||||||
// implementation in the trait itself. If not, raise a
|
// implementation in the trait itself. If not, raise a
|
||||||
// "missing method" error.
|
// "missing method" error.
|
||||||
|
|
||||||
let provided_methods = ty::provided_trait_methods(tcx, did);
|
let provided_methods = ty::provided_trait_methods(tcx, did);
|
||||||
match vec::find(provided_methods, |provided_method|
|
match vec::find(provided_methods, |provided_method|
|
||||||
*provided_method == trait_m.ident) {
|
*provided_method == trait_m.ident) {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
// If there's a provided method with the name we
|
// If there's a provided method with the name we
|
||||||
// want, then we're fine; nothing else to do.
|
// want, then we're fine; nothing else to do.
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tcx.sess.span_err(
|
||||||
|
a_trait_ty.path.span,
|
||||||
|
fmt!("missing method `%s`",
|
||||||
|
tcx.sess.str_of(trait_m.ident)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
}
|
||||||
tcx.sess.span_err(
|
|
||||||
a_trait_ty.path.span,
|
|
||||||
fmt!("missing method `%s`",
|
|
||||||
tcx.sess.str_of(trait_m.ident)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // match
|
} // match
|
||||||
} // |trait_m|
|
} // |trait_m|
|
||||||
} // fn
|
} // fn
|
||||||
|
@ -434,12 +466,17 @@ fn convert_field(ccx: @crate_ctxt,
|
||||||
ty: tt});
|
ty: tt});
|
||||||
}
|
}
|
||||||
|
|
||||||
type converted_method = {mty: ty::method, id: ast::node_id, span: span};
|
struct ConvertedMethod {
|
||||||
|
mty: ty::method,
|
||||||
|
id: ast::node_id,
|
||||||
|
span: span,
|
||||||
|
body_id: ast::node_id
|
||||||
|
}
|
||||||
|
|
||||||
fn convert_methods(ccx: @crate_ctxt,
|
fn convert_methods(ccx: @crate_ctxt,
|
||||||
ms: ~[@ast::method],
|
ms: ~[@ast::method],
|
||||||
rp: Option<ty::region_variance>,
|
rp: Option<ty::region_variance>,
|
||||||
rcvr_bounds: @~[ty::param_bounds]) -> ~[converted_method] {
|
rcvr_bounds: @~[ty::param_bounds]) -> ~[ConvertedMethod] {
|
||||||
|
|
||||||
let tcx = ccx.tcx;
|
let tcx = ccx.tcx;
|
||||||
do vec::map(ms) |m| {
|
do vec::map(ms) |m| {
|
||||||
|
@ -455,7 +492,8 @@ fn convert_methods(ccx: @crate_ctxt,
|
||||||
region_param: rp,
|
region_param: rp,
|
||||||
ty: fty});
|
ty: fty});
|
||||||
write_ty_to_tcx(tcx, m.id, fty);
|
write_ty_to_tcx(tcx, m.id, fty);
|
||||||
{mty: mty, id: m.id, span: m.span}
|
ConvertedMethod {mty: mty, id: m.id,
|
||||||
|
span: m.span, body_id: m.body.node.id}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -262,7 +262,7 @@ use util::common::{indent, indenter};
|
||||||
use ast::{unsafe_fn, impure_fn, pure_fn, extern_fn};
|
use ast::{unsafe_fn, impure_fn, pure_fn, extern_fn};
|
||||||
use ast::{m_const, m_imm, m_mutbl};
|
use ast::{m_const, m_imm, m_mutbl};
|
||||||
use dvec::DVec;
|
use dvec::DVec;
|
||||||
use region_var_bindings::{RegionVarBindings};
|
use region_inference::{RegionVarBindings};
|
||||||
use ast_util::dummy_sp;
|
use ast_util::dummy_sp;
|
||||||
use cmp::Eq;
|
use cmp::Eq;
|
||||||
|
|
||||||
|
@ -628,7 +628,7 @@ impl infer_ctxt {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_region_var_nb(span: span) -> ty::Region {
|
fn next_region_var_nb(span: span) -> ty::Region {
|
||||||
ty::re_var(self.region_vars.new_region_var(span))
|
ty::re_infer(ty::ReVar(self.region_vars.new_region_var(span)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_region_var_with_lb(span: span,
|
fn next_region_var_with_lb(span: span,
|
||||||
|
|
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/
|
http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
|
||||||
|
|
||||||
NOTE--for the most part, we do not yet handle these cases correctly!
|
Although my explanation will never compete with SPJ's (for one thing,
|
||||||
|
his is approximately 100 pages), I will attempt to explain the basic
|
||||||
|
problem and also how we solve it. Note that the paper only discusses
|
||||||
|
subtyping, not the computation of LUB/GLB.
|
||||||
|
|
||||||
## Subtyping and bound regions
|
The problem we are addressing is that there is a kind of subtyping
|
||||||
|
between functions with bound region parameters. Consider, for
|
||||||
|
example, whether the following relation holds:
|
||||||
|
|
||||||
### Simple examples
|
fn(&a/int) <: fn(&b/int)? (Yes, a => b)
|
||||||
|
|
||||||
The situation is well-summarized by these examples (here I am omitting
|
The answer is that of course it does. These two types are basically
|
||||||
the types as they are not interesting, and I am writing binding
|
the same, except that in one we used the name `a` and one we used
|
||||||
explicitly):
|
the name `b`.
|
||||||
|
|
||||||
1. fn<a>(&a/T) <: fn<b>(&b/T)? Yes: a -> b
|
In the examples that follow, it becomes very important to know whether
|
||||||
2. fn<a>(&a/T) <: fn(&b/T)? Yes: a -> b
|
a lifetime is bound in a function type (that is, is a lifetime
|
||||||
3. fn(&a/T) <: fn<b>(&b/T)? No!
|
parameter) or appears free (is defined in some outer scope).
|
||||||
4. fn(&a/T) <: fn(&b/T)? No!
|
Therefore, from now on I will write the bindings explicitly, using a
|
||||||
5. fn(&a/T) <: fn(&a)? Yes!
|
notation like `fn<a>(&a/int)` to indicate that `a` is a lifetime
|
||||||
|
parameter.
|
||||||
|
|
||||||
In case one, the two function types are equivalent because both
|
Now let's consider two more function types. Here, we assume that the
|
||||||
reference a bound region, just with different names.
|
`self` lifetime is defined somewhere outside and hence is not a
|
||||||
|
lifetime parameter bound by the function type (it "appears free"):
|
||||||
|
|
||||||
In case two, the subtyping relationship is valid because the subtyping
|
fn<a>(&a/int) <: fn(&self/int)? (Yes, a => self)
|
||||||
function accepts a pointer in *any* region, whereas the supertype
|
|
||||||
function accepts a pointer *only in the region `b`*. Therefore, it is
|
|
||||||
safe to use the subtype wherever the supertype is expected, as the
|
|
||||||
supertype can only be passed pointers in region `b`, and the subtype
|
|
||||||
can handle `b` (but also others).
|
|
||||||
|
|
||||||
Case three is the opposite: here the subtype requires the region `a`,
|
This subtyping relation does in fact hold. To see why, you have to
|
||||||
but the supertype must accept pointers in any region. That means that
|
consider what subtyping means. One way to look at `T1 <: T2` is to
|
||||||
it is not safe to use the subtype where the supertype is expected: the
|
say that it means that it is always ok to treat an instance of `T1` as
|
||||||
supertype can be passed pointers in any region, but the subtype can
|
if it had the type `T2`. So, with our functions, it is always ok to
|
||||||
only handle pointers in the region `a`.
|
treat a function that can take pointers with any lifetime as if it
|
||||||
|
were a function that can only take a pointer with the specific
|
||||||
|
lifetime `&self`. After all, `&self` is a lifetime, after all, and
|
||||||
|
the function can take values of any lifetime.
|
||||||
|
|
||||||
Case four is fairly simple. The subtype expects region `a` but the supertype
|
You can also look at subtyping as the *is a* relationship. This amounts
|
||||||
expects region `b`. These two regions are not the same. Therefore, not
|
to the same thing: a function that accepts pointers with any lifetime
|
||||||
a subtype.
|
*is a* function that accepts pointers with some specific lifetime.
|
||||||
|
|
||||||
Case five is similar to four, except that the subtype and supertype
|
So, what if we reverse the order of the two function types, like this:
|
||||||
expect the same region, so in fact they are the same type. That's
|
|
||||||
fine.
|
fn(&self/int) <: fn<a>(&a/int)? (No)
|
||||||
|
|
||||||
|
Does the subtyping relationship still hold? The answer of course is
|
||||||
|
no. In this case, the function accepts *only the lifetime `&self`*,
|
||||||
|
so it is not reasonable to treat it as if it were a function that
|
||||||
|
accepted any lifetime.
|
||||||
|
|
||||||
|
What about these two examples:
|
||||||
|
|
||||||
|
fn<a,b>(&a/int, &b/int) <: fn<a>(&a/int, &a/int)? (Yes)
|
||||||
|
fn<a>(&a/int, &a/int) <: fn<a,b>(&a/int, &b/int)? (No)
|
||||||
|
|
||||||
|
Here, it is true that functions which take two pointers with any two
|
||||||
|
lifetimes can be treated as if they only accepted two pointers with
|
||||||
|
the same lifetime, but not the reverse.
|
||||||
|
|
||||||
|
## The algorithm
|
||||||
|
|
||||||
Here is the algorithm we use to perform the subtyping check:
|
Here is the algorithm we use to perform the subtyping check:
|
||||||
|
|
||||||
|
@ -184,15 +205,13 @@ Here is the algorithm we use to perform the subtyping check:
|
||||||
4. Ensure that no skolemized regions 'leak' into region variables
|
4. Ensure that no skolemized regions 'leak' into region variables
|
||||||
visible from "the outside"
|
visible from "the outside"
|
||||||
|
|
||||||
I'll walk briefly through how this works with the examples above.
|
Let's walk through some examples and see how this algorithm plays out.
|
||||||
I'll ignore the last step for now, it'll come up in the complex
|
|
||||||
examples below.
|
|
||||||
|
|
||||||
#### First example
|
#### First example
|
||||||
|
|
||||||
Let's look first at the first example, which was:
|
We'll start with the first example, which was:
|
||||||
|
|
||||||
1. fn<a>(&a/T) <: fn<b>(&b/T/T)? Yes: a -> x
|
1. fn<a>(&a/T) <: fn<b>(&b/T)? Yes: a -> b
|
||||||
|
|
||||||
After steps 1 and 2 of the algorithm we will have replaced the types
|
After steps 1 and 2 of the algorithm we will have replaced the types
|
||||||
like so:
|
like so:
|
||||||
|
@ -204,7 +223,7 @@ region whose value is being inferred by the system. I also replaced
|
||||||
`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
|
`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
|
||||||
to indicate skolemized region names. We can assume they don't appear
|
to indicate skolemized region names. We can assume they don't appear
|
||||||
elsewhere. Note that neither the sub- nor the supertype bind any
|
elsewhere. Note that neither the sub- nor the supertype bind any
|
||||||
region names anymore (that is, the `<a>` and `<b>` have been removed).
|
region names anymore (as indicated by the absence of `<` and `>`).
|
||||||
|
|
||||||
The next step is to check that the parameter types match. Because
|
The next step is to check that the parameter types match. Because
|
||||||
parameters are contravariant, this means that we check whether:
|
parameters are contravariant, this means that we check whether:
|
||||||
|
@ -226,25 +245,25 @@ So far we have encountered no error, so the subtype check succeeds.
|
||||||
|
|
||||||
Now let's look first at the third example, which was:
|
Now let's look first at the third example, which was:
|
||||||
|
|
||||||
3. fn(&a/T) <: fn<b>(&b/T)? No!
|
3. fn(&self/T) <: fn<b>(&b/T)? No!
|
||||||
|
|
||||||
After steps 1 and 2 of the algorithm we will have replaced the types
|
After steps 1 and 2 of the algorithm we will have replaced the types
|
||||||
like so:
|
like so:
|
||||||
|
|
||||||
3. fn(&a/T) <: fn(&x/T)?
|
3. fn(&self/T) <: fn(&x/T)?
|
||||||
|
|
||||||
This looks pretty much the same as before, except that on the LHS `&a`
|
This looks pretty much the same as before, except that on the LHS
|
||||||
was not bound, and hence was left as-is and not replaced with a
|
`&self` was not bound, and hence was left as-is and not replaced with
|
||||||
variable. The next step is again to check that the parameter types
|
a variable. The next step is again to check that the parameter types
|
||||||
match. This will ultimately require (as before) that `&a` <= `&x`
|
match. This will ultimately require (as before) that `&self` <= `&x`
|
||||||
must hold: but this does not hold. `a` and `x` are both distinct free
|
must hold: but this does not hold. `self` and `x` are both distinct
|
||||||
regions. So the subtype check fails.
|
free regions. So the subtype check fails.
|
||||||
|
|
||||||
#### Checking for skolemization leaks
|
#### Checking for skolemization leaks
|
||||||
|
|
||||||
You may be wondering about that mysterious last step. So far it has not
|
You may be wondering about that mysterious last step in the algorithm.
|
||||||
been relevant. The purpose of that last step is to catch something like
|
So far it has not been relevant. The purpose of that last step is to
|
||||||
*this*:
|
catch something like *this*:
|
||||||
|
|
||||||
fn<a>() -> fn(&a/T) <: fn() -> fn<b>(&b/T)? No.
|
fn<a>() -> fn(&a/T) <: fn() -> fn<b>(&b/T)? No.
|
||||||
|
|
||||||
|
@ -295,10 +314,11 @@ rule.
|
||||||
|
|
||||||
So the way we solve this is to add a fourth step that examines the
|
So the way we solve this is to add a fourth step that examines the
|
||||||
constraints that refer to skolemized names. Basically, consider a
|
constraints that refer to skolemized names. Basically, consider a
|
||||||
non-directed verison of the constraint graph. The only things
|
non-directed verison of the constraint graph. Let `Tainted(x)` be the
|
||||||
reachable from a skolemized region ought to be the region variables
|
set of all things reachable from a skolemized variable `x`.
|
||||||
that were created at the same time. So this case here would fail
|
`Tainted(x)` should not contain any regions that existed before the
|
||||||
because `&x` was created alone, but is relatable to `&A`.
|
step at which the skolemization was performed. So this case here
|
||||||
|
would fail because `&x` was created alone, but is relatable to `&A`.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -313,7 +333,8 @@ use std::cell::{Cell, empty_cell};
|
||||||
use std::list::{List, Nil, Cons};
|
use std::list::{List, Nil, Cons};
|
||||||
|
|
||||||
use region::is_subregion_of;
|
use region::is_subregion_of;
|
||||||
use ty::{Region, RegionVid};
|
use ty::{Region, RegionVid, re_static, re_infer, re_free, re_bound,
|
||||||
|
re_scope, ReVar, ReSkolemized};
|
||||||
use syntax::codemap;
|
use syntax::codemap;
|
||||||
use to_str::ToStr;
|
use to_str::ToStr;
|
||||||
use util::ppaux::note_and_explain_region;
|
use util::ppaux::note_and_explain_region;
|
||||||
|
@ -350,7 +371,7 @@ impl Constraint : cmp::Eq {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Constraint : to_bytes::IterBytes {
|
impl Constraint : to_bytes::IterBytes {
|
||||||
pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
|
pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
|
||||||
match self {
|
match self {
|
||||||
ConstrainVarSubVar(ref v0, ref v1) =>
|
ConstrainVarSubVar(ref v0, ref v1) =>
|
||||||
to_bytes::iter_bytes_3(&0u8, v0, v1, lsb0, f),
|
to_bytes::iter_bytes_3(&0u8, v0, v1, lsb0, f),
|
||||||
|
@ -394,10 +415,11 @@ type CombineMap = HashMap<TwoRegions, RegionVid>;
|
||||||
struct RegionVarBindings {
|
struct RegionVarBindings {
|
||||||
tcx: ty::ctxt,
|
tcx: ty::ctxt,
|
||||||
var_spans: DVec<span>,
|
var_spans: DVec<span>,
|
||||||
values: Cell<~[ty::Region]>,
|
values: Cell<~[Region]>,
|
||||||
constraints: HashMap<Constraint, span>,
|
constraints: HashMap<Constraint, span>,
|
||||||
lubs: CombineMap,
|
lubs: CombineMap,
|
||||||
glbs: CombineMap,
|
glbs: CombineMap,
|
||||||
|
mut skolemization_count: uint,
|
||||||
|
|
||||||
// The undo log records actions that might later be undone.
|
// The undo log records actions that might later be undone.
|
||||||
//
|
//
|
||||||
|
@ -407,7 +429,7 @@ struct RegionVarBindings {
|
||||||
// actively snapshotting. The reason for this is that otherwise
|
// actively snapshotting. The reason for this is that otherwise
|
||||||
// we end up adding entries for things like the lower bound on
|
// we end up adding entries for things like the lower bound on
|
||||||
// a variable and so forth, which can never be rolled back.
|
// a variable and so forth, which can never be rolled back.
|
||||||
undo_log: DVec<UndoLogEntry>
|
mut undo_log: ~[UndoLogEntry]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
|
fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
|
||||||
|
@ -418,7 +440,8 @@ fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
|
||||||
constraints: HashMap(),
|
constraints: HashMap(),
|
||||||
lubs: CombineMap(),
|
lubs: CombineMap(),
|
||||||
glbs: CombineMap(),
|
glbs: CombineMap(),
|
||||||
undo_log: DVec()
|
skolemization_count: 0,
|
||||||
|
undo_log: ~[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,11 +453,11 @@ fn CombineMap() -> CombineMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegionVarBindings {
|
impl RegionVarBindings {
|
||||||
fn in_snapshot() -> bool {
|
fn in_snapshot(&self) -> bool {
|
||||||
self.undo_log.len() > 0
|
self.undo_log.len() > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_snapshot() -> uint {
|
fn start_snapshot(&self) -> uint {
|
||||||
debug!("RegionVarBindings: snapshot()=%u", self.undo_log.len());
|
debug!("RegionVarBindings: snapshot()=%u", self.undo_log.len());
|
||||||
if self.in_snapshot() {
|
if self.in_snapshot() {
|
||||||
self.undo_log.len()
|
self.undo_log.len()
|
||||||
|
@ -444,14 +467,14 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit() {
|
fn commit(&self) {
|
||||||
debug!("RegionVarBindings: commit()");
|
debug!("RegionVarBindings: commit()");
|
||||||
while self.undo_log.len() > 0 {
|
while self.undo_log.len() > 0 {
|
||||||
self.undo_log.pop();
|
self.undo_log.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rollback_to(snapshot: uint) {
|
fn rollback_to(&self, snapshot: uint) {
|
||||||
debug!("RegionVarBindings: rollback_to(%u)", snapshot);
|
debug!("RegionVarBindings: rollback_to(%u)", snapshot);
|
||||||
while self.undo_log.len() > snapshot {
|
while self.undo_log.len() > snapshot {
|
||||||
let undo_item = self.undo_log.pop();
|
let undo_item = self.undo_log.pop();
|
||||||
|
@ -472,11 +495,11 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn num_vars() -> uint {
|
fn num_vars(&self) -> uint {
|
||||||
self.var_spans.len()
|
self.var_spans.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_region_var(span: span) -> RegionVid {
|
fn new_region_var(&self, span: span) -> RegionVid {
|
||||||
let id = self.num_vars();
|
let id = self.num_vars();
|
||||||
self.var_spans.push(span);
|
self.var_spans.push(span);
|
||||||
let vid = RegionVid(id);
|
let vid = RegionVid(id);
|
||||||
|
@ -488,7 +511,13 @@ impl RegionVarBindings {
|
||||||
return vid;
|
return vid;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_constraint(+constraint: Constraint, span: span) {
|
fn new_skolemized(&self, br: ty::bound_region) -> Region {
|
||||||
|
let sc = self.skolemization_count;
|
||||||
|
self.skolemization_count += 1;
|
||||||
|
re_infer(ReSkolemized(sc, br))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_constraint(&self, +constraint: Constraint, span: span) {
|
||||||
// cannot add constraints once regions are resolved
|
// cannot add constraints once regions are resolved
|
||||||
assert self.values.is_empty();
|
assert self.values.is_empty();
|
||||||
|
|
||||||
|
@ -501,21 +530,22 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_subregion(span: span, sub: Region, sup: Region) -> cres<()> {
|
fn make_subregion(&self, span: span,
|
||||||
|
sub: Region, sup: Region) -> cres<()> {
|
||||||
// cannot add constraints once regions are resolved
|
// cannot add constraints once regions are resolved
|
||||||
assert self.values.is_empty();
|
assert self.values.is_empty();
|
||||||
|
|
||||||
debug!("RegionVarBindings: make_subregion(%?, %?)", sub, sup);
|
debug!("RegionVarBindings: make_subregion(%?, %?)", sub, sup);
|
||||||
match (sub, sup) {
|
match (sub, sup) {
|
||||||
(ty::re_var (sub_id), ty::re_var(sup_id)) => {
|
(re_infer(ReVar(sub_id)), re_infer(ReVar(sup_id))) => {
|
||||||
self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), span);
|
self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), span);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
(r, ty::re_var(sup_id)) => {
|
(r, re_infer(ReVar(sup_id))) => {
|
||||||
self.add_constraint(ConstrainRegSubVar(r, sup_id), span);
|
self.add_constraint(ConstrainRegSubVar(r, sup_id), span);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
(ty::re_var(sub_id), r) => {
|
(re_infer(ReVar(sub_id)), r) => {
|
||||||
self.add_constraint(ConstrainVarSubReg(sub_id, r), span);
|
self.add_constraint(ConstrainVarSubReg(sub_id, r), span);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -529,17 +559,17 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lub_regions(span: span, a: Region, b: Region) -> cres<Region> {
|
fn lub_regions(&self, span: span, a: Region, b: Region) -> cres<Region> {
|
||||||
// cannot add constraints once regions are resolved
|
// cannot add constraints once regions are resolved
|
||||||
assert self.values.is_empty();
|
assert self.values.is_empty();
|
||||||
|
|
||||||
debug!("RegionVarBindings: lub_regions(%?, %?)", a, b);
|
debug!("RegionVarBindings: lub_regions(%?, %?)", a, b);
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(ty::re_static, _) | (_, ty::re_static) => {
|
(re_static, _) | (_, re_static) => {
|
||||||
Ok(ty::re_static) // nothing lives longer than static
|
Ok(re_static) // nothing lives longer than static
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::re_var(*), _) | (_, ty::re_var(*)) => {
|
(re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => {
|
||||||
self.combine_vars(
|
self.combine_vars(
|
||||||
self.lubs, a, b, span,
|
self.lubs, a, b, span,
|
||||||
|old_r, new_r| self.make_subregion(span, old_r, new_r))
|
|old_r, new_r| self.make_subregion(span, old_r, new_r))
|
||||||
|
@ -551,18 +581,18 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glb_regions(span: span, a: Region, b: Region) -> cres<Region> {
|
fn glb_regions(&self, span: span, a: Region, b: Region) -> cres<Region> {
|
||||||
// cannot add constraints once regions are resolved
|
// cannot add constraints once regions are resolved
|
||||||
assert self.values.is_empty();
|
assert self.values.is_empty();
|
||||||
|
|
||||||
debug!("RegionVarBindings: glb_regions(%?, %?)", a, b);
|
debug!("RegionVarBindings: glb_regions(%?, %?)", a, b);
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(ty::re_static, r) | (r, ty::re_static) => {
|
(re_static, r) | (r, re_static) => {
|
||||||
// static lives longer than everything else
|
// static lives longer than everything else
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::re_var(*), _) | (_, ty::re_var(*)) => {
|
(re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => {
|
||||||
self.combine_vars(
|
self.combine_vars(
|
||||||
self.glbs, a, b, span,
|
self.glbs, a, b, span,
|
||||||
|old_r, new_r| self.make_subregion(span, new_r, old_r))
|
|old_r, new_r| self.make_subregion(span, new_r, old_r))
|
||||||
|
@ -574,7 +604,7 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_var(rid: RegionVid) -> ty::Region {
|
fn resolve_var(&self, rid: RegionVid) -> ty::Region {
|
||||||
debug!("RegionVarBindings: resolve_var(%?=%u)", rid, *rid);
|
debug!("RegionVarBindings: resolve_var(%?=%u)", rid, *rid);
|
||||||
if self.values.is_empty() {
|
if self.values.is_empty() {
|
||||||
self.tcx.sess.span_bug(
|
self.tcx.sess.span_bug(
|
||||||
|
@ -586,29 +616,129 @@ impl RegionVarBindings {
|
||||||
self.values.with_ref(|values| values[*rid])
|
self.values.with_ref(|values| values[*rid])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn combine_vars(combines: CombineMap, a: Region, b: Region, span: span,
|
fn combine_vars(&self,
|
||||||
|
combines: CombineMap,
|
||||||
|
a: Region,
|
||||||
|
b: Region,
|
||||||
|
span: span,
|
||||||
relate: fn(old_r: Region, new_r: Region) -> cres<()>)
|
relate: fn(old_r: Region, new_r: Region) -> cres<()>)
|
||||||
-> cres<Region> {
|
-> cres<Region> {
|
||||||
|
|
||||||
let vars = TwoRegions { a: a, b: b };
|
let vars = TwoRegions { a: a, b: b };
|
||||||
match combines.find(vars) {
|
match combines.find(vars) {
|
||||||
Some(c) => Ok(ty::re_var(c)),
|
Some(c) => Ok(re_infer(ReVar(c))),
|
||||||
None => {
|
None => {
|
||||||
let c = self.new_region_var(span);
|
let c = self.new_region_var(span);
|
||||||
combines.insert(vars, c);
|
combines.insert(vars, c);
|
||||||
if self.in_snapshot() {
|
if self.in_snapshot() {
|
||||||
self.undo_log.push(AddCombination(combines, vars));
|
self.undo_log.push(AddCombination(combines, vars));
|
||||||
}
|
}
|
||||||
do relate(a, ty::re_var(c)).then {
|
do relate(a, re_infer(ReVar(c))).then {
|
||||||
do relate(b, ty::re_var(c)).then {
|
do relate(b, re_infer(ReVar(c))).then {
|
||||||
debug!("combine_vars() c=%?", ty::re_var(c));
|
debug!("combine_vars() c=%?", c);
|
||||||
Ok(ty::re_var(c))
|
Ok(re_infer(ReVar(c)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tainted(&self, snapshot: uint, r0: Region) -> ~[Region] {
|
||||||
|
/*!
|
||||||
|
*
|
||||||
|
* Computes all regions that have been related to `r0` in any
|
||||||
|
* way since the snapshot `snapshot` was taken---excluding
|
||||||
|
* `r0` itself and any region variables added as part of the
|
||||||
|
* snapshot. This is used when checking whether skolemized
|
||||||
|
* regions are being improperly related to other regions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
debug!("tainted(snapshot=%u, r0=%?)", snapshot, r0);
|
||||||
|
let _indenter = indenter();
|
||||||
|
|
||||||
|
let undo_len = self.undo_log.len();
|
||||||
|
|
||||||
|
// collect variables added since the snapshot was taken
|
||||||
|
let new_vars = do vec::build |push| {
|
||||||
|
for uint::range(snapshot, undo_len) |i| {
|
||||||
|
match self.undo_log[i] {
|
||||||
|
AddVar(vid) => push(vid),
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `result_set` acts as a worklist: we explore all outgoing
|
||||||
|
// edges and add any new regions we find to result_set. This
|
||||||
|
// is not a terribly efficient implementation.
|
||||||
|
let mut result_set = ~[r0], result_index = 0;
|
||||||
|
while result_index < result_set.len() {
|
||||||
|
// nb: can't use uint::range() here because result_set grows
|
||||||
|
let r = result_set[result_index];
|
||||||
|
|
||||||
|
debug!("result_index=%u, r=%?", result_index, r);
|
||||||
|
|
||||||
|
let mut undo_index = snapshot;
|
||||||
|
while undo_index < undo_len {
|
||||||
|
// nb: can't use uint::range() here as we move result_set
|
||||||
|
let regs = match self.undo_log[undo_index] {
|
||||||
|
AddConstraint(ConstrainVarSubVar(ref a, ref b)) => {
|
||||||
|
Some((re_infer(ReVar(*a)),
|
||||||
|
re_infer(ReVar(*b))))
|
||||||
|
}
|
||||||
|
AddConstraint(ConstrainRegSubVar(ref a, ref b)) => {
|
||||||
|
Some((*a, re_infer(ReVar(*b))))
|
||||||
|
}
|
||||||
|
AddConstraint(ConstrainVarSubReg(ref a, ref b)) => {
|
||||||
|
Some((re_infer(ReVar(*a)), *b))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match regs {
|
||||||
|
None => {}
|
||||||
|
Some((ref r1, ref r2)) => {
|
||||||
|
result_set =
|
||||||
|
consider_adding_edge(move result_set, &r, r1, r2);
|
||||||
|
result_set =
|
||||||
|
consider_adding_edge(move result_set, &r, r2, r1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
undo_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop `r0` itself and any region variables that were created
|
||||||
|
// since the snapshot.
|
||||||
|
result_set.retain(|r| {
|
||||||
|
match *r {
|
||||||
|
re_infer(ReVar(ref vid)) => !new_vars.contains(vid),
|
||||||
|
_ => *r != r0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result_set;
|
||||||
|
|
||||||
|
fn consider_adding_edge(+result_set: ~[Region],
|
||||||
|
r: &Region,
|
||||||
|
r1: &Region,
|
||||||
|
r2: &Region) -> ~[Region]
|
||||||
|
{
|
||||||
|
let mut result_set = move result_set;
|
||||||
|
if *r == *r1 { // Clearly, this is potentially inefficient.
|
||||||
|
if !result_set.contains(r2) {
|
||||||
|
result_set.push(*r2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return move result_set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This function performs the actual region resolution. It must be
|
This function performs the actual region resolution. It must be
|
||||||
called after all constraints have been added. It performs a
|
called after all constraints have been added. It performs a
|
||||||
|
@ -616,32 +746,32 @@ impl RegionVarBindings {
|
||||||
constraints, assuming such values can be found; if they cannot,
|
constraints, assuming such values can be found; if they cannot,
|
||||||
errors are reported.
|
errors are reported.
|
||||||
*/
|
*/
|
||||||
fn resolve_regions() {
|
fn resolve_regions(&self) {
|
||||||
debug!("RegionVarBindings: resolve_regions()");
|
debug!("RegionVarBindings: resolve_regions()");
|
||||||
self.values.put_back(self.infer_variable_values());
|
self.values.put_back(self.infer_variable_values());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
priv impl RegionVarBindings {
|
priv impl RegionVarBindings {
|
||||||
fn is_subregion_of(sub: Region, sup: Region) -> bool {
|
fn is_subregion_of(&self, sub: Region, sup: Region) -> bool {
|
||||||
is_subregion_of(self.tcx.region_map, sub, sup)
|
is_subregion_of(self.tcx.region_map, sub, sup)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lub_concrete_regions(+a: Region, +b: Region) -> Region {
|
fn lub_concrete_regions(&self, +a: Region, +b: Region) -> Region {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(ty::re_static, _) | (_, ty::re_static) => {
|
(re_static, _) | (_, re_static) => {
|
||||||
ty::re_static // nothing lives longer than static
|
re_static // nothing lives longer than static
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::re_var(v_id), _) | (_, ty::re_var(v_id)) => {
|
(re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => {
|
||||||
self.tcx.sess.span_bug(
|
self.tcx.sess.span_bug(
|
||||||
self.var_spans[*v_id],
|
self.var_spans[*v_id],
|
||||||
fmt!("lub_concrete_regions invoked with \
|
fmt!("lub_concrete_regions invoked with \
|
||||||
non-concrete regions: %?, %?", a, b));
|
non-concrete regions: %?, %?", a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
(f @ ty::re_free(f_id, _), ty::re_scope(s_id)) |
|
(f @ re_free(f_id, _), re_scope(s_id)) |
|
||||||
(ty::re_scope(s_id), f @ ty::re_free(f_id, _)) => {
|
(re_scope(s_id), f @ re_free(f_id, _)) => {
|
||||||
// A "free" region can be interpreted as "some region
|
// A "free" region can be interpreted as "some region
|
||||||
// at least as big as the block f_id". So, we can
|
// at least as big as the block f_id". So, we can
|
||||||
// reasonably compare free regions and scopes:
|
// reasonably compare free regions and scopes:
|
||||||
|
@ -654,98 +784,103 @@ priv impl RegionVarBindings {
|
||||||
|
|
||||||
// otherwise, we don't know what the free region is,
|
// otherwise, we don't know what the free region is,
|
||||||
// so we must conservatively say the LUB is static:
|
// so we must conservatively say the LUB is static:
|
||||||
_ => ty::re_static
|
_ => re_static
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::re_scope(a_id), ty::re_scope(b_id)) => {
|
(re_scope(a_id), re_scope(b_id)) => {
|
||||||
// The region corresponding to an outer block is a
|
// The region corresponding to an outer block is a
|
||||||
// subtype of the region corresponding to an inner
|
// subtype of the region corresponding to an inner
|
||||||
// block.
|
// block.
|
||||||
let rm = self.tcx.region_map;
|
let rm = self.tcx.region_map;
|
||||||
match region::nearest_common_ancestor(rm, a_id, b_id) {
|
match region::nearest_common_ancestor(rm, a_id, b_id) {
|
||||||
Some(r_id) => ty::re_scope(r_id),
|
Some(r_id) => re_scope(r_id),
|
||||||
_ => ty::re_static
|
_ => re_static
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For these types, we cannot define any additional
|
// For these types, we cannot define any additional
|
||||||
// relationship:
|
// relationship:
|
||||||
(ty::re_free(_, _), ty::re_free(_, _)) |
|
(re_infer(ReSkolemized(*)), _) |
|
||||||
(ty::re_bound(_), ty::re_bound(_)) |
|
(_, re_infer(ReSkolemized(*))) |
|
||||||
(ty::re_bound(_), ty::re_free(_, _)) |
|
(re_free(_, _), re_free(_, _)) |
|
||||||
(ty::re_bound(_), ty::re_scope(_)) |
|
(re_bound(_), re_bound(_)) |
|
||||||
(ty::re_free(_, _), ty::re_bound(_)) |
|
(re_bound(_), re_free(_, _)) |
|
||||||
(ty::re_scope(_), ty::re_bound(_)) => {
|
(re_bound(_), re_scope(_)) |
|
||||||
if a == b {a} else {ty::re_static}
|
(re_free(_, _), re_bound(_)) |
|
||||||
|
(re_scope(_), re_bound(_)) => {
|
||||||
|
if a == b {a} else {re_static}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glb_concrete_regions(+a: Region, +b: Region) -> cres<Region> {
|
fn glb_concrete_regions(&self, +a: Region, +b: Region) -> cres<Region> {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(ty::re_static, r) | (r, ty::re_static) => {
|
(re_static, r) | (r, re_static) => {
|
||||||
// static lives longer than everything else
|
// static lives longer than everything else
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
|
||||||
|
|
||||||
(ty::re_var(v_id), _) | (_, ty::re_var(v_id)) => {
|
|
||||||
self.tcx.sess.span_bug(
|
|
||||||
self.var_spans[*v_id],
|
|
||||||
fmt!("glb_concrete_regions invoked with \
|
|
||||||
non-concrete regions: %?, %?", a, b));
|
|
||||||
}
|
|
||||||
|
|
||||||
(ty::re_free(f_id, _), s @ ty::re_scope(s_id)) |
|
|
||||||
(s @ ty::re_scope(s_id), ty::re_free(f_id, _)) => {
|
|
||||||
// Free region is something "at least as big as
|
|
||||||
// `f_id`." If we find that the scope `f_id` is bigger
|
|
||||||
// than the scope `s_id`, then we can say that the GLB
|
|
||||||
// is the scope `s_id`. Otherwise, as we do not know
|
|
||||||
// big the free region is precisely, the GLB is undefined.
|
|
||||||
let rm = self.tcx.region_map;
|
|
||||||
match region::nearest_common_ancestor(rm, f_id, s_id) {
|
|
||||||
Some(r_id) if r_id == f_id => Ok(s),
|
|
||||||
_ => Err(ty::terr_regions_no_overlap(b, a))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
(ty::re_scope(a_id), ty::re_scope(b_id)) |
|
(re_infer(ReVar(v_id)), _) |
|
||||||
(ty::re_free(a_id, _), ty::re_free(b_id, _)) => {
|
(_, re_infer(ReVar(v_id))) => {
|
||||||
if a == b {
|
self.tcx.sess.span_bug(
|
||||||
// Same scope or same free identifier, easy case.
|
self.var_spans[*v_id],
|
||||||
Ok(a)
|
fmt!("glb_concrete_regions invoked with \
|
||||||
} else {
|
non-concrete regions: %?, %?", a, b));
|
||||||
// We want to generate the intersection of two
|
}
|
||||||
// scopes or two free regions. So, if one of
|
|
||||||
// these scopes is a subscope of the other, return
|
(re_free(f_id, _), s @ re_scope(s_id)) |
|
||||||
// it. Otherwise fail.
|
(s @ re_scope(s_id), re_free(f_id, _)) => {
|
||||||
|
// Free region is something "at least as big as
|
||||||
|
// `f_id`." If we find that the scope `f_id` is bigger
|
||||||
|
// than the scope `s_id`, then we can say that the GLB
|
||||||
|
// is the scope `s_id`. Otherwise, as we do not know
|
||||||
|
// big the free region is precisely, the GLB is undefined.
|
||||||
let rm = self.tcx.region_map;
|
let rm = self.tcx.region_map;
|
||||||
match region::nearest_common_ancestor(rm, a_id, b_id) {
|
match region::nearest_common_ancestor(rm, f_id, s_id) {
|
||||||
Some(r_id) if a_id == r_id => Ok(ty::re_scope(b_id)),
|
Some(r_id) if r_id == f_id => Ok(s),
|
||||||
Some(r_id) if b_id == r_id => Ok(ty::re_scope(a_id)),
|
_ => Err(ty::terr_regions_no_overlap(b, a))
|
||||||
_ => Err(ty::terr_regions_no_overlap(b, a))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// For these types, we cannot define any additional
|
(re_scope(a_id), re_scope(b_id)) |
|
||||||
// relationship:
|
(re_free(a_id, _), re_free(b_id, _)) => {
|
||||||
(ty::re_bound(_), ty::re_bound(_)) |
|
if a == b {
|
||||||
(ty::re_bound(_), ty::re_free(_, _)) |
|
// Same scope or same free identifier, easy case.
|
||||||
(ty::re_bound(_), ty::re_scope(_)) |
|
Ok(a)
|
||||||
(ty::re_free(_, _), ty::re_bound(_)) |
|
} else {
|
||||||
(ty::re_scope(_), ty::re_bound(_)) => {
|
// We want to generate the intersection of two
|
||||||
if a == b {
|
// scopes or two free regions. So, if one of
|
||||||
Ok(a)
|
// these scopes is a subscope of the other, return
|
||||||
} else {
|
// it. Otherwise fail.
|
||||||
Err(ty::terr_regions_no_overlap(b, a))
|
let rm = self.tcx.region_map;
|
||||||
|
match region::nearest_common_ancestor(rm, a_id, b_id) {
|
||||||
|
Some(r_id) if a_id == r_id => Ok(re_scope(b_id)),
|
||||||
|
Some(r_id) if b_id == r_id => Ok(re_scope(a_id)),
|
||||||
|
_ => Err(ty::terr_regions_no_overlap(b, a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For these types, we cannot define any additional
|
||||||
|
// relationship:
|
||||||
|
(re_infer(ReSkolemized(*)), _) |
|
||||||
|
(_, re_infer(ReSkolemized(*))) |
|
||||||
|
(re_bound(_), re_bound(_)) |
|
||||||
|
(re_bound(_), re_free(_, _)) |
|
||||||
|
(re_bound(_), re_scope(_)) |
|
||||||
|
(re_free(_, _), re_bound(_)) |
|
||||||
|
(re_scope(_), re_bound(_)) => {
|
||||||
|
if a == b {
|
||||||
|
Ok(a)
|
||||||
|
} else {
|
||||||
|
Err(ty::terr_regions_no_overlap(b, a))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_type_error(span: span, terr: &ty::type_err) {
|
fn report_type_error(&self, span: span, terr: &ty::type_err) {
|
||||||
let terr_str = ty::type_err_to_str(self.tcx, terr);
|
let terr_str = ty::type_err_to_str(self.tcx, terr);
|
||||||
self.tcx.sess.span_err(span, terr_str);
|
self.tcx.sess.span_err(span, terr_str);
|
||||||
}
|
}
|
||||||
|
@ -803,14 +938,14 @@ fn TwoRegionsMap() -> TwoRegionsMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegionVarBindings {
|
impl RegionVarBindings {
|
||||||
fn infer_variable_values() -> ~[Region] {
|
fn infer_variable_values(&self) -> ~[Region] {
|
||||||
let graph = self.construct_graph();
|
let graph = self.construct_graph();
|
||||||
self.expansion(&graph);
|
self.expansion(&graph);
|
||||||
self.contraction(&graph);
|
self.contraction(&graph);
|
||||||
self.extract_regions_and_report_errors(&graph)
|
self.extract_regions_and_report_errors(&graph)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_graph() -> Graph {
|
fn construct_graph(&self) -> Graph {
|
||||||
let num_vars = self.num_vars();
|
let num_vars = self.num_vars();
|
||||||
let num_edges = self.constraints.size();
|
let num_edges = self.constraints.size();
|
||||||
|
|
||||||
|
@ -871,7 +1006,7 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expansion(graph: &Graph) {
|
fn expansion(&self, graph: &Graph) {
|
||||||
do self.iterate_until_fixed_point(~"Expansion", graph) |edge| {
|
do self.iterate_until_fixed_point(~"Expansion", graph) |edge| {
|
||||||
match edge.constraint {
|
match edge.constraint {
|
||||||
ConstrainRegSubVar(copy a_region, copy b_vid) => {
|
ConstrainRegSubVar(copy a_region, copy b_vid) => {
|
||||||
|
@ -895,7 +1030,8 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_node(a_region: Region,
|
fn expand_node(&self,
|
||||||
|
a_region: Region,
|
||||||
b_vid: RegionVid,
|
b_vid: RegionVid,
|
||||||
b_node: &GraphNode) -> bool {
|
b_node: &GraphNode) -> bool {
|
||||||
debug!("expand_node(%?, %? == %?)",
|
debug!("expand_node(%?, %? == %?)",
|
||||||
|
@ -929,7 +1065,7 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contraction(graph: &Graph) {
|
fn contraction(&self, graph: &Graph) {
|
||||||
do self.iterate_until_fixed_point(~"Contraction", graph) |edge| {
|
do self.iterate_until_fixed_point(~"Contraction", graph) |edge| {
|
||||||
match edge.constraint {
|
match edge.constraint {
|
||||||
ConstrainRegSubVar(*) => {
|
ConstrainRegSubVar(*) => {
|
||||||
|
@ -953,33 +1089,34 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contract_node(a_vid: RegionVid,
|
fn contract_node(&self,
|
||||||
|
a_vid: RegionVid,
|
||||||
a_node: &GraphNode,
|
a_node: &GraphNode,
|
||||||
b_region: Region) -> bool {
|
b_region: Region) -> bool {
|
||||||
debug!("contract_node(%? == %?/%?, %?)",
|
debug!("contract_node(%? == %?/%?, %?)",
|
||||||
a_vid, a_node.value, a_node.classification, b_region);
|
a_vid, a_node.value, a_node.classification, b_region);
|
||||||
|
|
||||||
return match a_node.value {
|
return match a_node.value {
|
||||||
NoValue => {
|
NoValue => {
|
||||||
assert a_node.classification == Contracting;
|
assert a_node.classification == Contracting;
|
||||||
a_node.value = Value(b_region);
|
a_node.value = Value(b_region);
|
||||||
true // changed
|
true // changed
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorValue => {
|
ErrorValue => {
|
||||||
false // no change
|
false // no change
|
||||||
}
|
}
|
||||||
|
|
||||||
Value(copy a_region) => {
|
Value(copy a_region) => {
|
||||||
match a_node.classification {
|
match a_node.classification {
|
||||||
Expanding => {
|
Expanding => {
|
||||||
check_node(&self, a_vid, a_node, a_region, b_region)
|
check_node(self, a_vid, a_node, a_region, b_region)
|
||||||
}
|
}
|
||||||
Contracting => {
|
Contracting => {
|
||||||
adjust_node(&self, a_vid, a_node, a_region, b_region)
|
adjust_node(self, a_vid, a_node, a_region, b_region)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn check_node(self: &RegionVarBindings,
|
fn check_node(self: &RegionVarBindings,
|
||||||
|
@ -1001,25 +1138,26 @@ impl RegionVarBindings {
|
||||||
a_region: Region,
|
a_region: Region,
|
||||||
b_region: Region) -> bool {
|
b_region: Region) -> bool {
|
||||||
match self.glb_concrete_regions(a_region, b_region) {
|
match self.glb_concrete_regions(a_region, b_region) {
|
||||||
Ok(glb) => {
|
Ok(glb) => {
|
||||||
if glb == a_region {
|
if glb == a_region {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
debug!("Contracting value of %? from %? to %?",
|
debug!("Contracting value of %? from %? to %?",
|
||||||
a_vid, a_region, glb);
|
a_vid, a_region, glb);
|
||||||
a_node.value = Value(glb);
|
a_node.value = Value(glb);
|
||||||
true
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
a_node.value = ErrorValue;
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
a_node.value = ErrorValue;
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iterate_until_fixed_point(
|
fn iterate_until_fixed_point(
|
||||||
|
&self,
|
||||||
tag: ~str,
|
tag: ~str,
|
||||||
graph: &Graph,
|
graph: &Graph,
|
||||||
body: fn(edge: &GraphEdge) -> bool)
|
body: fn(edge: &GraphEdge) -> bool)
|
||||||
|
@ -1040,7 +1178,7 @@ impl RegionVarBindings {
|
||||||
debug!("---- %s Complete after %u iteration(s)", tag, iteration);
|
debug!("---- %s Complete after %u iteration(s)", tag, iteration);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_regions_and_report_errors(graph: &Graph) -> ~[Region] {
|
fn extract_regions_and_report_errors(&self, graph: &Graph) -> ~[Region] {
|
||||||
let dup_map = TwoRegionsMap();
|
let dup_map = TwoRegionsMap();
|
||||||
graph.nodes.mapi(|idx, node| {
|
graph.nodes.mapi(|idx, node| {
|
||||||
match node.value {
|
match node.value {
|
||||||
|
@ -1050,7 +1188,7 @@ impl RegionVarBindings {
|
||||||
self.tcx.sess.span_err(
|
self.tcx.sess.span_err(
|
||||||
node.span,
|
node.span,
|
||||||
fmt!("Unconstrained region variable #%u", idx));
|
fmt!("Unconstrained region variable #%u", idx));
|
||||||
ty::re_static
|
re_static
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorValue => {
|
ErrorValue => {
|
||||||
|
@ -1065,21 +1203,23 @@ impl RegionVarBindings {
|
||||||
graph, dup_map, node_vid);
|
graph, dup_map, node_vid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty::re_static
|
re_static
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to suppress reporting the same basic error over and over
|
// Used to suppress reporting the same basic error over and over
|
||||||
fn is_reported(dup_map: TwoRegionsMap,
|
fn is_reported(&self,
|
||||||
|
dup_map: TwoRegionsMap,
|
||||||
r_a: Region,
|
r_a: Region,
|
||||||
r_b: Region) -> bool {
|
r_b: Region) -> bool {
|
||||||
let key = TwoRegions { a: r_a, b: r_b };
|
let key = TwoRegions { a: r_a, b: r_b };
|
||||||
!dup_map.insert(key, ())
|
!dup_map.insert(key, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_error_for_expanding_node(graph: &Graph,
|
fn report_error_for_expanding_node(&self,
|
||||||
|
graph: &Graph,
|
||||||
dup_map: TwoRegionsMap,
|
dup_map: TwoRegionsMap,
|
||||||
node_idx: RegionVid) {
|
node_idx: RegionVid) {
|
||||||
// Errors in expanding nodes result from a lower-bound that is
|
// Errors in expanding nodes result from a lower-bound that is
|
||||||
|
@ -1131,7 +1271,8 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_error_for_contracting_node(graph: &Graph,
|
fn report_error_for_contracting_node(&self,
|
||||||
|
graph: &Graph,
|
||||||
dup_map: TwoRegionsMap,
|
dup_map: TwoRegionsMap,
|
||||||
node_idx: RegionVid) {
|
node_idx: RegionVid) {
|
||||||
// Errors in contracting nodes result from two upper-bounds
|
// Errors in contracting nodes result from two upper-bounds
|
||||||
|
@ -1184,7 +1325,8 @@ impl RegionVarBindings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_concrete_regions(graph: &Graph,
|
fn collect_concrete_regions(&self,
|
||||||
|
graph: &Graph,
|
||||||
orig_node_idx: RegionVid,
|
orig_node_idx: RegionVid,
|
||||||
dir: Direction) -> ~[SpannedRegion] {
|
dir: Direction) -> ~[SpannedRegion] {
|
||||||
let set = HashMap();
|
let set = HashMap();
|
||||||
|
@ -1226,7 +1368,8 @@ impl RegionVarBindings {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn each_edge(graph: &Graph,
|
fn each_edge(&self,
|
||||||
|
graph: &Graph,
|
||||||
node_idx: RegionVid,
|
node_idx: RegionVid,
|
||||||
dir: Direction,
|
dir: Direction,
|
||||||
op: fn(edge: &GraphEdge) -> bool) {
|
op: fn(edge: &GraphEdge) -> bool) {
|
|
@ -148,21 +148,21 @@ impl resolve_state {
|
||||||
fn resolve_region(orig: ty::Region) -> ty::Region {
|
fn resolve_region(orig: ty::Region) -> ty::Region {
|
||||||
debug!("Resolve_region(%s)", orig.to_str(self.infcx));
|
debug!("Resolve_region(%s)", orig.to_str(self.infcx));
|
||||||
match orig {
|
match orig {
|
||||||
ty::re_var(rid) => self.resolve_region_var(rid),
|
ty::re_infer(ty::ReVar(rid)) => self.resolve_region_var(rid),
|
||||||
_ => orig
|
_ => orig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_region_var(rid: RegionVid) -> ty::Region {
|
fn resolve_region_var(rid: RegionVid) -> ty::Region {
|
||||||
if !self.should(resolve_rvar) {
|
if !self.should(resolve_rvar) {
|
||||||
return ty::re_var(rid)
|
return ty::re_infer(ty::ReVar(rid));
|
||||||
}
|
}
|
||||||
self.infcx.region_vars.resolve_var(rid)
|
self.infcx.region_vars.resolve_var(rid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_not_rvar(rid: RegionVid, r: ty::Region) {
|
fn assert_not_rvar(rid: RegionVid, r: ty::Region) {
|
||||||
match r {
|
match r {
|
||||||
ty::re_var(rid2) => {
|
ty::re_infer(ty::ReVar(rid2)) => {
|
||||||
self.err = Some(region_var_bound_by_region_var(rid, rid2));
|
self.err = Some(region_var_bound_by_region_var(rid, rid2));
|
||||||
}
|
}
|
||||||
_ => { }
|
_ => { }
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use combine::*;
|
use combine::*;
|
||||||
use unify::*;
|
use unify::*;
|
||||||
use to_str::ToStr;
|
use to_str::ToStr;
|
||||||
|
use std::list;
|
||||||
|
|
||||||
|
fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
|
||||||
|
|
||||||
enum Sub = combine_fields; // "subtype", "subregion" etc
|
enum Sub = combine_fields; // "subtype", "subregion" etc
|
||||||
|
|
||||||
|
@ -125,10 +128,21 @@ impl Sub: combine {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres<ty::FnTy> {
|
fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres<ty::FnTy> {
|
||||||
|
debug!("fns(a=%s, b=%s)", a.to_str(self.infcx), b.to_str(self.infcx));
|
||||||
|
let _indenter = indenter();
|
||||||
|
|
||||||
// Rather than checking the subtype relationship between `a` and `b`
|
// Rather than checking the subtype relationship between `a` and `b`
|
||||||
// as-is, we need to do some extra work here in order to make sure
|
// as-is, we need to do some extra work here in order to make sure
|
||||||
// that function subtyping works correctly with respect to regions
|
// that function subtyping works correctly with respect to regions
|
||||||
// (issue #2263).
|
//
|
||||||
|
// A rather detailed discussion of what's going on here can be
|
||||||
|
// found in the region_inference.rs module.
|
||||||
|
|
||||||
|
// Take a snapshot. We'll never roll this back, but in later
|
||||||
|
// phases we do want to be able to examine "all bindings that
|
||||||
|
// were created as part of this type comparison", and making a
|
||||||
|
// snapshot is a convenient way to do that.
|
||||||
|
let snapshot = self.infcx.region_vars.start_snapshot();
|
||||||
|
|
||||||
// First, we instantiate each bound region in the subtype with a fresh
|
// First, we instantiate each bound region in the subtype with a fresh
|
||||||
// region variable.
|
// region variable.
|
||||||
|
@ -140,26 +154,50 @@ impl Sub: combine {
|
||||||
// for it. The only thing we're doing with `br` here is
|
// for it. The only thing we're doing with `br` here is
|
||||||
// using it in the debug message.
|
// using it in the debug message.
|
||||||
let rvar = self.infcx.next_region_var_nb(self.span);
|
let rvar = self.infcx.next_region_var_nb(self.span);
|
||||||
debug!("Bound region %s maps to %s",
|
debug!("Bound region %s maps to %?",
|
||||||
bound_region_to_str(self.infcx.tcx, br),
|
bound_region_to_str(self.infcx.tcx, br),
|
||||||
region_to_str(self.infcx.tcx, rvar));
|
rvar);
|
||||||
rvar
|
rvar
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Second, we instantiate each bound region in the supertype with a
|
// Second, we instantiate each bound region in the supertype with a
|
||||||
// fresh concrete region.
|
// fresh concrete region.
|
||||||
let {fn_ty: b_fn_ty, _} = {
|
let {fn_ty: b_fn_ty, isr: skol_isr, _} = {
|
||||||
do replace_bound_regions_in_fn_ty(self.infcx.tcx, @Nil,
|
do replace_bound_regions_in_fn_ty(self.infcx.tcx, @Nil,
|
||||||
None, b) |br| {
|
None, b) |br| {
|
||||||
// FIXME: eventually re_skolemized (issue #2263)
|
let skol = self.infcx.region_vars.new_skolemized(br);
|
||||||
ty::re_bound(br)
|
debug!("Bound region %s skolemized to %?",
|
||||||
|
bound_region_to_str(self.infcx.tcx, br),
|
||||||
|
skol);
|
||||||
|
skol
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try to compare the supertype and subtype now that they've been
|
debug!("a_fn_ty=%s", a_fn_ty.to_str(self.infcx));
|
||||||
// instantiated.
|
debug!("b_fn_ty=%s", b_fn_ty.to_str(self.infcx));
|
||||||
super_fns(&self, &a_fn_ty, &b_fn_ty)
|
|
||||||
|
// Compare types now that bound regions have been replaced.
|
||||||
|
let fn_ty = if_ok!(super_fns(&self, &a_fn_ty, &b_fn_ty));
|
||||||
|
|
||||||
|
// Presuming type comparison succeeds, we need to check
|
||||||
|
// that the skolemized regions do not "leak".
|
||||||
|
for list::each(skol_isr) |pair| {
|
||||||
|
let (skol_br, skol) = *pair;
|
||||||
|
let tainted = self.infcx.region_vars.tainted(snapshot, skol);
|
||||||
|
for tainted.each |tainted_region| {
|
||||||
|
// A is not as polymorphic as B:
|
||||||
|
if self.a_is_expected {
|
||||||
|
return Err(ty::terr_regions_insufficiently_polymorphic(
|
||||||
|
skol_br, *tainted_region));
|
||||||
|
} else {
|
||||||
|
return Err(ty::terr_regions_overly_polymorphic(
|
||||||
|
skol_br, *tainted_region));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(fn_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traits please (FIXME: #2794):
|
// Traits please (FIXME: #2794):
|
||||||
|
|
|
@ -23,6 +23,12 @@ impl ty::Region: ToStr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ty::FnTy: ToStr {
|
||||||
|
fn to_str(cx: infer_ctxt) -> ~str {
|
||||||
|
ty::mk_fn(cx.tcx, self).to_str(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<V:Copy ToStr> bound<V>: ToStr {
|
impl<V:Copy ToStr> bound<V>: ToStr {
|
||||||
fn to_str(cx: infer_ctxt) -> ~str {
|
fn to_str(cx: infer_ctxt) -> ~str {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -133,7 +133,7 @@ mod middle {
|
||||||
#[legacy_exports]
|
#[legacy_exports]
|
||||||
mod lub;
|
mod lub;
|
||||||
#[legacy_exports]
|
#[legacy_exports]
|
||||||
mod region_var_bindings;
|
mod region_inference;
|
||||||
#[legacy_exports]
|
#[legacy_exports]
|
||||||
mod resolve;
|
mod resolve;
|
||||||
#[legacy_exports]
|
#[legacy_exports]
|
||||||
|
|
|
@ -6,7 +6,8 @@ use middle::ty::{bound_copy, bound_const, bound_owned, bound_send,
|
||||||
use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid};
|
use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid};
|
||||||
use middle::ty::{ck_block, ck_box, ck_uniq, ctxt, field, method};
|
use middle::ty::{ck_block, ck_box, ck_uniq, ctxt, field, method};
|
||||||
use middle::ty::{mt, t, param_bound};
|
use middle::ty::{mt, t, param_bound};
|
||||||
use middle::ty::{re_bound, re_free, re_scope, re_var, re_static, Region};
|
use middle::ty::{re_bound, re_free, re_scope, re_infer, re_static, Region};
|
||||||
|
use middle::ty::{ReSkolemized, ReVar};
|
||||||
use middle::ty::{ty_bool, ty_bot, ty_box, ty_class, ty_enum};
|
use middle::ty::{ty_bool, ty_bot, ty_box, ty_class, ty_enum};
|
||||||
use middle::ty::{ty_estr, ty_evec, ty_float, ty_fn, ty_trait, ty_int};
|
use middle::ty::{ty_estr, ty_evec, ty_float, ty_fn, ty_trait, ty_int};
|
||||||
use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param};
|
use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param};
|
||||||
|
@ -95,7 +96,7 @@ fn explain_region_and_span(cx: ctxt, region: ty::Region)
|
||||||
|
|
||||||
// I believe these cases should not occur (except when debugging,
|
// I believe these cases should not occur (except when debugging,
|
||||||
// perhaps)
|
// perhaps)
|
||||||
re_var(_) | re_bound(_) => {
|
re_infer(_) | re_bound(_) => {
|
||||||
(fmt!("lifetime %?", region), None)
|
(fmt!("lifetime %?", region), None)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -184,8 +185,9 @@ fn region_to_str(cx: ctxt, region: Region) -> ~str {
|
||||||
re_scope(_) => ~"&",
|
re_scope(_) => ~"&",
|
||||||
re_bound(br) => bound_region_to_str(cx, br),
|
re_bound(br) => bound_region_to_str(cx, br),
|
||||||
re_free(_, br) => bound_region_to_str(cx, br),
|
re_free(_, br) => bound_region_to_str(cx, br),
|
||||||
re_var(_) => ~"&",
|
re_infer(ReSkolemized(_, br)) => bound_region_to_str(cx, br),
|
||||||
re_static => ~"&static"
|
re_infer(ReVar(_)) => ~"&",
|
||||||
|
re_static => ~"&static"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,47 @@
|
||||||
// Here, `f` is a function that takes a pointer `x` and a function
|
fn of<T>() -> @fn(T) { fail; }
|
||||||
// `g`, where `g` requires its argument `y` to be in the same region
|
fn subtype<T>(x: @fn(T)) { fail; }
|
||||||
// that `x` is in.
|
|
||||||
fn has_same_region(f: fn(x: &a/int, g: fn(y: &a/int))) {
|
fn test_fn<T>(_x: &x/T, _y: &y/T, _z: &z/T) {
|
||||||
// Somewhat counterintuitively, this fails because, in
|
// Here, x, y, and z are free. Other letters
|
||||||
// `wants_two_regions`, the `g` argument needs to be able to
|
// are bound. Note that the arrangement
|
||||||
// accept any region. That is, the type that `has_same_region`
|
// subtype::<T1>(of::<T2>()) will typecheck
|
||||||
// expects is *not* a subtype of the type that `wants_two_regions`
|
// iff T1 <: T2.
|
||||||
// expects.
|
|
||||||
wants_two_regions(f); //~ ERROR mismatched types
|
subtype::<fn(&a/T)>(
|
||||||
|
of::<fn(&a/T)>());
|
||||||
|
|
||||||
|
subtype::<fn(&a/T)>(
|
||||||
|
of::<fn(&b/T)>());
|
||||||
|
|
||||||
|
subtype::<fn(&b/T)>(
|
||||||
|
of::<fn(&x/T)>());
|
||||||
|
|
||||||
|
subtype::<fn(&x/T)>(
|
||||||
|
of::<fn(&b/T)>()); //~ ERROR mismatched types
|
||||||
|
|
||||||
|
subtype::<fn(&a/T, &b/T)>(
|
||||||
|
of::<fn(&a/T, &a/T)>());
|
||||||
|
|
||||||
|
subtype::<fn(&a/T, &a/T)>(
|
||||||
|
of::<fn(&a/T, &b/T)>()); //~ ERROR mismatched types
|
||||||
|
|
||||||
|
subtype::<fn(&a/T, &b/T)>(
|
||||||
|
of::<fn(&x/T, &y/T)>());
|
||||||
|
|
||||||
|
subtype::<fn(&x/T, &y/T)>(
|
||||||
|
of::<fn(&a/T, &b/T)>()); //~ ERROR mismatched types
|
||||||
|
|
||||||
|
subtype::<fn(&x/T) -> @fn(&a/T)>(
|
||||||
|
of::<fn(&x/T) -> @fn(&a/T)>());
|
||||||
|
|
||||||
|
subtype::<fn(&a/T) -> @fn(&a/T)>(
|
||||||
|
of::<fn(&a/T) -> @fn(&b/T)>()); //~ ERROR mismatched types
|
||||||
|
|
||||||
|
subtype::<fn(&a/T) -> @fn(&a/T)>(
|
||||||
|
of::<fn(&x/T) -> @fn(&b/T)>()); //~ ERROR mismatched types
|
||||||
|
|
||||||
|
subtype::<fn(&a/T) -> @fn(&b/T)>(
|
||||||
|
of::<fn(&a/T) -> @fn(&a/T)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wants_two_regions(_f: fn(x: &int, g: fn(y: &int))) {
|
fn main() {}
|
||||||
// Suppose we were to write code here that passed some arbitrary
|
|
||||||
// &int and some arbitrary fn(&int) to whatever's passed in as _f.
|
|
||||||
// This would be fine as far as the type annotation on the formal
|
|
||||||
// parameter _f goes, but if _f were `f` we'd be in trouble since
|
|
||||||
// `f` can't handle those arguments.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue