mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-31 06:22:00 +00:00
new region inference, seperate infer into modules, improve error msgs
Fixes #2806 Fixes #3197 Fixes #3138
This commit is contained in:
parent
3b09c3deaa
commit
8ee79c79aa
@ -90,7 +90,7 @@ priv impl<A> DVec<A> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn borrow<B>(f: fn(-~[mut A]) -> B) -> B {
|
||||
fn check_out<B>(f: fn(-~[mut A]) -> B) -> B {
|
||||
unsafe {
|
||||
let mut data = unsafe::reinterpret_cast(null::<()>());
|
||||
data <-> self.data;
|
||||
@ -124,13 +124,13 @@ impl<A> DVec<A> {
|
||||
*/
|
||||
#[inline(always)]
|
||||
fn swap(f: fn(-~[mut A]) -> ~[mut A]) {
|
||||
self.borrow(|v| self.give_back(f(v)))
|
||||
self.check_out(|v| self.give_back(f(v)))
|
||||
}
|
||||
|
||||
/// Returns the number of elements currently in the dvec
|
||||
pure fn len() -> uint {
|
||||
unchecked {
|
||||
do self.borrow |v| {
|
||||
do self.check_out |v| {
|
||||
let l = v.len();
|
||||
self.give_back(v);
|
||||
l
|
||||
@ -146,7 +146,7 @@ impl<A> DVec<A> {
|
||||
|
||||
/// Remove and return the last element
|
||||
fn pop() -> A {
|
||||
do self.borrow |v| {
|
||||
do self.check_out |v| {
|
||||
let mut v <- v;
|
||||
let result = vec::pop(v);
|
||||
self.give_back(v);
|
||||
@ -176,7 +176,7 @@ impl<A> DVec<A> {
|
||||
|
||||
/// Remove and return the first element
|
||||
fn shift() -> A {
|
||||
do self.borrow |v| {
|
||||
do self.check_out |v| {
|
||||
let mut v = vec::from_mut(v);
|
||||
let result = vec::shift(v);
|
||||
self.give_back(vec::to_mut(v));
|
||||
@ -184,10 +184,29 @@ impl<A> DVec<A> {
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the elements in the list, in place
|
||||
/// Reverse the elements in the list, in place
|
||||
fn reverse() {
|
||||
do self.borrow |v| {
|
||||
do self.check_out |v| {
|
||||
vec::reverse(v);
|
||||
self.give_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives access to the vector as a slice with immutable contents
|
||||
fn borrow<R>(op: fn(x: &[A]) -> R) -> R {
|
||||
do self.check_out |v| {
|
||||
let result = op(v);
|
||||
self.give_back(v);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives access to the vector as a slice with mutable contents
|
||||
fn borrow_mut<R>(op: fn(x: &[mut A]) -> R) -> R {
|
||||
do self.check_out |v| {
|
||||
let result = op(v);
|
||||
self.give_back(v);
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,7 +268,7 @@ impl<A: copy> DVec<A> {
|
||||
*/
|
||||
pure fn get() -> ~[A] {
|
||||
unchecked {
|
||||
do self.borrow |v| {
|
||||
do self.check_out |v| {
|
||||
let w = vec::from_mut(copy v);
|
||||
self.give_back(v);
|
||||
w
|
||||
|
@ -1187,7 +1187,9 @@ fn spawn_raw(+opts: TaskOpts, +f: fn~()) {
|
||||
};
|
||||
if result {
|
||||
// Unwinding function in case any ancestral enlisting fails
|
||||
let bail = |tg| { leave_taskgroup(tg, child, false) };
|
||||
let bail = |tg: TaskGroupInner| {
|
||||
leave_taskgroup(tg, child, false)
|
||||
};
|
||||
// Attempt to join every ancestor group.
|
||||
result =
|
||||
for each_ancestor(ancestors, some(bail)) |ancestor_tg| {
|
||||
|
@ -41,10 +41,11 @@ impl<T> Cell<T> {
|
||||
}
|
||||
|
||||
// Calls a closure with a reference to the value.
|
||||
fn with_ref(f: fn(v: &T)) {
|
||||
let val = move self.take();
|
||||
f(&val);
|
||||
self.put_back(move val);
|
||||
fn with_ref<R>(op: fn(v: &T) -> R) -> R {
|
||||
let v = self.take();
|
||||
let r = op(&v);
|
||||
self.put_back(v);
|
||||
return move r;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ import middle::lint;
|
||||
import middle::lint::{get_lint_level, allow};
|
||||
import syntax::ast::*;
|
||||
import syntax::print::pprust::*;
|
||||
import util::ppaux::{ty_to_str, tys_to_str};
|
||||
import util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str};
|
||||
|
||||
export tv_vid, tvi_vid, region_vid, vid;
|
||||
export br_hashmap;
|
||||
@ -119,6 +119,7 @@ export proto_kind, kind_lteq, type_kind;
|
||||
export operators;
|
||||
export type_err, terr_vstore_kind;
|
||||
export type_err_to_str;
|
||||
export expected_found;
|
||||
export type_needs_drop;
|
||||
export type_is_empty;
|
||||
export type_is_integral;
|
||||
@ -179,6 +180,7 @@ export fn_proto, proto_bare, proto_vstore;
|
||||
export ast_proto_to_proto;
|
||||
export is_blockish;
|
||||
export method_call_bounds;
|
||||
export hash_region;
|
||||
|
||||
// Data types
|
||||
|
||||
@ -368,7 +370,7 @@ enum region {
|
||||
re_static,
|
||||
|
||||
/// A region variable. Should not exist after typeck.
|
||||
re_var(region_vid),
|
||||
re_var(region_vid)
|
||||
}
|
||||
|
||||
enum bound_region {
|
||||
@ -455,30 +457,35 @@ enum terr_vstore_kind {
|
||||
terr_vec, terr_str, terr_fn, terr_trait
|
||||
}
|
||||
|
||||
struct expected_found<T> {
|
||||
expected: T;
|
||||
found: T;
|
||||
}
|
||||
|
||||
// Data structures used in type unification
|
||||
enum type_err {
|
||||
terr_mismatch,
|
||||
terr_ret_style_mismatch(ast::ret_style, ast::ret_style),
|
||||
terr_purity_mismatch(purity, purity),
|
||||
terr_ret_style_mismatch(expected_found<ast::ret_style>),
|
||||
terr_purity_mismatch(expected_found<purity>),
|
||||
terr_mutability,
|
||||
terr_proto_mismatch(ty::fn_proto, ty::fn_proto),
|
||||
terr_proto_mismatch(expected_found<ty::fn_proto>),
|
||||
terr_box_mutability,
|
||||
terr_ptr_mutability,
|
||||
terr_ref_mutability,
|
||||
terr_vec_mutability,
|
||||
terr_tuple_size(uint, uint),
|
||||
terr_ty_param_size(uint, uint),
|
||||
terr_record_size(uint, uint),
|
||||
terr_tuple_size(expected_found<uint>),
|
||||
terr_ty_param_size(expected_found<uint>),
|
||||
terr_record_size(expected_found<uint>),
|
||||
terr_record_mutability,
|
||||
terr_record_fields(ast::ident, ast::ident),
|
||||
terr_record_fields(expected_found<ident>),
|
||||
terr_arg_count,
|
||||
terr_mode_mismatch(mode, mode),
|
||||
terr_mode_mismatch(expected_found<mode>),
|
||||
terr_regions_does_not_outlive(region, region),
|
||||
terr_regions_not_same(region, region),
|
||||
terr_regions_no_overlap(region, region),
|
||||
terr_vstores_differ(terr_vstore_kind, vstore, vstore),
|
||||
terr_vstores_differ(terr_vstore_kind, expected_found<vstore>),
|
||||
terr_in_field(@type_err, ast::ident),
|
||||
terr_sorts(t, t),
|
||||
terr_sorts(expected_found<t>),
|
||||
terr_self_substs,
|
||||
terr_no_integral_type,
|
||||
}
|
||||
@ -512,7 +519,7 @@ impl tvi_vid: vid {
|
||||
|
||||
impl region_vid: vid {
|
||||
pure fn to_uint() -> uint { *self }
|
||||
pure fn to_str() -> ~str { fmt!{"<R%u>", self.to_uint()} }
|
||||
pure fn to_str() -> ~str { fmt!{"%?", self} }
|
||||
}
|
||||
|
||||
trait purity_to_str {
|
||||
@ -2195,6 +2202,17 @@ fn br_hashmap<V:copy>() -> hashmap<bound_region, V> {
|
||||
map::hashmap(hash_bound_region, sys::shape_eq)
|
||||
}
|
||||
|
||||
pure fn hash_region(r: ®ion) -> uint {
|
||||
match *r { // no idea if this is any good
|
||||
re_bound(br) => (hash_bound_region(&br)) << 2u | 0u,
|
||||
re_free(id, br) => ((id as uint) << 4u) |
|
||||
(hash_bound_region(&br)) << 2u | 1u,
|
||||
re_scope(id) => ((id as uint) << 2u) | 2u,
|
||||
re_var(id) => (id.to_uint() << 2u) | 3u,
|
||||
re_bot => 4u
|
||||
}
|
||||
}
|
||||
|
||||
// Type hashing.
|
||||
pure fn hash_type_structure(st: &sty) -> uint {
|
||||
pure fn hash_uint(id: uint, n: uint) -> uint { (id << 2u) + n }
|
||||
@ -2210,16 +2228,6 @@ pure fn hash_type_structure(st: &sty) -> uint {
|
||||
for vec::each(subtys) |s| { h = (h << 2u) + type_id(s) }
|
||||
h
|
||||
}
|
||||
pure fn hash_region(r: ®ion) -> uint {
|
||||
match *r { // no idea if this is any good
|
||||
re_bound(br) => (hash_bound_region(&br)) << 2u | 0u,
|
||||
re_free(id, br) => ((id as uint) << 4u) |
|
||||
(hash_bound_region(&br)) << 2u | 1u,
|
||||
re_scope(id) => ((id as uint) << 2u) | 2u,
|
||||
re_var(id) => (id.to_uint() << 2u) | 3u,
|
||||
re_bot => 4u
|
||||
}
|
||||
}
|
||||
pure fn hash_substs(h: uint, substs: &substs) -> uint {
|
||||
let h = hash_subtys(h, substs.tps);
|
||||
h + substs.self_r.map_default(0u, |r| hash_region(&r))
|
||||
@ -2569,8 +2577,11 @@ fn resolved_mode(cx: ctxt, m: ast::mode) -> ast::rmode {
|
||||
fn arg_mode(cx: ctxt, a: arg) -> ast::rmode { resolved_mode(cx, a.mode) }
|
||||
|
||||
// Unifies `m1` and `m2`. Returns unified value or failure code.
|
||||
fn unify_mode(cx: ctxt, m1: ast::mode, m2: ast::mode)
|
||||
fn unify_mode(cx: ctxt, modes: expected_found<ast::mode>)
|
||||
-> result<ast::mode, type_err> {
|
||||
|
||||
let m1 = modes.expected;
|
||||
let m2 = modes.found;
|
||||
match (canon_mode(cx, m1), canon_mode(cx, m2)) {
|
||||
(m1, m2) if (m1 == m2) => {
|
||||
result::ok(m1)
|
||||
@ -2584,7 +2595,7 @@ fn unify_mode(cx: ctxt, m1: ast::mode, m2: ast::mode)
|
||||
result::ok(m1)
|
||||
}
|
||||
(m1, m2) => {
|
||||
result::err(terr_mode_mismatch(m1, m2))
|
||||
result::err(terr_mode_mismatch(modes))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2638,91 +2649,96 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
|
||||
}
|
||||
|
||||
match *err {
|
||||
terr_mismatch => return ~"types differ",
|
||||
terr_ret_style_mismatch(expect, actual) => {
|
||||
terr_mismatch => ~"types differ",
|
||||
terr_ret_style_mismatch(values) => {
|
||||
fn to_str(s: ast::ret_style) -> ~str {
|
||||
match s {
|
||||
ast::noreturn => ~"non-returning",
|
||||
ast::return_val => ~"return-by-value"
|
||||
}
|
||||
}
|
||||
return to_str(actual) + ~" function found where " + to_str(expect) +
|
||||
~" function was expected";
|
||||
fmt!("expected %s function, found %s function",
|
||||
to_str(values.expected),
|
||||
to_str(values.expected))
|
||||
}
|
||||
terr_purity_mismatch(f1, f2) => {
|
||||
return fmt!{"expected %s fn but found %s fn",
|
||||
purity_to_str(f1), purity_to_str(f2)};
|
||||
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(e, a) => {
|
||||
return fmt!{"closure protocol mismatch (%s vs %s)",
|
||||
util::ppaux::proto_ty_to_str(cx, e),
|
||||
util::ppaux::proto_ty_to_str(cx, a)};
|
||||
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 => return ~"values differ in mutability",
|
||||
terr_box_mutability => return ~"boxed values differ in mutability",
|
||||
terr_vec_mutability => return ~"vectors differ in mutability",
|
||||
terr_ptr_mutability => return ~"pointers differ in mutability",
|
||||
terr_ref_mutability => return ~"references differ in mutability",
|
||||
terr_ty_param_size(e_sz, a_sz) => {
|
||||
return ~"expected a type with " + uint::to_str(e_sz, 10u) +
|
||||
~" type params but found one with " + uint::to_str(a_sz, 10u) +
|
||||
~" type params";
|
||||
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(e_sz, a_sz) => {
|
||||
return ~"expected a tuple with " + uint::to_str(e_sz, 10u) +
|
||||
~" elements but found one with " + uint::to_str(a_sz, 10u) +
|
||||
~" elements";
|
||||
terr_tuple_size(values) => {
|
||||
fmt!("expected a tuple with %? elements \
|
||||
but found one with %? elements",
|
||||
values.expected, values.found)
|
||||
}
|
||||
terr_record_size(e_sz, a_sz) => {
|
||||
return ~"expected a record with " + uint::to_str(e_sz, 10u) +
|
||||
~" fields but found one with " + uint::to_str(a_sz, 10u) +
|
||||
~" fields";
|
||||
terr_record_size(values) => {
|
||||
fmt!("expected a record with %? fields \
|
||||
but found one with %? fields",
|
||||
values.expected, values.found)
|
||||
}
|
||||
terr_record_mutability => {
|
||||
return ~"record elements differ in mutability";
|
||||
~"record elements differ in mutability"
|
||||
}
|
||||
terr_record_fields(e_fld, a_fld) => {
|
||||
return ~"expected a record with field `" + *e_fld +
|
||||
~"` but found one with field `" + *a_fld + ~"`";
|
||||
terr_record_fields(values) => {
|
||||
fmt!("expected a record with field `%s` \
|
||||
but found one with field `%s`",
|
||||
*values.expected, *values.found)
|
||||
}
|
||||
terr_arg_count => return ~"incorrect number of function parameters",
|
||||
terr_mode_mismatch(e_mode, a_mode) => {
|
||||
return ~"expected argument mode " + mode_to_str(e_mode) +
|
||||
~" but found " + mode_to_str(a_mode);
|
||||
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(subregion, superregion) => {
|
||||
return fmt!{"%s does not necessarily outlive %s",
|
||||
explain_region(cx, subregion),
|
||||
explain_region(cx, superregion)};
|
||||
fmt!{"%s does not necessarily outlive %s",
|
||||
explain_region(cx, superregion),
|
||||
explain_region(cx, subregion)}
|
||||
}
|
||||
terr_regions_not_same(region1, region2) => {
|
||||
return fmt!{"%s is not the same as %s",
|
||||
explain_region(cx, region1),
|
||||
explain_region(cx, region2)};
|
||||
fmt!{"%s is not the same as %s",
|
||||
explain_region(cx, region1),
|
||||
explain_region(cx, region2)}
|
||||
}
|
||||
terr_regions_no_overlap(region1, region2) => {
|
||||
return fmt!{"%s does not intersect %s",
|
||||
explain_region(cx, region1),
|
||||
explain_region(cx, region2)};
|
||||
fmt!("%s does not intersect %s",
|
||||
explain_region(cx, region1),
|
||||
explain_region(cx, region2))
|
||||
}
|
||||
terr_vstores_differ(k, e_vs, a_vs) => {
|
||||
return fmt!{"%s storage differs: expected %s but found %s",
|
||||
terr_vstore_kind_to_str(k),
|
||||
vstore_to_str(cx, e_vs),
|
||||
vstore_to_str(cx, a_vs)};
|
||||
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) => {
|
||||
return fmt!{"in field `%s`, %s", *fname, type_err_to_str(cx, err)};
|
||||
fmt!{"in field `%s`, %s", *fname, type_err_to_str(cx, err)}
|
||||
}
|
||||
terr_sorts(exp, act) => {
|
||||
return fmt!{"%s vs %s", ty_sort_str(cx, exp), ty_sort_str(cx, act)};
|
||||
terr_sorts(values) => {
|
||||
fmt!{"expected %s but found %s",
|
||||
ty_sort_str(cx, values.expected),
|
||||
ty_sort_str(cx, values.found)}
|
||||
}
|
||||
terr_self_substs => {
|
||||
return ~"inconsistent self substitution"; // XXX this is more of a bug
|
||||
~"inconsistent self substitution" // XXX this is more of a bug
|
||||
}
|
||||
terr_no_integral_type => {
|
||||
return ~"couldn't determine an appropriate integral type for integer \
|
||||
literal";
|
||||
~"couldn't determine an appropriate integral type for integer \
|
||||
literal"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +206,7 @@ fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
|
||||
fn require_same_types(
|
||||
tcx: ty::ctxt,
|
||||
maybe_infcx: option<infer::infer_ctxt>,
|
||||
t1_is_expected: bool,
|
||||
span: span,
|
||||
t1: ty::t,
|
||||
t2: ty::t,
|
||||
@ -223,7 +224,7 @@ fn require_same_types(
|
||||
}
|
||||
}
|
||||
|
||||
match infer::mk_eqty(l_infcx, t1, t2) {
|
||||
match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
|
||||
result::ok(()) => true,
|
||||
result::err(ref terr) => {
|
||||
l_tcx.sess.span_err(span, msg() + ~": " +
|
||||
|
@ -72,8 +72,8 @@ fn ast_region_to_region<AC: ast_conv, RS: region_scope copy owned>(
|
||||
self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region {
|
||||
|
||||
let res = match a_r.node {
|
||||
ast::re_anon => rscope.anon_region(),
|
||||
ast::re_named(id) => rscope.named_region(id)
|
||||
ast::re_anon => rscope.anon_region(span),
|
||||
ast::re_named(id) => rscope.named_region(span, id)
|
||||
};
|
||||
|
||||
get_region_reporting_err(self.tcx(), span, res)
|
||||
@ -106,7 +106,7 @@ fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy owned>(
|
||||
none
|
||||
}
|
||||
(true, none) => {
|
||||
let res = rscope.anon_region();
|
||||
let res = rscope.anon_region(path.span);
|
||||
let r = get_region_reporting_err(self.tcx(), path.span, res);
|
||||
some(r)
|
||||
}
|
||||
@ -409,8 +409,10 @@ fn ty_of_arg<AC: ast_conv, RS: region_scope copy owned>(
|
||||
let mode = {
|
||||
match a.mode {
|
||||
ast::infer(_) if expected_ty.is_some() => {
|
||||
result::get(ty::unify_mode(self.tcx(), a.mode,
|
||||
expected_ty.get().mode))
|
||||
result::get(ty::unify_mode(
|
||||
self.tcx(),
|
||||
ty::expected_found {expected: expected_ty.get().mode,
|
||||
found: a.mode}))
|
||||
}
|
||||
ast::infer(_) => {
|
||||
match ty::get(ty).struct {
|
||||
@ -425,7 +427,10 @@ fn ty_of_arg<AC: ast_conv, RS: region_scope copy owned>(
|
||||
// will have been unified with m yet:
|
||||
_ => {
|
||||
let m1 = ast::expl(ty::default_arg_mode_for_ty(ty));
|
||||
result::get(ty::unify_mode(self.tcx(), a.mode, m1))
|
||||
result::get(ty::unify_mode(
|
||||
self.tcx(),
|
||||
ty::expected_found {expected: m1,
|
||||
found: a.mode}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -446,7 +451,7 @@ fn ast_proto_to_proto<AC: ast_conv, RS: region_scope copy owned>(
|
||||
ast::proto_box =>
|
||||
ty::proto_vstore(ty::vstore_box),
|
||||
ast::proto_block => {
|
||||
let result = rscope.anon_region();
|
||||
let result = rscope.anon_region(span);
|
||||
let region = get_region_reporting_err(self.tcx(), span, result);
|
||||
ty::proto_vstore(ty::vstore_slice(region))
|
||||
}
|
||||
|
@ -102,12 +102,12 @@ type fn_ctxt_ =
|
||||
// the end of (almost) any enclosing block or expression. We
|
||||
// want to pick the narrowest block that encompasses all uses.
|
||||
//
|
||||
// What we do in such cases is to generate a region variable and
|
||||
// assign it the following two fields as bounds. The lower bound
|
||||
// is always the innermost enclosing expression. The upper bound
|
||||
// is the outermost enclosing expression that we could legally
|
||||
// use. In practice, this is the innermost loop or function
|
||||
// body.
|
||||
// What we do in such cases is to generate a region variable with
|
||||
// `region_lb` as a lower bound. The regionck pass then adds
|
||||
// other constriants based on how the variable is used and region
|
||||
// inference selects the ultimate value. Finally, borrowck is
|
||||
// charged with guaranteeing that the value whose address was taken
|
||||
// can actually be made to live as long as it needs to live.
|
||||
mut region_lb: ast::node_id,
|
||||
|
||||
in_scope_regions: isr_alist,
|
||||
@ -292,20 +292,22 @@ fn check_fn(ccx: @crate_ctxt,
|
||||
arg_tys: ~[ty::t]) {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
|
||||
let assign = fn@(nid: ast::node_id, ty_opt: option<ty::t>) {
|
||||
let assign = fn@(span: span, nid: ast::node_id,
|
||||
ty_opt: option<ty::t>) {
|
||||
let var_id = fcx.infcx.next_ty_var_id();
|
||||
fcx.locals.insert(nid, var_id);
|
||||
match ty_opt {
|
||||
none => {/* nothing to do */ }
|
||||
some(typ) => {
|
||||
infer::mk_eqty(fcx.infcx, ty::mk_var(tcx, var_id), typ);
|
||||
infer::mk_eqty(fcx.infcx, false, span,
|
||||
ty::mk_var(tcx, var_id), typ);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add formal parameters.
|
||||
do vec::iter2(arg_tys, decl.inputs) |arg_ty, input| {
|
||||
assign(input.id, some(arg_ty));
|
||||
assign(input.ty.span, input.id, some(arg_ty));
|
||||
debug!{"Argument %s is assigned to %s",
|
||||
*input.ident, fcx.locals.get(input.id).to_str()};
|
||||
}
|
||||
@ -317,7 +319,7 @@ fn check_fn(ccx: @crate_ctxt,
|
||||
ast::ty_infer => none,
|
||||
_ => some(fcx.to_ty(local.node.ty))
|
||||
};
|
||||
assign(local.node.id, o_ty);
|
||||
assign(local.span, local.node.id, o_ty);
|
||||
debug!{"Local variable %s is assigned to %s",
|
||||
pat_to_str(local.node.pat),
|
||||
fcx.locals.get(local.node.id).to_str()};
|
||||
@ -329,7 +331,7 @@ fn check_fn(ccx: @crate_ctxt,
|
||||
match p.node {
|
||||
ast::pat_ident(_, path, _)
|
||||
if !pat_util::pat_is_variant(fcx.ccx.tcx.def_map, p) => {
|
||||
assign(p.id, none);
|
||||
assign(p.span, p.id, none);
|
||||
debug!{"Pattern binding %s is assigned to %s",
|
||||
*path.idents[0],
|
||||
fcx.locals.get(p.id).to_str()};
|
||||
@ -525,14 +527,14 @@ impl @fn_ctxt: ast_conv {
|
||||
}
|
||||
|
||||
impl @fn_ctxt: region_scope {
|
||||
fn anon_region() -> result<ty::region, ~str> {
|
||||
result::ok(self.infcx.next_region_var_nb())
|
||||
fn anon_region(span: span) -> result<ty::region, ~str> {
|
||||
result::ok(self.infcx.next_region_var_nb(span))
|
||||
}
|
||||
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
|
||||
do empty_rscope.named_region(id).chain_err |_e| {
|
||||
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
|
||||
do empty_rscope.named_region(span, id).chain_err |_e| {
|
||||
match self.in_scope_regions.find(ty::br_named(id)) {
|
||||
some(r) => result::ok(r),
|
||||
none if *id == ~"blk" => self.block_region(),
|
||||
none if *id == ~"blk" => result::ok(self.block_region()),
|
||||
none => {
|
||||
result::err(fmt!{"named region `%s` not in scope here", *id})
|
||||
}
|
||||
@ -543,8 +545,8 @@ impl @fn_ctxt: region_scope {
|
||||
|
||||
impl @fn_ctxt {
|
||||
fn tag() -> ~str { fmt!{"%x", ptr::addr_of(*self) as uint} }
|
||||
fn block_region() -> result<ty::region, ~str> {
|
||||
result::ok(ty::re_scope(self.region_lb))
|
||||
fn block_region() -> ty::region {
|
||||
ty::re_scope(self.region_lb)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn write_ty(node_id: ast::node_id, ty: ty::t) {
|
||||
@ -619,8 +621,9 @@ impl @fn_ctxt {
|
||||
ty::type_err_to_str(self.ccx.tcx, err)});
|
||||
}
|
||||
|
||||
fn mk_subty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
|
||||
infer::mk_subty(self.infcx, sub, sup)
|
||||
fn mk_subty(a_is_expected: bool, span: span,
|
||||
sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
|
||||
infer::mk_subty(self.infcx, a_is_expected, span, sub, sup)
|
||||
}
|
||||
|
||||
fn can_mk_subty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
|
||||
@ -641,12 +644,14 @@ impl @fn_ctxt {
|
||||
infer::can_mk_assignty(self.infcx, anmnt, sub, sup)
|
||||
}
|
||||
|
||||
fn mk_eqty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
|
||||
infer::mk_eqty(self.infcx, sub, sup)
|
||||
fn mk_eqty(a_is_expected: bool, span: span,
|
||||
sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
|
||||
infer::mk_eqty(self.infcx, a_is_expected, span, sub, sup)
|
||||
}
|
||||
|
||||
fn mk_subr(sub: ty::region, sup: ty::region) -> result<(), ty::type_err> {
|
||||
infer::mk_subr(self.infcx, sub, sup)
|
||||
fn mk_subr(a_is_expected: bool, span: span,
|
||||
sub: ty::region, sup: ty::region) -> result<(), ty::type_err> {
|
||||
infer::mk_subr(self.infcx, a_is_expected, span, sub, sup)
|
||||
}
|
||||
|
||||
fn require_unsafe(sp: span, op: ~str) {
|
||||
@ -748,8 +753,10 @@ fn check_expr(fcx: @fn_ctxt, expr: @ast::expr,
|
||||
// declared on the impl declaration e.g., `impl<A,B> for ~[(A,B)]`
|
||||
// would return ($0, $1) where $0 and $1 are freshly instantiated type
|
||||
// variables.
|
||||
fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id, require_rp: bool)
|
||||
-> ty_param_substs_and_ty {
|
||||
fn impl_self_ty(fcx: @fn_ctxt,
|
||||
expr: @ast::expr, // (potential) receiver for this impl
|
||||
did: ast::def_id,
|
||||
require_rp: bool) -> ty_param_substs_and_ty {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
|
||||
let {n_tps, rp, raw_ty} = if did.crate == ast::local_crate {
|
||||
@ -786,7 +793,8 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id, require_rp: bool)
|
||||
};
|
||||
|
||||
let rp = rp || require_rp;
|
||||
let self_r = if rp {some(fcx.infcx.next_region_var_nb())} else {none};
|
||||
let self_r = if rp {some(fcx.infcx.next_region_var(expr.span, expr.id))}
|
||||
else {none};
|
||||
let tps = fcx.infcx.next_ty_vars(n_tps);
|
||||
|
||||
let substs = {self_r: self_r, self_ty: none, tps: tps};
|
||||
@ -840,7 +848,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
sty @ ty::ty_fn(ref fn_ty) => {
|
||||
replace_bound_regions_in_fn_ty(
|
||||
fcx.ccx.tcx, @nil, none, fn_ty,
|
||||
|_br| fcx.infcx.next_region_var_nb()).fn_ty
|
||||
|_br| fcx.infcx.next_region_var(sp,
|
||||
call_expr_id)).fn_ty
|
||||
}
|
||||
sty => {
|
||||
// I would like to make this span_err, but it's
|
||||
@ -1442,8 +1451,23 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
bot = check_expr(fcx, oprnd, unpack_expected(fcx, expected, |ty|
|
||||
match ty { ty::ty_rptr(_, mt) => some(mt.ty), _ => none }
|
||||
));
|
||||
//let region = region_of(fcx, oprnd);
|
||||
let region = fcx.infcx.next_region_var_with_scope_lb(expr.id);
|
||||
|
||||
// Note: at this point, we cannot say what the best lifetime
|
||||
// is to use for resulting pointer. We want to use the
|
||||
// shortest lifetime possible so as to avoid spurious borrowck
|
||||
// errors. Moreover, the longest lifetime will depend on the
|
||||
// precise details of the value whose address is being taken
|
||||
// (and how long it is valid), which we don't know yet until type
|
||||
// inference is complete.
|
||||
//
|
||||
// Therefore, here we simply generate a region variable with
|
||||
// the current expression as a lower bound. The region
|
||||
// inferencer will then select the ultimate value. Finally,
|
||||
// borrowck is charged with guaranteeing that the value whose
|
||||
// address was taken can actually be made to live as long as
|
||||
// it needs to live.
|
||||
let region = fcx.infcx.next_region_var(expr.span, expr.id);
|
||||
|
||||
let tm = { ty: fcx.expr_ty(oprnd), mutbl: mutbl };
|
||||
let oprnd_t = ty::mk_rptr(tcx, region, tm);
|
||||
fcx.write_ty(id, oprnd_t);
|
||||
@ -1452,7 +1476,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
let defn = lookup_def(fcx, pth.span, id);
|
||||
|
||||
let tpt = ty_param_bounds_and_ty_for_def(fcx, expr.span, defn);
|
||||
instantiate_path(fcx, pth, tpt, expr.span, expr.id);
|
||||
let region_lb = ty::re_scope(expr.id);
|
||||
instantiate_path(fcx, pth, tpt, expr.span, expr.id, region_lb);
|
||||
}
|
||||
ast::expr_mac(_) => tcx.sess.bug(~"unexpanded macro"),
|
||||
ast::expr_fail(expr_opt) => {
|
||||
@ -1474,7 +1499,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
some(t) => t, none => fcx.ret_ty
|
||||
};
|
||||
match expr_opt {
|
||||
none => match fcx.mk_eqty(ret_ty, ty::mk_nil(tcx)) {
|
||||
none => match fcx.mk_eqty(false, expr.span,
|
||||
ret_ty, ty::mk_nil(tcx)) {
|
||||
result::ok(_) => { /* fall through */ }
|
||||
result::err(_) => {
|
||||
tcx.sess.span_err(
|
||||
@ -1550,7 +1576,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
let expected_sty = unpack_expected(fcx, expected, |x| some(x));
|
||||
let (inner_ty, proto) = match expected_sty {
|
||||
some(ty::ty_fn(fty)) => {
|
||||
match infer::mk_subty(fcx.infcx, fty.output, ty::mk_bool(tcx)) {
|
||||
match fcx.mk_subty(false, expr.span,
|
||||
fty.output, ty::mk_bool(tcx)) {
|
||||
result::ok(_) => (),
|
||||
result::err(err) => {
|
||||
tcx.sess.span_fatal(
|
||||
@ -1809,7 +1836,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
// Generate the struct type.
|
||||
let self_region;
|
||||
if region_parameterized {
|
||||
self_region = some(fcx.infcx.next_region_var_nb());
|
||||
self_region = some(fcx.infcx.next_region_var(expr.span, expr.id));
|
||||
} else {
|
||||
self_region = none;
|
||||
}
|
||||
@ -2307,8 +2334,9 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
|
||||
fn instantiate_path(fcx: @fn_ctxt,
|
||||
pth: @ast::path,
|
||||
tpt: ty_param_bounds_and_ty,
|
||||
sp: span,
|
||||
id: ast::node_id) {
|
||||
span: span,
|
||||
node_id: ast::node_id,
|
||||
region_lb: ty::region) {
|
||||
let ty_param_count = vec::len(*tpt.bounds);
|
||||
let ty_substs_len = vec::len(pth.types);
|
||||
|
||||
@ -2317,14 +2345,14 @@ fn instantiate_path(fcx: @fn_ctxt,
|
||||
let self_r = match pth.rp {
|
||||
some(r) if !tpt.rp => {
|
||||
fcx.ccx.tcx.sess.span_err
|
||||
(sp, ~"this item is not region-parameterized");
|
||||
(span, ~"this item is not region-parameterized");
|
||||
none
|
||||
}
|
||||
some(r) => {
|
||||
some(ast_region_to_region(fcx, fcx, sp, r))
|
||||
some(ast_region_to_region(fcx, fcx, span, r))
|
||||
}
|
||||
none if tpt.rp => {
|
||||
some(fcx.infcx.next_region_var_nb())
|
||||
some(fcx.infcx.next_region_var_with_lb(span, region_lb))
|
||||
}
|
||||
none => {
|
||||
none
|
||||
@ -2337,22 +2365,22 @@ fn instantiate_path(fcx: @fn_ctxt,
|
||||
fcx.infcx.next_ty_vars(ty_param_count)
|
||||
} else if ty_param_count == 0u {
|
||||
fcx.ccx.tcx.sess.span_err
|
||||
(sp, ~"this item does not take type parameters");
|
||||
(span, ~"this item does not take type parameters");
|
||||
fcx.infcx.next_ty_vars(ty_param_count)
|
||||
} else if ty_substs_len > ty_param_count {
|
||||
fcx.ccx.tcx.sess.span_err
|
||||
(sp, ~"too many type parameters provided for this item");
|
||||
(span, ~"too many type parameters provided for this item");
|
||||
fcx.infcx.next_ty_vars(ty_param_count)
|
||||
} else if ty_substs_len < ty_param_count {
|
||||
fcx.ccx.tcx.sess.span_err
|
||||
(sp, ~"not enough type parameters provided for this item");
|
||||
(span, ~"not enough type parameters provided for this item");
|
||||
fcx.infcx.next_ty_vars(ty_param_count)
|
||||
} else {
|
||||
pth.types.map(|aty| fcx.to_ty(aty))
|
||||
};
|
||||
|
||||
let substs = {self_r: self_r, self_ty: none, tps: tps};
|
||||
fcx.write_ty_substs(id, tpt.ty, substs);
|
||||
fcx.write_ty_substs(node_id, tpt.ty, substs);
|
||||
}
|
||||
|
||||
// Resolves `typ` by a single level if `typ` is a type variable. If no
|
||||
@ -2400,15 +2428,9 @@ fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
|
||||
}
|
||||
ast::vstore_uniq => ty::vstore_uniq,
|
||||
ast::vstore_box => ty::vstore_box,
|
||||
ast::vstore_slice(a_r) => match fcx.block_region() {
|
||||
result::ok(b_r) => {
|
||||
let r = fcx.infcx.next_region_var_with_scope_lb(e.id);
|
||||
ty::vstore_slice(r)
|
||||
}
|
||||
result::err(msg) => {
|
||||
fcx.ccx.tcx.sess.span_err(e.span, msg);
|
||||
ty::vstore_slice(ty::re_static)
|
||||
}
|
||||
ast::vstore_slice(a_r) => {
|
||||
let r = fcx.infcx.next_region_var(e.span, e.id);
|
||||
ty::vstore_slice(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2523,7 +2545,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
|
||||
expected %u", i_n_tps, n_tps});
|
||||
} else {
|
||||
require_same_types(
|
||||
tcx, none, it.span, i_ty.ty, fty,
|
||||
tcx, none, false, it.span, i_ty.ty, fty,
|
||||
|| fmt!{"intrinsic has wrong type: \
|
||||
expected `%s`",
|
||||
ty_to_str(ccx.tcx, fty)});
|
||||
|
@ -63,7 +63,8 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
|
||||
|
||||
// Assign the pattern the type of the *enum*, not the variant.
|
||||
let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm);
|
||||
instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id);
|
||||
instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id,
|
||||
pcx.block_region);
|
||||
|
||||
// Take the enum type params out of `expected`.
|
||||
match structure_of(pcx.fcx, pat.span, expected) {
|
||||
@ -143,7 +144,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
|
||||
debug!{"pat_range beginning type: %?", b_ty};
|
||||
debug!{"pat_range ending type: %?", e_ty};
|
||||
if !require_same_types(
|
||||
tcx, some(fcx.infcx), pat.span, b_ty, e_ty,
|
||||
tcx, some(fcx.infcx), false, pat.span, b_ty, e_ty,
|
||||
|| ~"mismatched types in range") {
|
||||
// no-op
|
||||
} else if !ty::type_is_numeric(b_ty) {
|
||||
@ -165,8 +166,8 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
|
||||
// then the type of x is &M T where M is the mutability
|
||||
// and T is the expected type
|
||||
let region_var =
|
||||
fcx.infcx.next_region_var({lb: some(pcx.block_region),
|
||||
ub: none});
|
||||
fcx.infcx.next_region_var_with_lb(
|
||||
pat.span, pcx.block_region);
|
||||
let mt = {ty: expected, mutbl: mutbl};
|
||||
let region_ty = ty::mk_rptr(tcx, region_var, mt);
|
||||
demand::eqtype(fcx, pat.span, region_ty, typ);
|
||||
|
@ -6,7 +6,8 @@ fn suptype(fcx: @fn_ctxt, sp: span,
|
||||
expected: ty::t, actual: ty::t) {
|
||||
|
||||
// n.b.: order of actual, expected is reversed
|
||||
match infer::mk_subty(fcx.infcx, actual, expected) {
|
||||
match infer::mk_subty(fcx.infcx, false, sp,
|
||||
actual, expected) {
|
||||
result::ok(()) => { /* ok */ }
|
||||
result::err(ref err) => {
|
||||
fcx.report_mismatched_types(sp, expected, actual, err);
|
||||
@ -17,7 +18,7 @@ fn suptype(fcx: @fn_ctxt, sp: span,
|
||||
fn eqtype(fcx: @fn_ctxt, sp: span,
|
||||
expected: ty::t, actual: ty::t) {
|
||||
|
||||
match infer::mk_eqty(fcx.infcx, actual, expected) {
|
||||
match infer::mk_eqty(fcx.infcx, false, sp, actual, expected) {
|
||||
result::ok(()) => { /* ok */ }
|
||||
result::err(ref err) => {
|
||||
fcx.report_mismatched_types(sp, expected, actual, err);
|
||||
|
@ -439,7 +439,7 @@ struct lookup {
|
||||
// determine the `self` of the impl with fresh
|
||||
// variables for each parameter:
|
||||
let {substs: impl_substs, ty: impl_ty} =
|
||||
impl_self_ty(self.fcx, im.did, need_rp);
|
||||
impl_self_ty(self.fcx, self.self_expr, im.did, need_rp);
|
||||
|
||||
let impl_ty = transform_self_type_for_method(
|
||||
self.tcx(), impl_substs.self_r,
|
||||
@ -458,15 +458,17 @@ struct lookup {
|
||||
self.self_ty,
|
||||
impl_ty),
|
||||
immutable_reference_mode => {
|
||||
let region = self.fcx.infcx.next_region_var_with_scope_lb
|
||||
(self.self_expr.id);
|
||||
let region = self.fcx.infcx.next_region_var(
|
||||
self.self_expr.span,
|
||||
self.self_expr.id);
|
||||
let tm = { ty: self.self_ty, mutbl: ast::m_imm };
|
||||
let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
|
||||
matches = self.fcx.can_mk_subty(ref_ty, impl_ty);
|
||||
}
|
||||
mutable_reference_mode => {
|
||||
let region = self.fcx.infcx.next_region_var_with_scope_lb
|
||||
(self.self_expr.id);
|
||||
let region = self.fcx.infcx.next_region_var(
|
||||
self.self_expr.span,
|
||||
self.self_expr.id);
|
||||
let tm = { ty: self.self_ty, mutbl: ast::m_mutbl };
|
||||
let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
|
||||
matches = self.fcx.can_mk_subty(ref_ty, impl_ty);
|
||||
@ -609,8 +611,9 @@ struct lookup {
|
||||
}
|
||||
immutable_reference_mode => {
|
||||
// Borrow as an immutable reference.
|
||||
let region_var = self.fcx.infcx.next_region_var_with_scope_lb
|
||||
(self.self_expr.id);
|
||||
let region_var = self.fcx.infcx.next_region_var(
|
||||
self.self_expr.span,
|
||||
self.self_expr.id);
|
||||
self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id,
|
||||
span: self.self_expr.span,
|
||||
scope: region_var,
|
||||
@ -618,8 +621,9 @@ struct lookup {
|
||||
}
|
||||
mutable_reference_mode => {
|
||||
// Borrow as a mutable reference.
|
||||
let region_var = self.fcx.infcx.next_region_var_with_scope_lb
|
||||
(self.self_expr.id);
|
||||
let region_var = self.fcx.infcx.next_region_var(
|
||||
self.self_expr.span,
|
||||
self.self_expr.id);
|
||||
self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id,
|
||||
span: self.self_expr.span,
|
||||
scope: region_var,
|
||||
|
@ -18,9 +18,9 @@ this point a bit better.
|
||||
*/
|
||||
|
||||
import util::ppaux;
|
||||
import ppaux::{note_and_explain_region, ty_to_str};
|
||||
import syntax::print::pprust;
|
||||
import infer::{resolve_type, resolve_all, force_all,
|
||||
resolve_rvar, force_rvar, fres};
|
||||
import infer::{resolve_and_force_all_but_regions, fres};
|
||||
import middle::kind::check_owned;
|
||||
import middle::pat_util::pat_bindings;
|
||||
|
||||
@ -52,8 +52,7 @@ impl @rcx {
|
||||
/// will effectively resolve `<R0>` to be the block B.
|
||||
fn resolve_type(unresolved_ty: ty::t) -> fres<ty::t> {
|
||||
resolve_type(self.fcx.infcx, unresolved_ty,
|
||||
(resolve_all | force_all) -
|
||||
(resolve_rvar | force_rvar))
|
||||
resolve_and_force_all_but_regions)
|
||||
}
|
||||
|
||||
/// Try to resolve the type for the given node.
|
||||
@ -66,6 +65,7 @@ fn regionck_expr(fcx: @fn_ctxt, e: @ast::expr) {
|
||||
let rcx = rcx_({fcx:fcx, mut errors_reported: 0u});
|
||||
let v = regionck_visitor();
|
||||
v.visit_expr(e, @rcx, v);
|
||||
fcx.infcx.resolve_regions();
|
||||
}
|
||||
|
||||
fn regionck_fn(fcx: @fn_ctxt,
|
||||
@ -74,6 +74,7 @@ fn regionck_fn(fcx: @fn_ctxt,
|
||||
let rcx = rcx_({fcx:fcx, mut errors_reported: 0u});
|
||||
let v = regionck_visitor();
|
||||
v.visit_block(blk, @rcx, v);
|
||||
fcx.infcx.resolve_regions();
|
||||
}
|
||||
|
||||
fn regionck_visitor() -> rvt {
|
||||
@ -209,10 +210,8 @@ fn visit_node(id: ast::node_id, span: span, rcx: @rcx) -> bool {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
let encl_region = ty::encl_region(tcx, id);
|
||||
|
||||
debug!{"visit_node(ty=%s, id=%d, encl_region=%s)",
|
||||
ppaux::ty_to_str(tcx, ty),
|
||||
id,
|
||||
ppaux::region_to_str(tcx, encl_region)};
|
||||
debug!{"visit_node(ty=%s, id=%d, encl_region=%?)",
|
||||
ty_to_str(tcx, ty), id, encl_region};
|
||||
|
||||
// Otherwise, look at the type and see if it is a region pointer.
|
||||
return constrain_regions_in_type(rcx, encl_region, span, ty);
|
||||
@ -237,9 +236,8 @@ fn constrain_regions_in_type(
|
||||
region: ty::region) {
|
||||
let tcx = rcx.fcx.ccx.tcx;
|
||||
|
||||
debug!{"constrain_region(encl_region=%s, region=%s)",
|
||||
ppaux::region_to_str(tcx, encl_region),
|
||||
ppaux::region_to_str(tcx, region)};
|
||||
debug!{"constrain_region(encl_region=%?, region=%?)",
|
||||
encl_region, region};
|
||||
|
||||
match region {
|
||||
ty::re_bound(_) => {
|
||||
@ -252,14 +250,15 @@ fn constrain_regions_in_type(
|
||||
_ => ()
|
||||
}
|
||||
|
||||
match rcx.fcx.mk_subr(encl_region, region) {
|
||||
match rcx.fcx.mk_subr(true, span, encl_region, region) {
|
||||
result::err(_) => {
|
||||
let region1 = rcx.fcx.infcx.resolve_region_if_possible(region);
|
||||
tcx.sess.span_err(
|
||||
span,
|
||||
fmt!{"reference is not valid outside \
|
||||
of its lifetime, %s",
|
||||
ppaux::region_to_str(tcx, region1)});
|
||||
fmt!("reference is not valid outside of its lifetime"));
|
||||
note_and_explain_region(
|
||||
tcx,
|
||||
~"the reference is only valid for",
|
||||
region);
|
||||
rcx.errors_reported += 1u;
|
||||
}
|
||||
result::ok(()) => {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import check::{fn_ctxt, impl_self_ty};
|
||||
import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str};
|
||||
import infer::{resolve_type, resolve_and_force_all_but_regions,
|
||||
fixup_err_to_str};
|
||||
import ast_util::new_def_hash;
|
||||
import syntax::print::pprust;
|
||||
|
||||
// vtable resolution looks for places where trait bounds are
|
||||
// subsituted in and figures out which vtable is used. There is some
|
||||
@ -27,7 +29,7 @@ fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
|
||||
}
|
||||
|
||||
fn lookup_vtables(fcx: @fn_ctxt,
|
||||
sp: span,
|
||||
expr: @ast::expr,
|
||||
bounds: @~[ty::param_bounds],
|
||||
substs: &ty::substs,
|
||||
allow_unsafe: bool,
|
||||
@ -39,7 +41,7 @@ fn lookup_vtables(fcx: @fn_ctxt,
|
||||
match bound {
|
||||
ty::bound_trait(i_ty) => {
|
||||
let i_ty = ty::subst(tcx, substs, i_ty);
|
||||
vec::push(result, lookup_vtable(fcx, sp, ty, i_ty,
|
||||
vec::push(result, lookup_vtable(fcx, expr, ty, i_ty,
|
||||
allow_unsafe, is_early));
|
||||
}
|
||||
_ => ()
|
||||
@ -50,31 +52,36 @@ fn lookup_vtables(fcx: @fn_ctxt,
|
||||
@result
|
||||
}
|
||||
|
||||
fn fixup_substs(fcx: @fn_ctxt, sp: span,
|
||||
fn fixup_substs(fcx: @fn_ctxt, expr: @ast::expr,
|
||||
id: ast::def_id, substs: ty::substs,
|
||||
is_early: bool) -> option<ty::substs> {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
// use a dummy type just to package up the substs that need fixing up
|
||||
let t = ty::mk_trait(tcx, id, substs, ty::vstore_slice(ty::re_static));
|
||||
do fixup_ty(fcx, sp, t, is_early).map |t_f| {
|
||||
do fixup_ty(fcx, expr, t, is_early).map |t_f| {
|
||||
match check ty::get(t_f).struct {
|
||||
ty::ty_trait(_, substs_f, _) => substs_f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn relate_trait_tys(fcx: @fn_ctxt, sp: span,
|
||||
fn relate_trait_tys(fcx: @fn_ctxt, expr: @ast::expr,
|
||||
exp_trait_ty: ty::t, act_trait_ty: ty::t) {
|
||||
demand::suptype(fcx, sp, exp_trait_ty, act_trait_ty)
|
||||
demand::suptype(fcx, expr.span, exp_trait_ty, act_trait_ty)
|
||||
}
|
||||
|
||||
/*
|
||||
Look up the vtable to use when treating an item of type <t>
|
||||
as if it has type <trait_ty>
|
||||
*/
|
||||
fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
allow_unsafe: bool, is_early: bool)
|
||||
-> vtable_origin {
|
||||
fn lookup_vtable(fcx: @fn_ctxt,
|
||||
expr: @ast::expr,
|
||||
ty: ty::t,
|
||||
trait_ty: ty::t,
|
||||
allow_unsafe: bool,
|
||||
is_early: bool)
|
||||
-> vtable_origin
|
||||
{
|
||||
|
||||
debug!{"lookup_vtable(ty=%s, trait_ty=%s)",
|
||||
fcx.infcx.ty_to_str(ty), fcx.infcx.ty_to_str(trait_ty)};
|
||||
@ -84,7 +91,7 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
let (trait_id, trait_substs) = match check ty::get(trait_ty).struct {
|
||||
ty::ty_trait(did, substs, _) => (did, substs)
|
||||
};
|
||||
let ty = match fixup_ty(fcx, sp, ty, is_early) {
|
||||
let ty = match fixup_ty(fcx, expr, ty, is_early) {
|
||||
some(ty) => ty,
|
||||
none => {
|
||||
// fixup_ty can only fail if this is early resolution
|
||||
@ -111,7 +118,7 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
if trait_id == idid {
|
||||
debug!{"(checking vtable) @0 relating ty to trait ty
|
||||
with did %?", idid};
|
||||
relate_trait_tys(fcx, sp, trait_ty, ity);
|
||||
relate_trait_tys(fcx, expr, trait_ty, ity);
|
||||
return vtable_param(n, n_bound);
|
||||
}
|
||||
}
|
||||
@ -126,17 +133,19 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
debug!{"(checking vtable) @1 relating ty to trait ty with did %?",
|
||||
did};
|
||||
|
||||
relate_trait_tys(fcx, sp, trait_ty, ty);
|
||||
relate_trait_tys(fcx, expr, trait_ty, ty);
|
||||
if !allow_unsafe && !is_early {
|
||||
for vec::each(*ty::trait_methods(tcx, did)) |m| {
|
||||
if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
|
||||
tcx.sess.span_err(
|
||||
sp, ~"a boxed trait with self types may not be \
|
||||
passed as a bounded type");
|
||||
expr.span,
|
||||
~"a boxed trait with self types may not be \
|
||||
passed as a bounded type");
|
||||
} else if (*m.tps).len() > 0u {
|
||||
tcx.sess.span_err(
|
||||
sp, ~"a boxed trait with generic methods may not \
|
||||
be passed as a bounded type");
|
||||
expr.span,
|
||||
~"a boxed trait with generic methods may not \
|
||||
be passed as a bounded type");
|
||||
|
||||
}
|
||||
}
|
||||
@ -176,9 +185,9 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
// check whether the type unifies with the type
|
||||
// that the impl is for, and continue if not
|
||||
let {substs: substs, ty: for_ty} =
|
||||
impl_self_ty(fcx, im.did, false);
|
||||
impl_self_ty(fcx, expr, im.did, false);
|
||||
let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
|
||||
match fcx.mk_subty(ty, for_ty) {
|
||||
match fcx.mk_subty(false, expr.span, ty, for_ty) {
|
||||
result::err(_) => again,
|
||||
result::ok(()) => ()
|
||||
}
|
||||
@ -189,12 +198,12 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
fcx.infcx.ty_to_str(trait_ty),
|
||||
fcx.infcx.ty_to_str(of_ty));
|
||||
let of_ty = ty::subst(tcx, &substs, of_ty);
|
||||
relate_trait_tys(fcx, sp, trait_ty, of_ty);
|
||||
relate_trait_tys(fcx, expr, trait_ty, of_ty);
|
||||
|
||||
// recursively process the bounds.
|
||||
let trait_tps = trait_substs.tps;
|
||||
// see comments around the earlier call to fixup_ty
|
||||
let substs_f = match fixup_substs(fcx, sp, trait_id,
|
||||
let substs_f = match fixup_substs(fcx, expr, trait_id,
|
||||
substs, is_early) {
|
||||
some(substs) => substs,
|
||||
none => {
|
||||
@ -204,10 +213,11 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
}
|
||||
};
|
||||
|
||||
connect_trait_tps(fcx, sp, substs_f.tps,
|
||||
connect_trait_tps(fcx, expr, substs_f.tps,
|
||||
trait_tps, im.did);
|
||||
let subres = lookup_vtables(fcx, sp, im_bs, &substs_f,
|
||||
false, is_early);
|
||||
let subres = lookup_vtables(
|
||||
fcx, expr, im_bs, &substs_f,
|
||||
false, is_early);
|
||||
vec::push(found,
|
||||
vtable_static(im.did, substs_f.tps,
|
||||
subres));
|
||||
@ -222,7 +232,8 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
_ => {
|
||||
if !is_early {
|
||||
fcx.ccx.tcx.sess.span_err(
|
||||
sp, ~"multiple applicable methods in scope");
|
||||
expr.span,
|
||||
~"multiple applicable methods in scope");
|
||||
}
|
||||
return found[0];
|
||||
}
|
||||
@ -231,19 +242,22 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||
}
|
||||
|
||||
tcx.sess.span_fatal(
|
||||
sp, ~"failed to find an implementation of trait " +
|
||||
ty_to_str(tcx, trait_ty) + ~" for " +
|
||||
ty_to_str(tcx, ty));
|
||||
expr.span,
|
||||
fmt!("failed to find an implementation of trait %s for %s",
|
||||
ty_to_str(tcx, trait_ty), ty_to_str(tcx, ty)));
|
||||
}
|
||||
|
||||
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t, is_early: bool)
|
||||
-> option<ty::t> {
|
||||
fn fixup_ty(fcx: @fn_ctxt,
|
||||
expr: @ast::expr,
|
||||
ty: ty::t,
|
||||
is_early: bool) -> option<ty::t>
|
||||
{
|
||||
let tcx = fcx.ccx.tcx;
|
||||
match resolve_type(fcx.infcx, ty, resolve_all | force_all) {
|
||||
match resolve_type(fcx.infcx, ty, resolve_and_force_all_but_regions) {
|
||||
result::ok(new_type) => some(new_type),
|
||||
result::err(e) if !is_early => {
|
||||
tcx.sess.span_fatal(
|
||||
sp,
|
||||
expr.span,
|
||||
fmt!{"cannot determine a type \
|
||||
for this bounded type parameter: %s",
|
||||
fixup_err_to_str(e)})
|
||||
@ -254,7 +268,7 @@ fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t, is_early: bool)
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_trait_tps(fcx: @fn_ctxt, sp: span, impl_tys: ~[ty::t],
|
||||
fn connect_trait_tps(fcx: @fn_ctxt, expr: @ast::expr, impl_tys: ~[ty::t],
|
||||
trait_tys: ~[ty::t], impl_did: ast::def_id) {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
|
||||
@ -266,22 +280,23 @@ fn connect_trait_tps(fcx: @fn_ctxt, sp: span, impl_tys: ~[ty::t],
|
||||
match check ty::get(trait_ty).struct {
|
||||
ty::ty_trait(_, substs, _) => {
|
||||
vec::iter2(substs.tps, trait_tys,
|
||||
|a, b| demand::suptype(fcx, sp, a, b));
|
||||
|a, b| demand::suptype(fcx, expr.span, a, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||
debug!("vtable: early_resolve_expr() ex with id %?: %s",
|
||||
ex.id, expr_to_str(ex));
|
||||
let cx = fcx.ccx;
|
||||
match ex.node {
|
||||
ast::expr_path(*) => {
|
||||
debug!("(vtable - resolving expr) resolving path expr");
|
||||
match fcx.opt_node_ty_substs(ex.id) {
|
||||
some(ref substs) => {
|
||||
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
|
||||
let item_ty = ty::lookup_item_type(cx.tcx, did);
|
||||
if has_trait_bounds(*item_ty.bounds) {
|
||||
let vtbls = lookup_vtables(fcx, ex.span, item_ty.bounds,
|
||||
let vtbls = lookup_vtables(fcx, ex, item_ty.bounds,
|
||||
substs, false, is_early);
|
||||
if !is_early { cx.vtable_map.insert(ex.id, vtbls); }
|
||||
}
|
||||
@ -293,8 +308,6 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||
ast::expr_field(*) | ast::expr_binary(*) |
|
||||
ast::expr_unary(*) | ast::expr_assign_op(*) |
|
||||
ast::expr_index(*) => {
|
||||
debug!("(vtable - resolving expr) resolving field/binary/unary/\
|
||||
assign/index expr");
|
||||
match ty::method_call_bounds(cx.tcx, cx.method_map, ex.id) {
|
||||
some(bounds) => {
|
||||
if has_trait_bounds(*bounds) {
|
||||
@ -303,7 +316,7 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||
_ => ex.callee_id
|
||||
};
|
||||
let substs = fcx.node_ty_substs(callee_id);
|
||||
let vtbls = lookup_vtables(fcx, ex.span, bounds,
|
||||
let vtbls = lookup_vtables(fcx, ex, bounds,
|
||||
&substs, false, is_early);
|
||||
if !is_early { cx.vtable_map.insert(callee_id, vtbls); }
|
||||
}
|
||||
@ -312,7 +325,6 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||
}
|
||||
}
|
||||
ast::expr_cast(src, _) => {
|
||||
debug!("(vtable - resolving expr) resolving cast expr");
|
||||
let target_ty = fcx.expr_ty(ex);
|
||||
match ty::get(target_ty).struct {
|
||||
ty::ty_trait(*) => {
|
||||
@ -320,7 +332,7 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||
Look up vtables for the type we're casting to,
|
||||
passing in the source and target type
|
||||
*/
|
||||
let vtable = lookup_vtable(fcx, ex.span, fcx.expr_ty(src),
|
||||
let vtable = lookup_vtable(fcx, ex, fcx.expr_ty(src),
|
||||
target_ty, true, is_early);
|
||||
/*
|
||||
Map this expression to that vtable (that is: "ex has
|
||||
|
@ -15,7 +15,7 @@ import middle::ty::{ty_float, ty_estr, ty_evec, ty_rec};
|
||||
import middle::ty::{ty_fn, ty_trait, ty_tup, ty_var, ty_var_integral};
|
||||
import middle::ty::{ty_param, ty_self, ty_type, ty_opaque_box};
|
||||
import middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec, type_is_var};
|
||||
import middle::typeck::infer::{infer_ctxt, mk_subty};
|
||||
import middle::typeck::infer::{infer_ctxt, can_mk_subty};
|
||||
import middle::typeck::infer::{new_infer_ctxt, resolve_ivar, resolve_type};
|
||||
import syntax::ast::{crate, def_id, def_mod};
|
||||
import syntax::ast::{item, item_class, item_const, item_enum, item_fn};
|
||||
@ -387,18 +387,22 @@ struct CoherenceChecker {
|
||||
|
||||
let monotype_a = self.universally_quantify_polytype(polytype_a);
|
||||
let monotype_b = self.universally_quantify_polytype(polytype_b);
|
||||
return
|
||||
mk_subty(self.inference_context, monotype_a, monotype_b).is_ok()
|
||||
|| mk_subty(self.inference_context, monotype_b, monotype_a).is_ok();
|
||||
return can_mk_subty(self.inference_context,
|
||||
monotype_a, monotype_b).is_ok()
|
||||
|| can_mk_subty(self.inference_context,
|
||||
monotype_b, monotype_a).is_ok();
|
||||
}
|
||||
|
||||
// Converts a polytype to a monotype by replacing all parameters with
|
||||
// type variables.
|
||||
|
||||
fn universally_quantify_polytype(polytype: ty_param_bounds_and_ty) -> t {
|
||||
let self_region =
|
||||
if !polytype.rp {none}
|
||||
else {some(self.inference_context.next_region_var_nb())};
|
||||
// NDM--this span is bogus.
|
||||
let self_region = if !polytype.rp {
|
||||
none
|
||||
} else {
|
||||
some(self.inference_context.next_region_var_nb(dummy_sp()))
|
||||
};
|
||||
|
||||
let bounds_count = polytype.bounds.len();
|
||||
let type_parameters =
|
||||
|
@ -298,7 +298,7 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
|
||||
ty::subst(tcx, &substs, trait_fty)
|
||||
};
|
||||
require_same_types(
|
||||
tcx, none, sp, impl_fty, trait_fty,
|
||||
tcx, none, false, sp, impl_fty, trait_fty,
|
||||
|| ~"method `" + *trait_m.ident + ~"` has an incompatible type");
|
||||
return;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
198
src/rustc/middle/typeck/infer/assignment.rs
Normal file
198
src/rustc/middle/typeck/infer/assignment.rs
Normal file
@ -0,0 +1,198 @@
|
||||
// ______________________________________________________________________
|
||||
// Type assignment
|
||||
//
|
||||
// True if rvalues of type `a` can be assigned to lvalues of type `b`.
|
||||
// This may cause borrowing to the region scope enclosing `a_node_id`.
|
||||
//
|
||||
// The strategy here is somewhat non-obvious. The problem is
|
||||
// that the constraint we wish to contend with is not a subtyping
|
||||
// constraint. Currently, for variables, we only track what it
|
||||
// must be a subtype of, not what types it must be assignable to
|
||||
// (or from). Possibly, we should track that, but I leave that
|
||||
// refactoring for another day.
|
||||
//
|
||||
// Instead, we look at each variable involved and try to extract
|
||||
// *some* sort of bound. Typically, the type a is the argument
|
||||
// supplied to a call; it typically has a *lower bound* (which
|
||||
// comes from having been assigned a value). What we'd actually
|
||||
// *like* here is an upper-bound, but we generally don't have
|
||||
// one. The type b is the expected type and it typically has a
|
||||
// lower-bound too, which is good.
|
||||
//
|
||||
// The way we deal with the fact that we often don't have the
|
||||
// bounds we need is to be a bit careful. We try to get *some*
|
||||
// bound from each side, preferring the upper from a and the
|
||||
// lower from b. If we fail to get a bound from both sides, then
|
||||
// we just fall back to requiring that a <: b.
|
||||
//
|
||||
// Assuming we have a bound from both sides, we will then examine
|
||||
// these bounds and see if they have the form (@M_a T_a, &rb.M_b T_b)
|
||||
// (resp. ~M_a T_a, ~[M_a T_a], etc). If they do not, we fall back to
|
||||
// subtyping.
|
||||
//
|
||||
// If they *do*, then we know that the two types could never be
|
||||
// subtypes of one another. We will then construct a type @const T_b
|
||||
// and ensure that type a is a subtype of that. This allows for the
|
||||
// possibility of assigning from a type like (say) @~[mut T1] to a type
|
||||
// &~[T2] where T1 <: T2. This might seem surprising, since the `@`
|
||||
// points at mutable memory but the `&` points at immutable memory.
|
||||
// This would in fact be unsound, except for the borrowck, which comes
|
||||
// later and guarantees that such mutability conversions are safe.
|
||||
// See borrowck for more details. Next we require that the region for
|
||||
// the enclosing scope be a superregion of the region r.
|
||||
//
|
||||
// You might wonder why we don't make the type &e.const T_a where e is
|
||||
// the enclosing region and check that &e.const T_a <: B. The reason
|
||||
// is that the type of A is (generally) just a *lower-bound*, so this
|
||||
// would be imposing that lower-bound also as the upper-bound on type
|
||||
// A. But this upper-bound might be stricter than what is truly
|
||||
// needed.
|
||||
|
||||
import to_str::to_str;
|
||||
|
||||
impl infer_ctxt {
|
||||
fn assign_tys(anmnt: &assignment, a: ty::t, b: ty::t) -> ures {
|
||||
|
||||
fn select(fst: option<ty::t>, snd: option<ty::t>) -> option<ty::t> {
|
||||
match fst {
|
||||
some(t) => some(t),
|
||||
none => match snd {
|
||||
some(t) => some(t),
|
||||
none => none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!{"assign_tys(anmnt=%?, %s -> %s)",
|
||||
anmnt, a.to_str(self), b.to_str(self)};
|
||||
let _r = indenter();
|
||||
|
||||
match (ty::get(a).struct, ty::get(b).struct) {
|
||||
(ty::ty_bot, _) => {
|
||||
uok()
|
||||
}
|
||||
|
||||
(ty::ty_var(a_id), ty::ty_var(b_id)) => {
|
||||
let nde_a = self.get(&self.ty_var_bindings, a_id);
|
||||
let nde_b = self.get(&self.ty_var_bindings, b_id);
|
||||
let a_bounds = nde_a.possible_types;
|
||||
let b_bounds = nde_b.possible_types;
|
||||
|
||||
let a_bnd = select(a_bounds.ub, a_bounds.lb);
|
||||
let b_bnd = select(b_bounds.lb, b_bounds.ub);
|
||||
self.assign_tys_or_sub(anmnt, a, b, a_bnd, b_bnd)
|
||||
}
|
||||
|
||||
(ty::ty_var(a_id), _) => {
|
||||
let nde_a = self.get(&self.ty_var_bindings, a_id);
|
||||
let a_bounds = nde_a.possible_types;
|
||||
|
||||
let a_bnd = select(a_bounds.ub, a_bounds.lb);
|
||||
self.assign_tys_or_sub(anmnt, a, b, a_bnd, some(b))
|
||||
}
|
||||
|
||||
(_, ty::ty_var(b_id)) => {
|
||||
let nde_b = self.get(&self.ty_var_bindings, b_id);
|
||||
let b_bounds = nde_b.possible_types;
|
||||
|
||||
let b_bnd = select(b_bounds.lb, b_bounds.ub);
|
||||
self.assign_tys_or_sub(anmnt, a, b, some(a), b_bnd)
|
||||
}
|
||||
|
||||
(_, _) => {
|
||||
self.assign_tys_or_sub(anmnt, a, b, some(a), some(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_tys_or_sub(
|
||||
anmnt: &assignment,
|
||||
a: ty::t, b: ty::t,
|
||||
+a_bnd: option<ty::t>, +b_bnd: option<ty::t>) -> ures {
|
||||
|
||||
debug!{"assign_tys_or_sub(anmnt=%?, %s -> %s, %s -> %s)",
|
||||
anmnt, a.to_str(self), b.to_str(self),
|
||||
a_bnd.to_str(self), b_bnd.to_str(self)};
|
||||
let _r = indenter();
|
||||
|
||||
fn is_borrowable(v: ty::vstore) -> bool {
|
||||
match v {
|
||||
ty::vstore_fixed(_) | ty::vstore_uniq | ty::vstore_box => true,
|
||||
ty::vstore_slice(_) => false
|
||||
}
|
||||
}
|
||||
|
||||
match (a_bnd, b_bnd) {
|
||||
(some(a_bnd), some(b_bnd)) => {
|
||||
match (ty::get(a_bnd).struct, ty::get(b_bnd).struct) {
|
||||
(ty::ty_box(mt_a), ty::ty_rptr(r_b, mt_b)) => {
|
||||
let nr_b = ty::mk_box(self.tcx, {ty: mt_b.ty,
|
||||
mutbl: m_const});
|
||||
self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
|
||||
}
|
||||
(ty::ty_uniq(mt_a), ty::ty_rptr(r_b, mt_b)) => {
|
||||
let nr_b = ty::mk_uniq(self.tcx, {ty: mt_b.ty,
|
||||
mutbl: m_const});
|
||||
self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
|
||||
}
|
||||
(ty::ty_estr(vs_a),
|
||||
ty::ty_estr(ty::vstore_slice(r_b)))
|
||||
if is_borrowable(vs_a) => {
|
||||
let nr_b = ty::mk_estr(self.tcx, vs_a);
|
||||
self.crosspollinate(anmnt, a, nr_b, m_imm, r_b)
|
||||
}
|
||||
|
||||
(ty::ty_evec(mt_a, vs_a),
|
||||
ty::ty_evec(mt_b, ty::vstore_slice(r_b)))
|
||||
if is_borrowable(vs_a) => {
|
||||
let nr_b = ty::mk_evec(self.tcx, {ty: mt_b.ty,
|
||||
mutbl: m_const}, vs_a);
|
||||
self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
|
||||
}
|
||||
|
||||
_ => {
|
||||
mk_sub(self, false, anmnt.span).tys(a, b).to_ures()
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
mk_sub(self, false, anmnt.span).tys(a, b).to_ures()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn crosspollinate(anmnt: &assignment,
|
||||
a: ty::t,
|
||||
nr_b: ty::t,
|
||||
m: ast::mutability,
|
||||
r_b: ty::region) -> ures {
|
||||
|
||||
debug!{"crosspollinate(anmnt=%?, a=%s, nr_b=%s, r_b=%s)",
|
||||
anmnt, a.to_str(self), nr_b.to_str(self),
|
||||
r_b.to_str(self)};
|
||||
|
||||
do indent {
|
||||
let sub = mk_sub(self, false, anmnt.span);
|
||||
do sub.tys(a, nr_b).chain |_t| {
|
||||
// Create a fresh region variable `r_a` with the given
|
||||
// borrow bounds:
|
||||
let r_a = self.next_region_var(anmnt.span,
|
||||
anmnt.borrow_lb);
|
||||
|
||||
debug!{"anmnt=%?", anmnt};
|
||||
do sub.contraregions(r_a, r_b).chain |_r| {
|
||||
// if successful, add an entry indicating that
|
||||
// borrowing occurred
|
||||
debug!{"borrowing expression #%?, scope=%?, m=%?",
|
||||
anmnt, r_a, m};
|
||||
self.borrowings.push({expr_id: anmnt.expr_id,
|
||||
span: anmnt.span,
|
||||
scope: r_a,
|
||||
mutbl: m});
|
||||
uok()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
442
src/rustc/middle/typeck/infer/combine.rs
Normal file
442
src/rustc/middle/typeck/infer/combine.rs
Normal file
@ -0,0 +1,442 @@
|
||||
// ______________________________________________________________________
|
||||
// Type combining
|
||||
//
|
||||
// There are three type combiners: sub, lub, and glb. Each implements
|
||||
// the trait `combine` and contains methods for combining two
|
||||
// instances of various things and yielding a new instance. These
|
||||
// combiner methods always yield a `result<T>`---failure is propagated
|
||||
// upward using `chain()` methods.
|
||||
//
|
||||
// There is a lot of common code for these operations, which is
|
||||
// abstracted out into functions named `super_X()` which take a combiner
|
||||
// instance as the first parameter. This would be better implemented
|
||||
// using traits. For this system to work properly, you should not
|
||||
// call the `super_X(foo, ...)` functions directly, but rather call
|
||||
// `foo.X(...)`. The implementation of `X()` can then choose to delegate
|
||||
// to the `super` routine or to do other things.
|
||||
//
|
||||
// In reality, the sub operation is rather different from lub/glb, but
|
||||
// they are combined into one trait to avoid duplication (they used to
|
||||
// be separate but there were many bugs because there were two copies
|
||||
// of most routines).
|
||||
//
|
||||
// The differences are:
|
||||
//
|
||||
// - when making two things have a sub relationship, the order of the
|
||||
// arguments is significant (a <: b) and the return value of the
|
||||
// combine functions is largely irrelevant. The important thing is
|
||||
// whether the action succeeds or fails. If it succeeds, then side
|
||||
// effects have been committed into the type variables.
|
||||
//
|
||||
// - for GLB/LUB, the order of arguments is not significant (GLB(a,b) ==
|
||||
// GLB(b,a)) and the return value is important (it is the GLB). Of
|
||||
// course GLB/LUB may also have side effects.
|
||||
//
|
||||
// Contravariance
|
||||
//
|
||||
// When you are relating two things which have a contravariant
|
||||
// relationship, you should use `contratys()` or `contraregions()`,
|
||||
// rather than inversing the order of arguments! This is necessary
|
||||
// because the order of arguments is not relevant for LUB and GLB. It
|
||||
// is also useful to track which value is the "expected" value in
|
||||
// terms of error reporting, although we do not do that properly right
|
||||
// now.
|
||||
|
||||
import to_str::to_str;
|
||||
|
||||
trait combine {
|
||||
fn infcx() -> infer_ctxt;
|
||||
fn tag() -> ~str;
|
||||
fn a_is_expected() -> bool;
|
||||
|
||||
fn sub() -> Sub;
|
||||
fn lub() -> Lub;
|
||||
fn glb() -> Glb;
|
||||
|
||||
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt>;
|
||||
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t>;
|
||||
fn tys(a: ty::t, b: ty::t) -> cres<ty::t>;
|
||||
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]>;
|
||||
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>>;
|
||||
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs>;
|
||||
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty>;
|
||||
fn flds(a: ty::field, b: ty::field) -> cres<ty::field>;
|
||||
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode>;
|
||||
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg>;
|
||||
fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres<ty::fn_proto>;
|
||||
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style>;
|
||||
fn purities(a: purity, b: purity) -> cres<purity>;
|
||||
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region>;
|
||||
fn regions(a: ty::region, b: ty::region) -> cres<ty::region>;
|
||||
fn vstores(vk: ty::terr_vstore_kind,
|
||||
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore>;
|
||||
}
|
||||
|
||||
struct combine_fields {
|
||||
infcx: infer_ctxt;
|
||||
a_is_expected: bool;
|
||||
span: span;
|
||||
}
|
||||
|
||||
fn expected_found<C: combine,T>(
|
||||
self: &C, +a: T, +b: T) -> ty::expected_found<T> {
|
||||
|
||||
if self.a_is_expected() {
|
||||
ty::expected_found {expected: a, found: b}
|
||||
} else {
|
||||
ty::expected_found {expected: b, found: a}
|
||||
}
|
||||
}
|
||||
|
||||
fn eq_tys<C: combine>(self: &C, a: ty::t, b: ty::t) -> ures {
|
||||
let suber = self.sub();
|
||||
do self.infcx().try {
|
||||
do suber.tys(a, b).chain |_ok| {
|
||||
suber.contratys(a, b)
|
||||
}.to_ures()
|
||||
}
|
||||
}
|
||||
|
||||
fn eq_regions<C: combine>(self: &C, a: ty::region, b: ty::region) -> ures {
|
||||
debug!{"eq_regions(%s, %s)",
|
||||
a.to_str(self.infcx()),
|
||||
b.to_str(self.infcx())};
|
||||
let sub = self.sub();
|
||||
do indent {
|
||||
self.infcx().try(|| {
|
||||
do sub.regions(a, b).chain |_r| {
|
||||
sub.contraregions(a, b)
|
||||
}
|
||||
}).chain_err(|e| {
|
||||
// substitute a better error, but use the regions
|
||||
// found in the original error
|
||||
match e {
|
||||
ty::terr_regions_does_not_outlive(a1, b1) =>
|
||||
err(ty::terr_regions_not_same(a1, b1)),
|
||||
_ => err(e)
|
||||
}
|
||||
}).to_ures()
|
||||
}
|
||||
}
|
||||
|
||||
fn eq_opt_regions<C:combine>(
|
||||
self: &C,
|
||||
a: option<ty::region>,
|
||||
b: option<ty::region>) -> cres<option<ty::region>> {
|
||||
|
||||
match (a, b) {
|
||||
(none, none) => {
|
||||
ok(none)
|
||||
}
|
||||
(some(a), some(b)) => {
|
||||
do eq_regions(self, a, b).then {
|
||||
ok(some(a))
|
||||
}
|
||||
}
|
||||
(_, _) => {
|
||||
// If these two substitutions are for the same type (and
|
||||
// they should be), then the type should either
|
||||
// consistently have a region parameter or not have a
|
||||
// region parameter.
|
||||
self.infcx().tcx.sess.bug(
|
||||
fmt!{"substitution a had opt_region %s and \
|
||||
b had opt_region %s",
|
||||
a.to_str(self.infcx()),
|
||||
b.to_str(self.infcx())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn super_substs<C:combine>(
|
||||
self: &C, a: &ty::substs, b: &ty::substs) -> cres<ty::substs> {
|
||||
|
||||
do self.tps(a.tps, b.tps).chain |tps| {
|
||||
do self.self_tys(a.self_ty, b.self_ty).chain |self_ty| {
|
||||
do eq_opt_regions(self, a.self_r, b.self_r).chain
|
||||
|self_r| {
|
||||
ok({self_r: self_r, self_ty: self_ty, tps: tps})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn super_tps<C:combine>(
|
||||
self: &C, as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
|
||||
|
||||
// Note: type parameters are always treated as *invariant*
|
||||
// (otherwise the type system would be unsound). In the
|
||||
// future we could allow type parameters to declare a
|
||||
// variance.
|
||||
|
||||
if vec::same_length(as, bs) {
|
||||
iter_vec2(as, bs, |a, b| {
|
||||
eq_tys(self, a, b)
|
||||
}).then(|| ok(as.to_vec()) )
|
||||
} else {
|
||||
err(ty::terr_ty_param_size(
|
||||
expected_found(self, as.len(), bs.len())))
|
||||
}
|
||||
}
|
||||
|
||||
fn super_self_tys<C:combine>(
|
||||
self: &C, a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
|
||||
|
||||
// Note: the self type parameter is (currently) always treated as
|
||||
// *invariant* (otherwise the type system would be unsound).
|
||||
|
||||
match (a, b) {
|
||||
(none, none) => {
|
||||
ok(none)
|
||||
}
|
||||
(some(a), some(b)) => {
|
||||
eq_tys(self, a, b).then(|| ok(some(a)) )
|
||||
}
|
||||
(none, some(_)) |
|
||||
(some(_), none) => {
|
||||
// I think it should never happen that we unify two substs and
|
||||
// one of them has a self_ty and one doesn't...? I could be
|
||||
// wrong about this.
|
||||
err(ty::terr_self_substs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn super_flds<C:combine>(
|
||||
self: &C, a: ty::field, b: ty::field) -> cres<ty::field> {
|
||||
|
||||
if a.ident == b.ident {
|
||||
self.mts(a.mt, b.mt)
|
||||
.chain(|mt| ok({ident: a.ident, mt: mt}) )
|
||||
.chain_err(|e| err(ty::terr_in_field(@e, a.ident)) )
|
||||
} else {
|
||||
err(ty::terr_record_fields(
|
||||
expected_found(self, a.ident, b.ident)))
|
||||
}
|
||||
}
|
||||
|
||||
fn super_modes<C:combine>(
|
||||
self: &C, a: ast::mode, b: ast::mode)
|
||||
-> cres<ast::mode> {
|
||||
|
||||
let tcx = self.infcx().tcx;
|
||||
ty::unify_mode(tcx, expected_found(self, a, b))
|
||||
}
|
||||
|
||||
fn super_args<C:combine>(
|
||||
self: &C, a: ty::arg, b: ty::arg)
|
||||
-> cres<ty::arg> {
|
||||
|
||||
do self.modes(a.mode, b.mode).chain |m| {
|
||||
do self.contratys(a.ty, b.ty).chain |t| {
|
||||
ok({mode: m, ty: t})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn super_vstores<C:combine>(
|
||||
self: &C, vk: ty::terr_vstore_kind,
|
||||
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
|
||||
|
||||
match (a, b) {
|
||||
(ty::vstore_slice(a_r), ty::vstore_slice(b_r)) => {
|
||||
do self.contraregions(a_r, b_r).chain |r| {
|
||||
ok(ty::vstore_slice(r))
|
||||
}
|
||||
}
|
||||
|
||||
_ if a == b => {
|
||||
ok(a)
|
||||
}
|
||||
|
||||
_ => {
|
||||
err(ty::terr_vstores_differ(vk, expected_found(self, a, b)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn super_fns<C:combine>(
|
||||
self: &C, a_f: &ty::fn_ty, b_f: &ty::fn_ty) -> cres<ty::fn_ty> {
|
||||
|
||||
fn argvecs<C:combine>(self: &C, a_args: ~[ty::arg],
|
||||
b_args: ~[ty::arg]) -> cres<~[ty::arg]> {
|
||||
|
||||
if vec::same_length(a_args, b_args) {
|
||||
map_vec2(a_args, b_args, |a, b| self.args(a, b) )
|
||||
} else {
|
||||
err(ty::terr_arg_count)
|
||||
}
|
||||
}
|
||||
|
||||
do self.protos(a_f.proto, b_f.proto).chain |p| {
|
||||
do self.ret_styles(a_f.ret_style, b_f.ret_style).chain |rs| {
|
||||
do argvecs(self, a_f.inputs, b_f.inputs).chain |inputs| {
|
||||
do self.tys(a_f.output, b_f.output).chain |output| {
|
||||
do self.purities(a_f.purity, b_f.purity).chain |purity| {
|
||||
// FIXME: uncomment if #2588 doesn't get accepted:
|
||||
// self.infcx().constrvecs(a_f.constraints,
|
||||
// b_f.constraints).then {||
|
||||
ok({purity: purity,
|
||||
proto: p,
|
||||
bounds: a_f.bounds, // XXX: This is wrong!
|
||||
inputs: inputs,
|
||||
output: output,
|
||||
ret_style: rs})
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn super_tys<C:combine>(
|
||||
self: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||
|
||||
let tcx = self.infcx().tcx;
|
||||
match (ty::get(a).struct, ty::get(b).struct) {
|
||||
// The "subtype" ought to be handling cases involving bot or var:
|
||||
(ty::ty_bot, _) |
|
||||
(_, ty::ty_bot) |
|
||||
(ty::ty_var(_), _) |
|
||||
(_, ty::ty_var(_)) => {
|
||||
tcx.sess.bug(
|
||||
fmt!{"%s: bot and var types should have been handled (%s,%s)",
|
||||
self.tag(),
|
||||
a.to_str(self.infcx()),
|
||||
b.to_str(self.infcx())});
|
||||
}
|
||||
|
||||
// Relate integral variables to other types
|
||||
(ty::ty_var_integral(a_id), ty::ty_var_integral(b_id)) => {
|
||||
self.infcx().vars_integral(a_id, b_id).then(|| ok(a) )
|
||||
}
|
||||
(ty::ty_var_integral(a_id), ty::ty_int(_)) |
|
||||
(ty::ty_var_integral(a_id), ty::ty_uint(_)) => {
|
||||
self.infcx().var_integral_sub_t(a_id, b).then(|| ok(a) )
|
||||
}
|
||||
(ty::ty_int(_), ty::ty_var_integral(b_id)) |
|
||||
(ty::ty_uint(_), ty::ty_var_integral(b_id)) => {
|
||||
self.infcx().t_sub_var_integral(a, b_id).then(|| ok(a) )
|
||||
}
|
||||
|
||||
(ty::ty_int(_), _) |
|
||||
(ty::ty_uint(_), _) |
|
||||
(ty::ty_float(_), _) => {
|
||||
let as = ty::get(a).struct;
|
||||
let bs = ty::get(b).struct;
|
||||
if as == bs {
|
||||
ok(a)
|
||||
} else {
|
||||
err(ty::terr_sorts(expected_found(self, a, b)))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_nil, _) |
|
||||
(ty::ty_bool, _) => {
|
||||
let cfg = tcx.sess.targ_cfg;
|
||||
if ty::mach_sty(cfg, a) == ty::mach_sty(cfg, b) {
|
||||
ok(a)
|
||||
} else {
|
||||
err(ty::terr_sorts(expected_found(self, a, b)))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_param(a_p), ty::ty_param(b_p)) if a_p.idx == b_p.idx => {
|
||||
ok(a)
|
||||
}
|
||||
|
||||
(ty::ty_enum(a_id, ref a_substs),
|
||||
ty::ty_enum(b_id, ref b_substs))
|
||||
if a_id == b_id => {
|
||||
do self.substs(a_substs, b_substs).chain |substs| {
|
||||
ok(ty::mk_enum(tcx, a_id, substs))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_trait(a_id, ref a_substs, a_vstore),
|
||||
ty::ty_trait(b_id, ref b_substs, b_vstore))
|
||||
if a_id == b_id => {
|
||||
do self.substs(a_substs, b_substs).chain |substs| {
|
||||
do self.vstores(ty::terr_trait, a_vstore, b_vstore).chain |vs| {
|
||||
ok(ty::mk_trait(tcx, a_id, substs, vs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_class(a_id, ref a_substs), ty::ty_class(b_id, ref b_substs))
|
||||
if a_id == b_id => {
|
||||
do self.substs(a_substs, b_substs).chain |substs| {
|
||||
ok(ty::mk_class(tcx, a_id, substs))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_box(a_mt), ty::ty_box(b_mt)) => {
|
||||
do self.mts(a_mt, b_mt).chain |mt| {
|
||||
ok(ty::mk_box(tcx, mt))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_uniq(a_mt), ty::ty_uniq(b_mt)) => {
|
||||
do self.mts(a_mt, b_mt).chain |mt| {
|
||||
ok(ty::mk_uniq(tcx, mt))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_ptr(a_mt), ty::ty_ptr(b_mt)) => {
|
||||
do self.mts(a_mt, b_mt).chain |mt| {
|
||||
ok(ty::mk_ptr(tcx, mt))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_rptr(a_r, a_mt), ty::ty_rptr(b_r, b_mt)) => {
|
||||
do self.contraregions(a_r, b_r).chain |r| {
|
||||
do self.mts(a_mt, b_mt).chain |mt| {
|
||||
ok(ty::mk_rptr(tcx, r, mt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_evec(a_mt, vs_a), ty::ty_evec(b_mt, vs_b)) => {
|
||||
do self.mts(a_mt, b_mt).chain |mt| {
|
||||
do self.vstores(ty::terr_vec, vs_a, vs_b).chain |vs| {
|
||||
ok(ty::mk_evec(tcx, mt, vs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_estr(vs_a), ty::ty_estr(vs_b)) => {
|
||||
do self.vstores(ty::terr_str, vs_a, vs_b).chain |vs| {
|
||||
ok(ty::mk_estr(tcx,vs))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_rec(as), ty::ty_rec(bs)) => {
|
||||
if vec::same_length(as, bs) {
|
||||
map_vec2(as, bs, |a,b| {
|
||||
self.flds(a, b)
|
||||
}).chain(|flds| ok(ty::mk_rec(tcx, flds)) )
|
||||
} else {
|
||||
err(ty::terr_record_size(expected_found(self, as.len(),
|
||||
bs.len())))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_tup(as), ty::ty_tup(bs)) => {
|
||||
if vec::same_length(as, bs) {
|
||||
map_vec2(as, bs, |a, b| self.tys(a, b) )
|
||||
.chain(|ts| ok(ty::mk_tup(tcx, ts)) )
|
||||
} else {
|
||||
err(ty::terr_tuple_size(expected_found(self, as.len(), bs.len())))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_fn(ref a_fty), ty::ty_fn(ref b_fty)) => {
|
||||
do self.fns(a_fty, b_fty).chain |fty| {
|
||||
ok(ty::mk_fn(tcx, fty))
|
||||
}
|
||||
}
|
||||
|
||||
_ => err(ty::terr_sorts(expected_found(self, a, b)))
|
||||
}
|
||||
}
|
||||
|
166
src/rustc/middle/typeck/infer/glb.rs
Normal file
166
src/rustc/middle/typeck/infer/glb.rs
Normal file
@ -0,0 +1,166 @@
|
||||
import combine::*;
|
||||
import lattice::*;
|
||||
import to_str::to_str;
|
||||
|
||||
enum Glb = combine_fields; // "greatest lower bound" (common subtype)
|
||||
|
||||
impl Glb: combine {
|
||||
fn infcx() -> infer_ctxt { self.infcx }
|
||||
fn tag() -> ~str { ~"glb" }
|
||||
fn a_is_expected() -> bool { self.a_is_expected }
|
||||
|
||||
fn sub() -> Sub { Sub(*self) }
|
||||
fn lub() -> Lub { Lub(*self) }
|
||||
fn glb() -> Glb { Glb(*self) }
|
||||
|
||||
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
debug!{"%s.mts(%s, %s)",
|
||||
self.tag(),
|
||||
mt_to_str(tcx, a),
|
||||
mt_to_str(tcx, b)};
|
||||
|
||||
match (a.mutbl, b.mutbl) {
|
||||
// If one side or both is mut, then the GLB must use
|
||||
// the precise type from the mut side.
|
||||
(m_mutbl, m_const) => {
|
||||
Sub(*self).tys(a.ty, b.ty).chain(|_t| {
|
||||
ok({ty: a.ty, mutbl: m_mutbl})
|
||||
})
|
||||
}
|
||||
(m_const, m_mutbl) => {
|
||||
Sub(*self).tys(b.ty, a.ty).chain(|_t| {
|
||||
ok({ty: b.ty, mutbl: m_mutbl})
|
||||
})
|
||||
}
|
||||
(m_mutbl, m_mutbl) => {
|
||||
eq_tys(&self, a.ty, b.ty).then(|| {
|
||||
ok({ty: a.ty, mutbl: m_mutbl})
|
||||
})
|
||||
}
|
||||
|
||||
// If one side or both is immutable, we can use the GLB of
|
||||
// both sides but mutbl must be `m_imm`.
|
||||
(m_imm, m_const) |
|
||||
(m_const, m_imm) |
|
||||
(m_imm, m_imm) => {
|
||||
self.tys(a.ty, b.ty).chain(|t| {
|
||||
ok({ty: t, mutbl: m_imm})
|
||||
})
|
||||
}
|
||||
|
||||
// If both sides are const, then we can use GLB of both
|
||||
// sides and mutbl of only `m_const`.
|
||||
(m_const, m_const) => {
|
||||
self.tys(a.ty, b.ty).chain(|t| {
|
||||
ok({ty: t, mutbl: m_const})
|
||||
})
|
||||
}
|
||||
|
||||
// There is no mutual subtype of these combinations.
|
||||
(m_mutbl, m_imm) |
|
||||
(m_imm, m_mutbl) => {
|
||||
err(ty::terr_mutability)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||
Lub(*self).tys(a, b)
|
||||
}
|
||||
|
||||
fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres<ty::fn_proto> {
|
||||
match (p1, p2) {
|
||||
(ty::proto_vstore(ty::vstore_slice(_)), _) => ok(p2),
|
||||
(_, ty::proto_vstore(ty::vstore_slice(_))) => ok(p1),
|
||||
(ty::proto_vstore(v1), ty::proto_vstore(v2)) => {
|
||||
self.infcx.try(|| {
|
||||
do self.vstores(terr_fn, v1, v2).chain |vs| {
|
||||
ok(ty::proto_vstore(vs))
|
||||
}
|
||||
}).chain_err(|_err| {
|
||||
// XXX: Totally unsound, but fixed up later.
|
||||
ok(ty::proto_bare)
|
||||
})
|
||||
}
|
||||
_ => ok(ty::proto_bare)
|
||||
}
|
||||
}
|
||||
|
||||
fn purities(a: purity, b: purity) -> cres<purity> {
|
||||
match (a, b) {
|
||||
(pure_fn, _) | (_, pure_fn) => ok(pure_fn),
|
||||
(extern_fn, _) | (_, extern_fn) => ok(extern_fn),
|
||||
(impure_fn, _) | (_, impure_fn) => ok(impure_fn),
|
||||
(unsafe_fn, unsafe_fn) => ok(unsafe_fn)
|
||||
}
|
||||
}
|
||||
|
||||
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
|
||||
match (r1, r2) {
|
||||
(ast::return_val, ast::return_val) => {
|
||||
ok(ast::return_val)
|
||||
}
|
||||
(ast::noreturn, _) |
|
||||
(_, ast::noreturn) => {
|
||||
ok(ast::noreturn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
debug!{"%s.regions(%?, %?)",
|
||||
self.tag(),
|
||||
a.to_str(self.infcx),
|
||||
b.to_str(self.infcx)};
|
||||
|
||||
do indent {
|
||||
self.infcx.region_vars.glb_regions(self.span, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
Lub(*self).regions(a, b)
|
||||
}
|
||||
|
||||
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||
lattice_tys(&self, a, b)
|
||||
}
|
||||
|
||||
// Traits please:
|
||||
|
||||
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
|
||||
super_flds(&self, a, b)
|
||||
}
|
||||
|
||||
fn vstores(vk: ty::terr_vstore_kind,
|
||||
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
|
||||
super_vstores(&self, vk, a, b)
|
||||
}
|
||||
|
||||
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
|
||||
super_modes(&self, a, b)
|
||||
}
|
||||
|
||||
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
|
||||
super_args(&self, a, b)
|
||||
}
|
||||
|
||||
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty> {
|
||||
super_fns(&self, a, b)
|
||||
}
|
||||
|
||||
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
|
||||
super_substs(&self, as, bs)
|
||||
}
|
||||
|
||||
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
|
||||
super_tps(&self, as, bs)
|
||||
}
|
||||
|
||||
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
|
||||
super_self_tys(&self, a, b)
|
||||
}
|
||||
}
|
||||
|
78
src/rustc/middle/typeck/infer/integral.rs
Normal file
78
src/rustc/middle/typeck/infer/integral.rs
Normal file
@ -0,0 +1,78 @@
|
||||
/*!
|
||||
|
||||
Code related to integral type inference.
|
||||
|
||||
*/
|
||||
|
||||
import to_str::to_str;
|
||||
|
||||
// Bitvector to represent sets of integral types
|
||||
enum int_ty_set = uint;
|
||||
|
||||
// Constants representing singleton sets containing each of the
|
||||
// integral types
|
||||
const INT_TY_SET_EMPTY : uint = 0b00_0000_0000u;
|
||||
const INT_TY_SET_i8 : uint = 0b00_0000_0001u;
|
||||
const INT_TY_SET_u8 : uint = 0b00_0000_0010u;
|
||||
const INT_TY_SET_i16 : uint = 0b00_0000_0100u;
|
||||
const INT_TY_SET_u16 : uint = 0b00_0000_1000u;
|
||||
const INT_TY_SET_i32 : uint = 0b00_0001_0000u;
|
||||
const INT_TY_SET_u32 : uint = 0b00_0010_0000u;
|
||||
const INT_TY_SET_i64 : uint = 0b00_0100_0000u;
|
||||
const INT_TY_SET_u64 : uint = 0b00_1000_0000u;
|
||||
const INT_TY_SET_i : uint = 0b01_0000_0000u;
|
||||
const INT_TY_SET_u : uint = 0b10_0000_0000u;
|
||||
|
||||
fn int_ty_set_all() -> int_ty_set {
|
||||
int_ty_set(INT_TY_SET_i8 | INT_TY_SET_u8 |
|
||||
INT_TY_SET_i16 | INT_TY_SET_u16 |
|
||||
INT_TY_SET_i32 | INT_TY_SET_u32 |
|
||||
INT_TY_SET_i64 | INT_TY_SET_u64 |
|
||||
INT_TY_SET_i | INT_TY_SET_u)
|
||||
}
|
||||
|
||||
fn intersection(a: int_ty_set, b: int_ty_set) -> int_ty_set {
|
||||
int_ty_set(*a & *b)
|
||||
}
|
||||
|
||||
fn single_type_contained_in(tcx: ty::ctxt, a: int_ty_set) ->
|
||||
option<ty::t> {
|
||||
debug!{"single_type_contained_in(a=%s)", uint::to_str(*a, 10u)};
|
||||
|
||||
if *a == INT_TY_SET_i8 { return some(ty::mk_i8(tcx)); }
|
||||
if *a == INT_TY_SET_u8 { return some(ty::mk_u8(tcx)); }
|
||||
if *a == INT_TY_SET_i16 { return some(ty::mk_i16(tcx)); }
|
||||
if *a == INT_TY_SET_u16 { return some(ty::mk_u16(tcx)); }
|
||||
if *a == INT_TY_SET_i32 { return some(ty::mk_i32(tcx)); }
|
||||
if *a == INT_TY_SET_u32 { return some(ty::mk_u32(tcx)); }
|
||||
if *a == INT_TY_SET_i64 { return some(ty::mk_i64(tcx)); }
|
||||
if *a == INT_TY_SET_u64 { return some(ty::mk_u64(tcx)); }
|
||||
if *a == INT_TY_SET_i { return some(ty::mk_int(tcx)); }
|
||||
if *a == INT_TY_SET_u { return some(ty::mk_uint(tcx)); }
|
||||
return none;
|
||||
}
|
||||
|
||||
fn convert_integral_ty_to_int_ty_set(tcx: ty::ctxt, t: ty::t)
|
||||
-> int_ty_set {
|
||||
|
||||
match get(t).struct {
|
||||
ty_int(int_ty) => match int_ty {
|
||||
ast::ty_i8 => int_ty_set(INT_TY_SET_i8),
|
||||
ast::ty_i16 => int_ty_set(INT_TY_SET_i16),
|
||||
ast::ty_i32 => int_ty_set(INT_TY_SET_i32),
|
||||
ast::ty_i64 => int_ty_set(INT_TY_SET_i64),
|
||||
ast::ty_i => int_ty_set(INT_TY_SET_i),
|
||||
ast::ty_char => tcx.sess.bug(
|
||||
~"char type passed to convert_integral_ty_to_int_ty_set()")
|
||||
},
|
||||
ty_uint(uint_ty) => match uint_ty {
|
||||
ast::ty_u8 => int_ty_set(INT_TY_SET_u8),
|
||||
ast::ty_u16 => int_ty_set(INT_TY_SET_u16),
|
||||
ast::ty_u32 => int_ty_set(INT_TY_SET_u32),
|
||||
ast::ty_u64 => int_ty_set(INT_TY_SET_u64),
|
||||
ast::ty_u => int_ty_set(INT_TY_SET_u)
|
||||
},
|
||||
_ => tcx.sess.bug(~"non-integral type passed to \
|
||||
convert_integral_ty_to_int_ty_set()")
|
||||
}
|
||||
}
|
148
src/rustc/middle/typeck/infer/lattice.rs
Normal file
148
src/rustc/middle/typeck/infer/lattice.rs
Normal file
@ -0,0 +1,148 @@
|
||||
import combine::*;
|
||||
import unify::*;
|
||||
import to_str::to_str;
|
||||
|
||||
// ______________________________________________________________________
|
||||
// Lattice operations on variables
|
||||
//
|
||||
// This is common code used by both LUB and GLB to compute the LUB/GLB
|
||||
// for pairs of variables or for variables and values.
|
||||
|
||||
trait lattice_ops {
|
||||
fn bnd(b: bounds<ty::t>) -> option<ty::t>;
|
||||
fn with_bnd(b: bounds<ty::t>, t: ty::t) -> bounds<ty::t>;
|
||||
fn ty_bot(t: ty::t) -> cres<ty::t>;
|
||||
}
|
||||
|
||||
impl Lub: lattice_ops {
|
||||
fn bnd(b: bounds<ty::t>) -> option<ty::t> { b.ub }
|
||||
fn with_bnd(b: bounds<ty::t>, t: ty::t) -> bounds<ty::t> {
|
||||
{ub: some(t) with b}
|
||||
}
|
||||
fn ty_bot(t: ty::t) -> cres<ty::t> {
|
||||
ok(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl Glb: lattice_ops {
|
||||
fn bnd(b: bounds<ty::t>) -> option<ty::t> { b.lb }
|
||||
fn with_bnd(b: bounds<ty::t>, t: ty::t) -> bounds<ty::t> {
|
||||
{lb: some(t) with b}
|
||||
}
|
||||
fn ty_bot(_t: ty::t) -> cres<ty::t> {
|
||||
ok(ty::mk_bot(self.infcx.tcx))
|
||||
}
|
||||
}
|
||||
|
||||
fn lattice_tys<L:lattice_ops combine>(
|
||||
self: &L, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||
|
||||
debug!{"%s.lattice_tys(%s, %s)", self.tag(),
|
||||
a.to_str(self.infcx()),
|
||||
b.to_str(self.infcx())};
|
||||
if a == b { return ok(a); }
|
||||
do indent {
|
||||
match (ty::get(a).struct, ty::get(b).struct) {
|
||||
(ty::ty_bot, _) => self.ty_bot(b),
|
||||
(_, ty::ty_bot) => self.ty_bot(a),
|
||||
|
||||
(ty::ty_var(a_id), ty::ty_var(b_id)) => {
|
||||
lattice_vars(self, a, a_id, b_id,
|
||||
|x, y| self.tys(x, y) )
|
||||
}
|
||||
|
||||
(ty::ty_var(a_id), _) => {
|
||||
lattice_var_and_t(self, a_id, b,
|
||||
|x, y| self.tys(x, y) )
|
||||
}
|
||||
|
||||
(_, ty::ty_var(b_id)) => {
|
||||
lattice_var_and_t(self, b_id, a,
|
||||
|x, y| self.tys(x, y) )
|
||||
}
|
||||
_ => {
|
||||
super_tys(self, a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lattice_vars<L:lattice_ops combine>(
|
||||
self: &L, +a_t: ty::t, +a_vid: ty::tv_vid, +b_vid: ty::tv_vid,
|
||||
c_ts: fn(ty::t, ty::t) -> cres<ty::t>) -> cres<ty::t> {
|
||||
|
||||
// The comments in this function are written for LUB and types,
|
||||
// but they apply equally well to GLB and regions if you inverse
|
||||
// upper/lower/sub/super/etc.
|
||||
|
||||
// Need to find a type that is a supertype of both a and b:
|
||||
let vb = &self.infcx().ty_var_bindings;
|
||||
let nde_a = self.infcx().get(vb, a_vid);
|
||||
let nde_b = self.infcx().get(vb, b_vid);
|
||||
let a_vid = nde_a.root;
|
||||
let b_vid = nde_b.root;
|
||||
let a_bounds = nde_a.possible_types;
|
||||
let b_bounds = nde_b.possible_types;
|
||||
|
||||
debug!{"%s.lattice_vars(%s=%s <: %s=%s)",
|
||||
self.tag(),
|
||||
a_vid.to_str(), a_bounds.to_str(self.infcx()),
|
||||
b_vid.to_str(), b_bounds.to_str(self.infcx())};
|
||||
|
||||
if a_vid == b_vid {
|
||||
return ok(a_t);
|
||||
}
|
||||
|
||||
// If both A and B have an UB type, then we can just compute the
|
||||
// LUB of those types:
|
||||
let a_bnd = self.bnd(a_bounds), b_bnd = self.bnd(b_bounds);
|
||||
match (a_bnd, b_bnd) {
|
||||
(some(a_ty), some(b_ty)) => {
|
||||
match self.infcx().try(|| c_ts(a_ty, b_ty) ) {
|
||||
ok(t) => return ok(t),
|
||||
err(_) => { /*fallthrough */ }
|
||||
}
|
||||
}
|
||||
_ => {/*fallthrough*/}
|
||||
}
|
||||
|
||||
// Otherwise, we need to merge A and B into one variable. We can
|
||||
// then use either variable as an upper bound:
|
||||
var_sub_var(self, a_vid, b_vid).then(|| ok(a_t) )
|
||||
}
|
||||
|
||||
fn lattice_var_and_t<L:lattice_ops combine>(
|
||||
self: &L, a_id: ty::tv_vid, b: ty::t,
|
||||
c_ts: fn(ty::t, ty::t) -> cres<ty::t>) -> cres<ty::t> {
|
||||
|
||||
let vb = &self.infcx().ty_var_bindings;
|
||||
let nde_a = self.infcx().get(vb, a_id);
|
||||
let a_id = nde_a.root;
|
||||
let a_bounds = nde_a.possible_types;
|
||||
|
||||
// The comments in this function are written for LUB, but they
|
||||
// apply equally well to GLB if you inverse upper/lower/sub/super/etc.
|
||||
|
||||
debug!{"%s.lattice_var_and_t(%s=%s <: %s)",
|
||||
self.tag(),
|
||||
a_id.to_str(), a_bounds.to_str(self.infcx()),
|
||||
b.to_str(self.infcx())};
|
||||
|
||||
match self.bnd(a_bounds) {
|
||||
some(a_bnd) => {
|
||||
// If a has an upper bound, return the LUB(a.ub, b)
|
||||
debug!{"bnd=some(%s)", a_bnd.to_str(self.infcx())};
|
||||
return c_ts(a_bnd, b);
|
||||
}
|
||||
none => {
|
||||
// If a does not have an upper bound, make b the upper bound of a
|
||||
// and then return b.
|
||||
debug!{"bnd=none"};
|
||||
let a_bounds = self.with_bnd(a_bounds, b);
|
||||
do bnds(self, a_bounds.lb, a_bounds.ub).then {
|
||||
self.infcx().set(vb, a_id, root(a_bounds, nde_a.rank));
|
||||
ok(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
144
src/rustc/middle/typeck/infer/lub.rs
Normal file
144
src/rustc/middle/typeck/infer/lub.rs
Normal file
@ -0,0 +1,144 @@
|
||||
import combine::*;
|
||||
import lattice::*;
|
||||
import to_str::to_str;
|
||||
|
||||
enum Lub = combine_fields; // "subtype", "subregion" etc
|
||||
|
||||
impl Lub: combine {
|
||||
fn infcx() -> infer_ctxt { self.infcx }
|
||||
fn tag() -> ~str { ~"lub" }
|
||||
fn a_is_expected() -> bool { self.a_is_expected }
|
||||
|
||||
fn sub() -> Sub { Sub(*self) }
|
||||
fn lub() -> Lub { Lub(*self) }
|
||||
fn glb() -> Glb { Glb(*self) }
|
||||
|
||||
fn bot_ty(b: ty::t) -> cres<ty::t> { ok(b) }
|
||||
fn ty_bot(b: ty::t) -> cres<ty::t> { self.bot_ty(b) } // commutative
|
||||
|
||||
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
debug!{"%s.mts(%s, %s)",
|
||||
self.tag(),
|
||||
mt_to_str(tcx, a),
|
||||
mt_to_str(tcx, b)};
|
||||
|
||||
let m = if a.mutbl == b.mutbl {
|
||||
a.mutbl
|
||||
} else {
|
||||
m_const
|
||||
};
|
||||
|
||||
match m {
|
||||
m_imm | m_const => {
|
||||
self.tys(a.ty, b.ty).chain(|t| ok({ty: t, mutbl: m}) )
|
||||
}
|
||||
|
||||
m_mutbl => {
|
||||
self.infcx.try(|| {
|
||||
eq_tys(&self, a.ty, b.ty).then(|| {
|
||||
ok({ty: a.ty, mutbl: m})
|
||||
})
|
||||
}).chain_err(|_e| {
|
||||
self.tys(a.ty, b.ty).chain(|t| {
|
||||
ok({ty: t, mutbl: m_const})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||
Glb(*self).tys(a, b)
|
||||
}
|
||||
|
||||
// XXX: Wrong.
|
||||
fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres<ty::fn_proto> {
|
||||
match (p1, p2) {
|
||||
(ty::proto_bare, _) => ok(p2),
|
||||
(_, ty::proto_bare) => ok(p1),
|
||||
(ty::proto_vstore(v1), ty::proto_vstore(v2)) => {
|
||||
self.infcx.try(|| {
|
||||
do self.vstores(terr_fn, v1, v2).chain |vs| {
|
||||
ok(ty::proto_vstore(vs))
|
||||
}
|
||||
}).chain_err(|_err| {
|
||||
// XXX: Totally unsound, but fixed up later.
|
||||
ok(ty::proto_vstore(ty::vstore_slice(ty::re_static)))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn purities(a: purity, b: purity) -> cres<purity> {
|
||||
match (a, b) {
|
||||
(unsafe_fn, _) | (_, unsafe_fn) => ok(unsafe_fn),
|
||||
(impure_fn, _) | (_, impure_fn) => ok(impure_fn),
|
||||
(extern_fn, _) | (_, extern_fn) => ok(extern_fn),
|
||||
(pure_fn, pure_fn) => ok(pure_fn)
|
||||
}
|
||||
}
|
||||
|
||||
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
|
||||
match (r1, r2) {
|
||||
(ast::return_val, _) |
|
||||
(_, ast::return_val) => ok(ast::return_val),
|
||||
(ast::noreturn, ast::noreturn) => ok(ast::noreturn)
|
||||
}
|
||||
}
|
||||
|
||||
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
return Glb(*self).regions(a, b);
|
||||
}
|
||||
|
||||
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
debug!{"%s.regions(%?, %?)",
|
||||
self.tag(),
|
||||
a.to_str(self.infcx),
|
||||
b.to_str(self.infcx)};
|
||||
|
||||
do indent {
|
||||
self.infcx.region_vars.lub_regions(self.span, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
// Traits please:
|
||||
|
||||
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||
lattice_tys(&self, a, b)
|
||||
}
|
||||
|
||||
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
|
||||
super_flds(&self, a, b)
|
||||
}
|
||||
|
||||
fn vstores(vk: ty::terr_vstore_kind,
|
||||
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
|
||||
super_vstores(&self, vk, a, b)
|
||||
}
|
||||
|
||||
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
|
||||
super_modes(&self, a, b)
|
||||
}
|
||||
|
||||
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
|
||||
super_args(&self, a, b)
|
||||
}
|
||||
|
||||
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty> {
|
||||
super_fns(&self, a, b)
|
||||
}
|
||||
|
||||
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
|
||||
super_substs(&self, as, bs)
|
||||
}
|
||||
|
||||
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
|
||||
super_tps(&self, as, bs)
|
||||
}
|
||||
|
||||
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
|
||||
super_self_tys(&self, a, b)
|
||||
}
|
||||
}
|
1198
src/rustc/middle/typeck/infer/region_var_bindings.rs
Normal file
1198
src/rustc/middle/typeck/infer/region_var_bindings.rs
Normal file
File diff suppressed because it is too large
Load Diff
234
src/rustc/middle/typeck/infer/resolve.rs
Normal file
234
src/rustc/middle/typeck/infer/resolve.rs
Normal file
@ -0,0 +1,234 @@
|
||||
// Resolution is the process of removing type variables and replacing
|
||||
// them with their inferred values. Unfortunately our inference has
|
||||
// become fairly complex and so there are a number of options to
|
||||
// control *just how much* you want to resolve and how you want to do
|
||||
// it.
|
||||
//
|
||||
// # Controlling the scope of resolution
|
||||
//
|
||||
// The options resolve_* determine what kinds of variables get
|
||||
// resolved. Generally resolution starts with a top-level type
|
||||
// variable; we will always resolve this. However, once we have
|
||||
// resolved that variable, we may end up with a type that still
|
||||
// contains type variables. For example, if we resolve `<T0>` we may
|
||||
// end up with something like `[<T1>]`. If the option
|
||||
// `resolve_nested_tvar` is passed, we will then go and recursively
|
||||
// resolve `<T1>`.
|
||||
//
|
||||
// The options `resolve_rvar` and `resolve_ivar` control whether we
|
||||
// resolve region and integral variables, respectively.
|
||||
//
|
||||
// # What do if things are unconstrained
|
||||
//
|
||||
// Sometimes we will encounter a variable that has no constraints, and
|
||||
// therefore cannot sensibly be mapped to any particular result. By
|
||||
// default, we will leave such variables as is (so you will get back a
|
||||
// variable in your result). The options force_* will cause the
|
||||
// resolution to fail in this case intead, except for the case of
|
||||
// integral variables, which resolve to `int` if forced.
|
||||
//
|
||||
// # resolve_all and force_all
|
||||
//
|
||||
// The options are a bit set, so you can use the *_all to resolve or
|
||||
// force all kinds of variables (including those we may add in the
|
||||
// future). If you want to resolve everything but one type, you are
|
||||
// probably better off writing `resolve_all - resolve_ivar`.
|
||||
|
||||
import integral::*;
|
||||
import to_str::to_str;
|
||||
|
||||
const resolve_nested_tvar: uint = 0b00000001;
|
||||
const resolve_rvar: uint = 0b00000010;
|
||||
const resolve_ivar: uint = 0b00000100;
|
||||
const resolve_all: uint = 0b00000111;
|
||||
const force_tvar: uint = 0b00010000;
|
||||
const force_rvar: uint = 0b00100000;
|
||||
const force_ivar: uint = 0b01000000;
|
||||
const force_all: uint = 0b01110000;
|
||||
|
||||
const not_regions: uint = !(force_rvar | resolve_rvar);
|
||||
|
||||
const resolve_and_force_all_but_regions: uint =
|
||||
(resolve_all | force_all) & not_regions;
|
||||
|
||||
type resolve_state_ = {
|
||||
infcx: infer_ctxt,
|
||||
modes: uint,
|
||||
mut err: option<fixup_err>,
|
||||
mut v_seen: ~[tv_vid]
|
||||
};
|
||||
|
||||
enum resolve_state {
|
||||
resolve_state_(@resolve_state_)
|
||||
}
|
||||
|
||||
fn resolver(infcx: infer_ctxt, modes: uint) -> resolve_state {
|
||||
resolve_state_(@{infcx: infcx,
|
||||
modes: modes,
|
||||
mut err: none,
|
||||
mut v_seen: ~[]})
|
||||
}
|
||||
|
||||
impl resolve_state {
|
||||
fn should(mode: uint) -> bool {
|
||||
(self.modes & mode) == mode
|
||||
}
|
||||
|
||||
fn resolve_type_chk(typ: ty::t) -> fres<ty::t> {
|
||||
self.err = none;
|
||||
|
||||
debug!{"Resolving %s (modes=%x)",
|
||||
ty_to_str(self.infcx.tcx, typ),
|
||||
self.modes};
|
||||
|
||||
// n.b. This is a hokey mess because the current fold doesn't
|
||||
// allow us to pass back errors in any useful way.
|
||||
|
||||
assert vec::is_empty(self.v_seen);
|
||||
let rty = indent(|| self.resolve_type(typ) );
|
||||
assert vec::is_empty(self.v_seen);
|
||||
match self.err {
|
||||
none => {
|
||||
debug!{"Resolved to %s (modes=%x)",
|
||||
ty_to_str(self.infcx.tcx, rty),
|
||||
self.modes};
|
||||
return ok(rty);
|
||||
}
|
||||
some(e) => return err(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_region_chk(orig: ty::region) -> fres<ty::region> {
|
||||
self.err = none;
|
||||
let resolved = indent(|| self.resolve_region(orig) );
|
||||
match self.err {
|
||||
none => ok(resolved),
|
||||
some(e) => err(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_type(typ: ty::t) -> ty::t {
|
||||
debug!{"resolve_type(%s)", typ.to_str(self.infcx)};
|
||||
indent(fn&() -> ty::t {
|
||||
if !ty::type_needs_infer(typ) { return typ; }
|
||||
|
||||
match ty::get(typ).struct {
|
||||
ty::ty_var(vid) => {
|
||||
self.resolve_ty_var(vid)
|
||||
}
|
||||
ty::ty_var_integral(vid) => {
|
||||
self.resolve_ty_var_integral(vid)
|
||||
}
|
||||
_ => {
|
||||
if !self.should(resolve_rvar) &&
|
||||
!self.should(resolve_nested_tvar) {
|
||||
// shortcircuit for efficiency
|
||||
typ
|
||||
} else {
|
||||
ty::fold_regions_and_ty(
|
||||
self.infcx.tcx, typ,
|
||||
|r| self.resolve_region(r),
|
||||
|t| self.resolve_nested_tvar(t),
|
||||
|t| self.resolve_nested_tvar(t))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_nested_tvar(typ: ty::t) -> ty::t {
|
||||
debug!{"Resolve_if_deep(%s)", typ.to_str(self.infcx)};
|
||||
if !self.should(resolve_nested_tvar) {
|
||||
typ
|
||||
} else {
|
||||
self.resolve_type(typ)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_region(orig: ty::region) -> ty::region {
|
||||
debug!{"Resolve_region(%s)", orig.to_str(self.infcx)};
|
||||
match orig {
|
||||
ty::re_var(rid) => self.resolve_region_var(rid),
|
||||
_ => orig
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_region_var(rid: region_vid) -> ty::region {
|
||||
if !self.should(resolve_rvar) {
|
||||
return ty::re_var(rid)
|
||||
}
|
||||
self.infcx.region_vars.resolve_var(rid)
|
||||
}
|
||||
|
||||
fn assert_not_rvar(rid: region_vid, r: ty::region) {
|
||||
match r {
|
||||
ty::re_var(rid2) => {
|
||||
self.err = some(region_var_bound_by_region_var(rid, rid2));
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_ty_var(vid: tv_vid) -> ty::t {
|
||||
if vec::contains(self.v_seen, vid) {
|
||||
self.err = some(cyclic_ty(vid));
|
||||
return ty::mk_var(self.infcx.tcx, vid);
|
||||
} else {
|
||||
vec::push(self.v_seen, vid);
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
// Nonobvious: prefer the most specific type
|
||||
// (i.e., the lower bound) to the more general
|
||||
// one. More general types in Rust (e.g., fn())
|
||||
// tend to carry more restrictions or higher
|
||||
// perf. penalties, so it pays to know more.
|
||||
|
||||
let nde = self.infcx.get(&self.infcx.ty_var_bindings, vid);
|
||||
let bounds = nde.possible_types;
|
||||
|
||||
let t1 = match bounds {
|
||||
{ ub:_, lb:some(t) } if !type_is_bot(t) => self.resolve_type(t),
|
||||
{ ub:some(t), lb:_ } => self.resolve_type(t),
|
||||
{ ub:_, lb:some(t) } => self.resolve_type(t),
|
||||
{ ub:none, lb:none } => {
|
||||
if self.should(force_tvar) {
|
||||
self.err = some(unresolved_ty(vid));
|
||||
}
|
||||
ty::mk_var(tcx, vid)
|
||||
}
|
||||
};
|
||||
vec::pop(self.v_seen);
|
||||
return t1;
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_ty_var_integral(vid: tvi_vid) -> ty::t {
|
||||
if !self.should(resolve_ivar) {
|
||||
return ty::mk_var_integral(self.infcx.tcx, vid);
|
||||
}
|
||||
|
||||
let nde = self.infcx.get(&self.infcx.ty_var_integral_bindings, vid);
|
||||
let pt = nde.possible_types;
|
||||
|
||||
// If there's only one type in the set of possible types, then
|
||||
// that's the answer.
|
||||
match single_type_contained_in(self.infcx.tcx, pt) {
|
||||
some(t) => t,
|
||||
none => {
|
||||
if self.should(force_ivar) {
|
||||
// As a last resort, default to int.
|
||||
let ty = ty::mk_int(self.infcx.tcx);
|
||||
self.infcx.set(
|
||||
&self.infcx.ty_var_integral_bindings, vid,
|
||||
root(convert_integral_ty_to_int_ty_set(self.infcx.tcx,
|
||||
ty),
|
||||
nde.rank));
|
||||
ty
|
||||
} else {
|
||||
ty::mk_var_integral(self.infcx.tcx, vid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
199
src/rustc/middle/typeck/infer/sub.rs
Normal file
199
src/rustc/middle/typeck/infer/sub.rs
Normal file
@ -0,0 +1,199 @@
|
||||
import combine::*;
|
||||
import unify::*;
|
||||
import to_str::to_str;
|
||||
|
||||
enum Sub = combine_fields; // "subtype", "subregion" etc
|
||||
|
||||
impl Sub: combine {
|
||||
fn infcx() -> infer_ctxt { self.infcx }
|
||||
fn tag() -> ~str { ~"sub" }
|
||||
fn a_is_expected() -> bool { self.a_is_expected }
|
||||
|
||||
fn sub() -> Sub { Sub(*self) }
|
||||
fn lub() -> Lub { Lub(*self) }
|
||||
fn glb() -> Glb { Glb(*self) }
|
||||
|
||||
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||
let opp = combine_fields {
|
||||
a_is_expected: !self.a_is_expected, with *self
|
||||
};
|
||||
Sub(opp).tys(b, a)
|
||||
}
|
||||
|
||||
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
let opp = combine_fields {
|
||||
a_is_expected: !self.a_is_expected, with *self
|
||||
};
|
||||
Sub(opp).regions(b, a)
|
||||
}
|
||||
|
||||
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
debug!{"%s.regions(%s, %s)",
|
||||
self.tag(),
|
||||
a.to_str(self.infcx),
|
||||
b.to_str(self.infcx)};
|
||||
do indent {
|
||||
match self.infcx.region_vars.make_subregion(self.span, a, b) {
|
||||
ok(()) => ok(a),
|
||||
err(e) => err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
|
||||
debug!{"mts(%s <: %s)", a.to_str(self.infcx), b.to_str(self.infcx)};
|
||||
|
||||
if a.mutbl != b.mutbl && b.mutbl != m_const {
|
||||
return err(ty::terr_mutability);
|
||||
}
|
||||
|
||||
match b.mutbl {
|
||||
m_mutbl => {
|
||||
// If supertype is mut, subtype must match exactly
|
||||
// (i.e., invariant if mut):
|
||||
eq_tys(&self, a.ty, b.ty).then(|| ok(a) )
|
||||
}
|
||||
m_imm | m_const => {
|
||||
// Otherwise we can be covariant:
|
||||
self.tys(a.ty, b.ty).chain(|_t| ok(a) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn protos(a: ty::fn_proto, b: ty::fn_proto) -> cres<ty::fn_proto> {
|
||||
match (a, b) {
|
||||
(ty::proto_bare, _) =>
|
||||
ok(ty::proto_bare),
|
||||
|
||||
(ty::proto_vstore(ty::vstore_box),
|
||||
ty::proto_vstore(ty::vstore_slice(_))) =>
|
||||
ok(ty::proto_vstore(ty::vstore_box)),
|
||||
|
||||
(ty::proto_vstore(ty::vstore_uniq),
|
||||
ty::proto_vstore(ty::vstore_slice(_))) =>
|
||||
ok(ty::proto_vstore(ty::vstore_uniq)),
|
||||
|
||||
(_, ty::proto_bare) =>
|
||||
err(ty::terr_proto_mismatch(expected_found(&self, a, b))),
|
||||
|
||||
(ty::proto_vstore(vs_a), ty::proto_vstore(vs_b)) => {
|
||||
do self.vstores(ty::terr_fn, vs_a, vs_b).chain |vs_c| {
|
||||
ok(ty::proto_vstore(vs_c))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn purities(a: purity, b: purity) -> cres<purity> {
|
||||
self.lub().purities(a, b).compare(b, || {
|
||||
ty::terr_purity_mismatch(expected_found(&self, a, b))
|
||||
})
|
||||
}
|
||||
|
||||
fn ret_styles(a: ret_style, b: ret_style) -> cres<ret_style> {
|
||||
self.lub().ret_styles(a, b).compare(b, || {
|
||||
ty::terr_ret_style_mismatch(expected_found(&self, a, b))
|
||||
})
|
||||
}
|
||||
|
||||
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||
debug!{"%s.tys(%s, %s)", self.tag(),
|
||||
a.to_str(self.infcx), b.to_str(self.infcx)};
|
||||
if a == b { return ok(a); }
|
||||
do indent {
|
||||
match (ty::get(a).struct, ty::get(b).struct) {
|
||||
(ty::ty_bot, _) => {
|
||||
ok(a)
|
||||
}
|
||||
(ty::ty_var(a_id), ty::ty_var(b_id)) => {
|
||||
var_sub_var(&self, a_id, b_id).then(|| ok(a) )
|
||||
}
|
||||
(ty::ty_var(a_id), _) => {
|
||||
var_sub_t(&self, a_id, b).then(|| ok(a) )
|
||||
}
|
||||
(_, ty::ty_var(b_id)) => {
|
||||
t_sub_var(&self, a, b_id).then(|| ok(a) )
|
||||
}
|
||||
(_, ty::ty_bot) => {
|
||||
err(ty::terr_sorts(expected_found(&self, a, b)))
|
||||
}
|
||||
_ => {
|
||||
super_tys(&self, a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty> {
|
||||
// Rather than checking the subtype relationship between `a` and `b`
|
||||
// as-is, we need to do some extra work here in order to make sure
|
||||
// that function subtyping works correctly with respect to regions
|
||||
// (issue #2263).
|
||||
|
||||
// First, we instantiate each bound region in the subtype with a fresh
|
||||
// region variable.
|
||||
let {fn_ty: a_fn_ty, _} = {
|
||||
do replace_bound_regions_in_fn_ty(self.infcx.tcx, @nil,
|
||||
none, a) |br| {
|
||||
// N.B.: The name of the bound region doesn't have
|
||||
// anything to do with the region variable that's created
|
||||
// for it. The only thing we're doing with `br` here is
|
||||
// using it in the debug message.
|
||||
//
|
||||
// NDM--we should not be used dummy_sp() here, but
|
||||
// rather passing in the span or something like that.
|
||||
let rvar = self.infcx.next_region_var_nb(dummy_sp());
|
||||
debug!{"Bound region %s maps to %s",
|
||||
bound_region_to_str(self.infcx.tcx, br),
|
||||
region_to_str(self.infcx.tcx, rvar)};
|
||||
rvar
|
||||
}
|
||||
};
|
||||
|
||||
// Second, we instantiate each bound region in the supertype with a
|
||||
// fresh concrete region.
|
||||
let {fn_ty: b_fn_ty, _} = {
|
||||
do replace_bound_regions_in_fn_ty(self.infcx.tcx, @nil,
|
||||
none, b) |br| {
|
||||
// FIXME: eventually re_skolemized (issue #2263)
|
||||
ty::re_bound(br)
|
||||
}
|
||||
};
|
||||
|
||||
// Try to compare the supertype and subtype now that they've been
|
||||
// instantiated.
|
||||
super_fns(&self, &a_fn_ty, &b_fn_ty)
|
||||
}
|
||||
|
||||
// Traits please:
|
||||
|
||||
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
|
||||
super_flds(&self, a, b)
|
||||
}
|
||||
|
||||
fn vstores(vk: ty::terr_vstore_kind,
|
||||
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
|
||||
super_vstores(&self, vk, a, b)
|
||||
}
|
||||
|
||||
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
|
||||
super_modes(&self, a, b)
|
||||
}
|
||||
|
||||
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
|
||||
super_args(&self, a, b)
|
||||
}
|
||||
|
||||
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
|
||||
super_substs(&self, as, bs)
|
||||
}
|
||||
|
||||
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
|
||||
super_tps(&self, as, bs)
|
||||
}
|
||||
|
||||
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
|
||||
super_self_tys(&self, a, b)
|
||||
}
|
||||
}
|
||||
|
60
src/rustc/middle/typeck/infer/to_str.rs
Normal file
60
src/rustc/middle/typeck/infer/to_str.rs
Normal file
@ -0,0 +1,60 @@
|
||||
import integral::{int_ty_set};
|
||||
import unify::{var_value, redirect, root};
|
||||
|
||||
trait to_str {
|
||||
fn to_str(cx: infer_ctxt) -> ~str;
|
||||
}
|
||||
|
||||
impl ty::t: to_str {
|
||||
fn to_str(cx: infer_ctxt) -> ~str {
|
||||
ty_to_str(cx.tcx, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ty::mt: to_str {
|
||||
fn to_str(cx: infer_ctxt) -> ~str {
|
||||
mt_to_str(cx.tcx, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ty::region: to_str {
|
||||
fn to_str(cx: infer_ctxt) -> ~str {
|
||||
util::ppaux::region_to_str(cx.tcx, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V:copy to_str> bound<V>: to_str {
|
||||
fn to_str(cx: infer_ctxt) -> ~str {
|
||||
match self {
|
||||
some(v) => v.to_str(cx),
|
||||
none => ~"none"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:copy to_str> bounds<T>: to_str {
|
||||
fn to_str(cx: infer_ctxt) -> ~str {
|
||||
fmt!{"{%s <: %s}",
|
||||
self.lb.to_str(cx),
|
||||
self.ub.to_str(cx)}
|
||||
}
|
||||
}
|
||||
|
||||
impl int_ty_set: to_str {
|
||||
fn to_str(_cx: infer_ctxt) -> ~str {
|
||||
match self {
|
||||
int_ty_set(v) => uint::to_str(v, 10u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V:copy vid, T:copy to_str> var_value<V, T>: to_str {
|
||||
fn to_str(cx: infer_ctxt) -> ~str {
|
||||
match self {
|
||||
redirect(vid) => fmt!{"redirect(%s)", vid.to_str()},
|
||||
root(pt, rk) => fmt!{"root(%s, %s)", pt.to_str(cx),
|
||||
uint::to_str(rk, 10u)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
379
src/rustc/middle/typeck/infer/unify.rs
Normal file
379
src/rustc/middle/typeck/infer/unify.rs
Normal file
@ -0,0 +1,379 @@
|
||||
import combine::combine;
|
||||
import integral::*;
|
||||
import to_str::to_str;
|
||||
|
||||
enum var_value<V:copy, T:copy> {
|
||||
redirect(V),
|
||||
root(T, uint),
|
||||
}
|
||||
|
||||
struct vals_and_bindings<V:copy, T:copy> {
|
||||
vals: smallintmap<var_value<V, T>>;
|
||||
mut bindings: ~[(V, var_value<V, T>)];
|
||||
}
|
||||
|
||||
struct node<V:copy, T:copy> {
|
||||
root: V;
|
||||
possible_types: T;
|
||||
rank: uint;
|
||||
}
|
||||
|
||||
impl infer_ctxt {
|
||||
fn get<V:copy vid, T:copy>(
|
||||
vb: &vals_and_bindings<V, T>, vid: V) -> node<V, T> {
|
||||
|
||||
let vid_u = vid.to_uint();
|
||||
match vb.vals.find(vid_u) {
|
||||
none => {
|
||||
self.tcx.sess.bug(fmt!{"failed lookup of vid `%u`", vid_u});
|
||||
}
|
||||
some(var_val) => {
|
||||
match var_val {
|
||||
redirect(vid) => {
|
||||
let node = self.get(vb, vid);
|
||||
if node.root != vid {
|
||||
// Path compression
|
||||
vb.vals.insert(vid.to_uint(), redirect(node.root));
|
||||
}
|
||||
node
|
||||
}
|
||||
root(pt, rk) => {
|
||||
node {root: vid, possible_types: pt, rank: rk}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set<V:copy vid, T:copy to_str>(
|
||||
vb: &vals_and_bindings<V, T>, vid: V,
|
||||
+new_v: var_value<V, T>) {
|
||||
|
||||
let old_v = vb.vals.get(vid.to_uint());
|
||||
vec::push(vb.bindings, (vid, old_v));
|
||||
vb.vals.insert(vid.to_uint(), new_v);
|
||||
|
||||
debug!{"Updating variable %s from %s to %s",
|
||||
vid.to_str(), old_v.to_str(self), new_v.to_str(self)};
|
||||
}
|
||||
}
|
||||
|
||||
// Combines the two bounds into a more general bound.
|
||||
fn merge_bnd<C: combine>(
|
||||
self: &C, a: bound<ty::t>, b: bound<ty::t>,
|
||||
merge_op: fn(ty::t,ty::t) -> cres<ty::t>) -> cres<bound<ty::t>> {
|
||||
|
||||
debug!("merge_bnd(%s,%s)",
|
||||
a.to_str(self.infcx()),
|
||||
b.to_str(self.infcx()));
|
||||
let _r = indenter();
|
||||
|
||||
match (a, b) {
|
||||
(none, none) => ok(none),
|
||||
(some(_), none) => ok(a),
|
||||
(none, some(_)) => ok(b),
|
||||
(some(v_a), some(v_b)) => {
|
||||
do merge_op(v_a, v_b).chain |v| {
|
||||
ok(some(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_bnds<C: combine>(
|
||||
self: &C, a: bounds<ty::t>, b: bounds<ty::t>,
|
||||
lub: fn(ty::t,ty::t) -> cres<ty::t>,
|
||||
glb: fn(ty::t,ty::t) -> cres<ty::t>) -> cres<bounds<ty::t>> {
|
||||
|
||||
let _r = indenter();
|
||||
do merge_bnd(self, a.ub, b.ub, glb).chain |ub| {
|
||||
debug!{"glb of ubs %s and %s is %s",
|
||||
a.ub.to_str(self.infcx()),
|
||||
b.ub.to_str(self.infcx()),
|
||||
ub.to_str(self.infcx())};
|
||||
do merge_bnd(self, a.lb, b.lb, lub).chain |lb| {
|
||||
debug!{"lub of lbs %s and %s is %s",
|
||||
a.lb.to_str(self.infcx()),
|
||||
b.lb.to_str(self.infcx()),
|
||||
lb.to_str(self.infcx())};
|
||||
ok({lb: lb, ub: ub})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the bounds for the variable `v_id` to be the intersection
|
||||
// of `a` and `b`. That is, the new bounds for `v_id` will be
|
||||
// a bounds c such that:
|
||||
// c.ub <: a.ub
|
||||
// c.ub <: b.ub
|
||||
// a.lb <: c.lb
|
||||
// b.lb <: c.lb
|
||||
// If this cannot be achieved, the result is failure.
|
||||
|
||||
fn set_var_to_merged_bounds<C: combine>(
|
||||
self: &C,
|
||||
v_id: ty::tv_vid,
|
||||
a: bounds<ty::t>,
|
||||
b: bounds<ty::t>,
|
||||
rank: uint) -> ures {
|
||||
|
||||
let vb = &self.infcx().ty_var_bindings;
|
||||
|
||||
// Think of the two diamonds, we want to find the
|
||||
// intersection. There are basically four possibilities (you
|
||||
// can swap A/B in these pictures):
|
||||
//
|
||||
// A A
|
||||
// / \ / \
|
||||
// / B \ / B \
|
||||
// / / \ \ / / \ \
|
||||
// * * * * * / * *
|
||||
// \ \ / / \ / /
|
||||
// \ B / / \ / /
|
||||
// \ / * \ /
|
||||
// A \ / A
|
||||
// B
|
||||
|
||||
debug!{"merge(%s,%s,%s)",
|
||||
v_id.to_str(),
|
||||
a.to_str(self.infcx()),
|
||||
b.to_str(self.infcx())};
|
||||
|
||||
// First, relate the lower/upper bounds of A and B.
|
||||
// Note that these relations *must* hold for us to
|
||||
// to be able to merge A and B at all, and relating
|
||||
// them explicitly gives the type inferencer more
|
||||
// information and helps to produce tighter bounds
|
||||
// when necessary.
|
||||
do indent {
|
||||
do bnds(self, a.lb, b.ub).then {
|
||||
do bnds(self, b.lb, a.ub).then {
|
||||
do merge_bnd(self, a.ub, b.ub,
|
||||
|x, y| self.glb().tys(x, y)).chain |ub| {
|
||||
do merge_bnd(self, a.lb, b.lb,
|
||||
|x, y| self.lub().tys(x, y)).chain |lb| {
|
||||
let bounds = {lb: lb, ub: ub};
|
||||
debug!{"merge(%s): bounds=%s",
|
||||
v_id.to_str(),
|
||||
bounds.to_str(self.infcx())};
|
||||
|
||||
// the new bounds must themselves
|
||||
// be relatable:
|
||||
do bnds(self, bounds.lb, bounds.ub).then {
|
||||
self.infcx().set(vb, v_id, root(bounds, rank));
|
||||
uok()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that variable A is a subtype of variable B. This is a
|
||||
/// subtle and tricky process, as described in detail at the top
|
||||
/// of infer.rs
|
||||
fn var_sub_var<C: combine>(self: &C,
|
||||
a_id: ty::tv_vid,
|
||||
b_id: ty::tv_vid) -> ures {
|
||||
let vb = &self.infcx().ty_var_bindings;
|
||||
|
||||
// Need to make sub_id a subtype of sup_id.
|
||||
let nde_a = self.infcx().get(vb, a_id);
|
||||
let nde_b = self.infcx().get(vb, b_id);
|
||||
let a_id = nde_a.root;
|
||||
let b_id = nde_b.root;
|
||||
let a_bounds = nde_a.possible_types;
|
||||
let b_bounds = nde_b.possible_types;
|
||||
|
||||
debug!{"vars(%s=%s <: %s=%s)",
|
||||
a_id.to_str(), a_bounds.to_str(self.infcx()),
|
||||
b_id.to_str(), b_bounds.to_str(self.infcx())};
|
||||
|
||||
if a_id == b_id { return uok(); }
|
||||
|
||||
// If both A's UB and B's LB have already been bound to types,
|
||||
// see if we can make those types subtypes.
|
||||
match (a_bounds.ub, b_bounds.lb) {
|
||||
(some(a_ub), some(b_lb)) => {
|
||||
let r = self.infcx().try(|| self.sub().tys(a_ub, b_lb));
|
||||
match r {
|
||||
ok(_ty) => return result::ok(()),
|
||||
err(_) => { /*fallthrough */ }
|
||||
}
|
||||
}
|
||||
_ => { /*fallthrough*/ }
|
||||
}
|
||||
|
||||
// Otherwise, we need to merge A and B so as to guarantee that
|
||||
// A remains a subtype of B. Actually, there are other options,
|
||||
// but that's the route we choose to take.
|
||||
|
||||
// Rank optimization
|
||||
|
||||
// Make the node with greater rank the parent of the node with
|
||||
// smaller rank.
|
||||
if nde_a.rank > nde_b.rank {
|
||||
debug!{"vars(): a has smaller rank"};
|
||||
// a has greater rank, so a should become b's parent,
|
||||
// i.e., b should redirect to a.
|
||||
self.infcx().set(vb, b_id, redirect(a_id));
|
||||
set_var_to_merged_bounds(
|
||||
self, a_id, a_bounds, b_bounds, nde_a.rank)
|
||||
} else if nde_a.rank < nde_b.rank {
|
||||
debug!{"vars(): b has smaller rank"};
|
||||
// b has greater rank, so a should redirect to b.
|
||||
self.infcx().set(vb, a_id, redirect(b_id));
|
||||
set_var_to_merged_bounds(
|
||||
self, b_id, a_bounds, b_bounds, nde_b.rank)
|
||||
} else {
|
||||
debug!{"vars(): a and b have equal rank"};
|
||||
assert nde_a.rank == nde_b.rank;
|
||||
// If equal, just redirect one to the other and increment
|
||||
// the other's rank. We choose arbitrarily to redirect b
|
||||
// to a and increment a's rank.
|
||||
self.infcx().set(vb, b_id, redirect(a_id));
|
||||
set_var_to_merged_bounds(
|
||||
self, a_id, a_bounds, b_bounds, nde_a.rank + 1u
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// make variable a subtype of T
|
||||
fn var_sub_t<C: combine>(self: &C, a_id: ty::tv_vid, b: ty::t) -> ures {
|
||||
|
||||
let vb = &self.infcx().ty_var_bindings;
|
||||
let nde_a = self.infcx().get(vb, a_id);
|
||||
let a_id = nde_a.root;
|
||||
let a_bounds = nde_a.possible_types;
|
||||
|
||||
debug!{"var_sub_t(%s=%s <: %s)",
|
||||
a_id.to_str(),
|
||||
a_bounds.to_str(self.infcx()),
|
||||
b.to_str(self.infcx())};
|
||||
let b_bounds = {lb: none, ub: some(b)};
|
||||
set_var_to_merged_bounds(self, a_id, a_bounds, b_bounds, nde_a.rank)
|
||||
}
|
||||
|
||||
/// make T a subtype of variable
|
||||
fn t_sub_var<C: combine>(self: &C, a: ty::t, b_id: ty::tv_vid) -> ures {
|
||||
|
||||
let vb = &self.infcx().ty_var_bindings;
|
||||
let a_bounds = {lb: some(a), ub: none};
|
||||
let nde_b = self.infcx().get(vb, b_id);
|
||||
let b_id = nde_b.root;
|
||||
let b_bounds = nde_b.possible_types;
|
||||
|
||||
debug!{"t_sub_var(%s <: %s=%s)",
|
||||
a.to_str(self.infcx()),
|
||||
b_id.to_str(),
|
||||
b_bounds.to_str(self.infcx())};
|
||||
set_var_to_merged_bounds(self, b_id, a_bounds, b_bounds, nde_b.rank)
|
||||
}
|
||||
|
||||
fn bnds<C: combine>(
|
||||
self: &C, a: bound<ty::t>, b: bound<ty::t>) -> ures {
|
||||
|
||||
debug!{"bnds(%s <: %s)", a.to_str(self.infcx()), b.to_str(self.infcx())};
|
||||
do indent {
|
||||
match (a, b) {
|
||||
(none, none) |
|
||||
(some(_), none) |
|
||||
(none, some(_)) => {
|
||||
uok()
|
||||
}
|
||||
(some(t_a), some(t_b)) => {
|
||||
self.sub().tys(t_a, t_b).to_ures()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ______________________________________________________________________
|
||||
// Integral variables
|
||||
|
||||
impl infer_ctxt {
|
||||
fn vars_integral(a_id: ty::tvi_vid, b_id: ty::tvi_vid) -> ures {
|
||||
let vb = &self.ty_var_integral_bindings;
|
||||
|
||||
let nde_a = self.get(vb, a_id);
|
||||
let nde_b = self.get(vb, b_id);
|
||||
let a_id = nde_a.root;
|
||||
let b_id = nde_b.root;
|
||||
let a_pt = nde_a.possible_types;
|
||||
let b_pt = nde_b.possible_types;
|
||||
|
||||
// If we're already dealing with the same two variables,
|
||||
// there's nothing to do.
|
||||
if a_id == b_id { return uok(); }
|
||||
|
||||
// Otherwise, take the intersection of the two sets of
|
||||
// possible types.
|
||||
let intersection = intersection(a_pt, b_pt);
|
||||
if *intersection == INT_TY_SET_EMPTY {
|
||||
return err(ty::terr_no_integral_type);
|
||||
}
|
||||
|
||||
// Rank optimization
|
||||
if nde_a.rank > nde_b.rank {
|
||||
debug!{"vars_integral(): a has smaller rank"};
|
||||
// a has greater rank, so a should become b's parent,
|
||||
// i.e., b should redirect to a.
|
||||
self.set(vb, a_id, root(intersection, nde_a.rank));
|
||||
self.set(vb, b_id, redirect(a_id));
|
||||
} else if nde_a.rank < nde_b.rank {
|
||||
debug!{"vars_integral(): b has smaller rank"};
|
||||
// b has greater rank, so a should redirect to b.
|
||||
self.set(vb, b_id, root(intersection, nde_b.rank));
|
||||
self.set(vb, a_id, redirect(b_id));
|
||||
} else {
|
||||
debug!{"vars_integral(): a and b have equal rank"};
|
||||
assert nde_a.rank == nde_b.rank;
|
||||
// If equal, just redirect one to the other and increment
|
||||
// the other's rank. We choose arbitrarily to redirect b
|
||||
// to a and increment a's rank.
|
||||
self.set(vb, a_id, root(intersection, nde_a.rank + 1u));
|
||||
self.set(vb, b_id, redirect(a_id));
|
||||
};
|
||||
|
||||
uok()
|
||||
}
|
||||
|
||||
fn var_integral_sub_t(a_id: ty::tvi_vid, b: ty::t) -> ures {
|
||||
assert ty::type_is_integral(b);
|
||||
|
||||
let vb = &self.ty_var_integral_bindings;
|
||||
let nde_a = self.get(vb, a_id);
|
||||
let a_id = nde_a.root;
|
||||
let a_pt = nde_a.possible_types;
|
||||
|
||||
let intersection =
|
||||
intersection(a_pt,
|
||||
convert_integral_ty_to_int_ty_set(self.tcx, b));
|
||||
if *intersection == INT_TY_SET_EMPTY {
|
||||
return err(ty::terr_no_integral_type);
|
||||
}
|
||||
self.set(vb, a_id, root(intersection, nde_a.rank));
|
||||
uok()
|
||||
}
|
||||
|
||||
fn t_sub_var_integral(a: ty::t, b_id: ty::tvi_vid) -> ures {
|
||||
assert ty::type_is_integral(a);
|
||||
let vb = &self.ty_var_integral_bindings;
|
||||
|
||||
let nde_b = self.get(vb, b_id);
|
||||
let b_id = nde_b.root;
|
||||
let b_pt = nde_b.possible_types;
|
||||
|
||||
let intersection =
|
||||
intersection(b_pt,
|
||||
convert_integral_ty_to_int_ty_set(self.tcx, a));
|
||||
if *intersection == INT_TY_SET_EMPTY {
|
||||
return err(ty::terr_no_integral_type);
|
||||
}
|
||||
self.set(vb, b_id, root(intersection, nde_b.rank));
|
||||
uok()
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
import result::result;
|
||||
|
||||
trait region_scope {
|
||||
fn anon_region() -> result<ty::region, ~str>;
|
||||
fn named_region(id: ast::ident) -> result<ty::region, ~str>;
|
||||
fn anon_region(span: span) -> result<ty::region, ~str>;
|
||||
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str>;
|
||||
}
|
||||
|
||||
enum empty_rscope { empty_rscope }
|
||||
impl empty_rscope: region_scope {
|
||||
fn anon_region() -> result<ty::region, ~str> {
|
||||
fn anon_region(_span: span) -> result<ty::region, ~str> {
|
||||
result::ok(ty::re_static)
|
||||
}
|
||||
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
|
||||
fn named_region(_span: span, id: ast::ident) -> result<ty::region, ~str> {
|
||||
if *id == ~"static" { result::ok(ty::re_static) }
|
||||
else { result::err(~"only the static region is allowed here") }
|
||||
}
|
||||
@ -18,7 +18,7 @@ impl empty_rscope: region_scope {
|
||||
|
||||
enum type_rscope = bool;
|
||||
impl type_rscope: region_scope {
|
||||
fn anon_region() -> result<ty::region, ~str> {
|
||||
fn anon_region(_span: span) -> result<ty::region, ~str> {
|
||||
if *self {
|
||||
result::ok(ty::re_bound(ty::br_self))
|
||||
} else {
|
||||
@ -26,10 +26,11 @@ impl type_rscope: region_scope {
|
||||
must be declared with a region bound")
|
||||
}
|
||||
}
|
||||
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
|
||||
do empty_rscope.named_region(id).chain_err |_e| {
|
||||
if *id == ~"self" { self.anon_region() }
|
||||
else {
|
||||
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
|
||||
do empty_rscope.named_region(span, id).chain_err |_e| {
|
||||
if *id == ~"self" {
|
||||
self.anon_region(span)
|
||||
} else {
|
||||
result::err(~"named regions other than `self` are not \
|
||||
allowed as part of a type declaration")
|
||||
}
|
||||
@ -43,11 +44,11 @@ fn in_anon_rscope<RS: region_scope copy owned>(self: RS, r: ty::region)
|
||||
@anon_rscope({anon: r, base: self as region_scope})
|
||||
}
|
||||
impl @anon_rscope: region_scope {
|
||||
fn anon_region() -> result<ty::region, ~str> {
|
||||
fn anon_region(_span: span) -> result<ty::region, ~str> {
|
||||
result::ok(self.anon)
|
||||
}
|
||||
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
|
||||
self.base.named_region(id)
|
||||
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
|
||||
self.base.named_region(span, id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,11 +59,11 @@ fn in_binding_rscope<RS: region_scope copy owned>(self: RS)
|
||||
@binding_rscope({base: base})
|
||||
}
|
||||
impl @binding_rscope: region_scope {
|
||||
fn anon_region() -> result<ty::region, ~str> {
|
||||
fn anon_region(_span: span) -> result<ty::region, ~str> {
|
||||
result::ok(ty::re_bound(ty::br_anon))
|
||||
}
|
||||
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
|
||||
do self.base.named_region(id).chain_err |_e| {
|
||||
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
|
||||
do self.base.named_region(span, id).chain_err |_e| {
|
||||
result::ok(ty::re_bound(ty::br_named(id)))
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,19 @@ mod middle {
|
||||
}
|
||||
mod rscope;
|
||||
mod astconv;
|
||||
mod infer;
|
||||
mod infer {
|
||||
mod assignment;
|
||||
mod combine;
|
||||
mod glb;
|
||||
mod integral;
|
||||
mod lattice;
|
||||
mod lub;
|
||||
mod region_var_bindings;
|
||||
mod resolve;
|
||||
mod sub;
|
||||
mod to_str;
|
||||
mod unify;
|
||||
}
|
||||
mod collect;
|
||||
mod coherence;
|
||||
}
|
||||
|
@ -21,10 +21,31 @@ import syntax::{ast, ast_util};
|
||||
import syntax::ast_map;
|
||||
import driver::session::session;
|
||||
|
||||
/// Returns a string like "reference valid for the block at 27:31 in foo.rs"
|
||||
/// that attempts to explain a lifetime in a way it might plausibly be
|
||||
/// understood.
|
||||
fn note_and_explain_region(cx: ctxt, prefix: ~str, region: ty::region) {
|
||||
match explain_region_and_span(cx, region) {
|
||||
(str, some(span)) => {
|
||||
cx.sess.span_note(
|
||||
span,
|
||||
fmt!("%s %s", prefix, str));
|
||||
}
|
||||
(str, none) => {
|
||||
cx.sess.note(
|
||||
fmt!("%s %s", prefix, str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a string like "the block at 27:31" that attempts to explain a
|
||||
/// lifetime in a way it might plausibly be understood.
|
||||
fn explain_region(cx: ctxt, region: ty::region) -> ~str {
|
||||
let (res, _) = explain_region_and_span(cx, region);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
fn explain_region_and_span(cx: ctxt, region: ty::region)
|
||||
-> (~str, option<span>)
|
||||
{
|
||||
return match region {
|
||||
re_scope(node_id) => {
|
||||
match cx.items.find(node_id) {
|
||||
@ -33,14 +54,15 @@ fn explain_region(cx: ctxt, region: ty::region) -> ~str {
|
||||
}
|
||||
some(ast_map::node_expr(expr)) => {
|
||||
match expr.node {
|
||||
ast::expr_call(*) => { explain_span(cx, ~"call", expr.span) }
|
||||
ast::expr_match(*) => { explain_span(cx, ~"alt", expr.span) }
|
||||
_ => { explain_span(cx, ~"expression", expr.span) }
|
||||
ast::expr_call(*) => explain_span(cx, ~"call", expr.span),
|
||||
ast::expr_match(*) => explain_span(cx, ~"alt", expr.span),
|
||||
_ => explain_span(cx, ~"expression", expr.span)
|
||||
}
|
||||
}
|
||||
some(_) | none => {
|
||||
// this really should not happen
|
||||
fmt!{"unknown scope: %d. Please report a bug.", node_id}
|
||||
(fmt!("unknown scope: %d. Please report a bug.", node_id),
|
||||
none)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,30 +70,34 @@ fn explain_region(cx: ctxt, region: ty::region) -> ~str {
|
||||
re_free(id, br) => {
|
||||
match cx.items.find(id) {
|
||||
some(ast_map::node_block(blk)) => {
|
||||
fmt!{"the lifetime %s as defined on %s",
|
||||
bound_region_to_str(cx, br),
|
||||
explain_span(cx, ~"block", blk.span)}
|
||||
let (msg, opt_span) = explain_span(cx, ~"block", blk.span);
|
||||
(fmt!("the lifetime %s as defined on %s",
|
||||
bound_region_to_str(cx, br), msg),
|
||||
opt_span)
|
||||
}
|
||||
some(_) | none => {
|
||||
// this really should not happen
|
||||
fmt!{"the lifetime %s as defined on node %d",
|
||||
bound_region_to_str(cx, br), id}
|
||||
(fmt!("the lifetime %s as defined on node %d",
|
||||
bound_region_to_str(cx, br), id),
|
||||
none)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
re_static => { ~"the static lifetime" }
|
||||
re_static => { (~"the static lifetime", none) }
|
||||
|
||||
// I believe these cases should not occur (except when debugging,
|
||||
// perhaps)
|
||||
re_var(_) | re_bound(_) => {
|
||||
fmt!{"lifetime %?", region}
|
||||
(fmt!("lifetime %?", region), none)
|
||||
}
|
||||
};
|
||||
|
||||
fn explain_span(cx: ctxt, heading: ~str, span: span) -> ~str {
|
||||
fn explain_span(cx: ctxt, heading: ~str, span: span)
|
||||
-> (~str, option<span>)
|
||||
{
|
||||
let lo = codemap::lookup_char_pos_adj(cx.sess.codemap, span.lo);
|
||||
fmt!{"the %s at %u:%u", heading, lo.line, lo.col}
|
||||
(fmt!{"the %s at %u:%u", heading, lo.line, lo.col}, some(span))
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,30 +159,23 @@ fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> ~str {
|
||||
}
|
||||
}
|
||||
|
||||
// In general, if you are giving a region error message,
|
||||
// you should use `explain_region()` or, better yet,
|
||||
// `note_and_explain_region()`
|
||||
fn region_to_str(cx: ctxt, region: region) -> ~str {
|
||||
match region {
|
||||
re_scope(node_id) => {
|
||||
if cx.sess.ppregions() {
|
||||
fmt!{"&%s", re_scope_id_to_str(cx, node_id)}
|
||||
} else {
|
||||
~"&"
|
||||
}
|
||||
}
|
||||
re_bound(br) => {
|
||||
bound_region_to_str(cx, br)
|
||||
}
|
||||
re_free(id, br) => {
|
||||
if cx.sess.ppregions() {
|
||||
// For debugging, this version is sometimes helpful:
|
||||
fmt!{"{%d} %s", id, bound_region_to_str(cx, br)}
|
||||
} else {
|
||||
// But this version is what the user expects to see:
|
||||
bound_region_to_str(cx, br)
|
||||
}
|
||||
}
|
||||
if cx.sess.ppregions() {
|
||||
return fmt!("&%?", region);
|
||||
}
|
||||
|
||||
// These two should not be seen by end-users (very often, anyhow):
|
||||
re_var(id) => fmt!{"&%s", id.to_str()},
|
||||
// These printouts are concise. They do not contain all the information
|
||||
// the user might want to diagnose an error, but there is basically no way
|
||||
// to fit that into a short string. Hence the recommendation to use
|
||||
// `explain_region()` or `note_and_explain_region()`.
|
||||
match region {
|
||||
re_scope(node_id) => ~"&",
|
||||
re_bound(br) => bound_region_to_str(cx, br),
|
||||
re_free(id, br) => bound_region_to_str(cx, br),
|
||||
re_var(id) => ~"&",
|
||||
re_static => ~"&static"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
// error-pattern: reference is not valid outside of its lifetime
|
||||
use std;
|
||||
import std::arc;
|
||||
fn main() {
|
||||
@ -6,6 +5,7 @@ fn main() {
|
||||
let mut y = none;
|
||||
do x.write_downgrade |write_mode| {
|
||||
y = some(x.downgrade(write_mode));
|
||||
//~^ ERROR cannot infer an appropriate lifetime
|
||||
}
|
||||
// Adding this line causes a method unification failure instead
|
||||
// do (&option::unwrap(y)).read |state| { assert *state == 1; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
// error-pattern:expected `fn&<R0>()` but found `*u8`
|
||||
// error-pattern:expected `fn&()` but found `*u8`
|
||||
extern fn f() {
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,9 @@ fn repeater<A:copy>(v: @A) -> repeat<A> {
|
||||
fn main() {
|
||||
// Here, an error results as the type of y is inferred to
|
||||
// repeater<</3> where lt is the block.
|
||||
let y = { //~ ERROR reference is not valid outside of its lifetime
|
||||
let x: &blk/int = &3;
|
||||
let y = {
|
||||
let x: &blk/int = &3; //~ ERROR cannot infer an appropriate lifetime
|
||||
repeater(@x)
|
||||
};
|
||||
assert 3 == *(y.get()); //~ ERROR reference is not valid outside of its lifetime
|
||||
assert 3 == *(y.get());
|
||||
}
|
@ -7,5 +7,5 @@ fn apply_int(f: fn(int) -> int, a: int) -> int { f(a) }
|
||||
fn main() {
|
||||
let f = {|i| i};
|
||||
assert apply_int(f, 2) == 2;
|
||||
assert apply(f, 2) == 2; //~ ERROR expected argument mode ++
|
||||
assert apply(f, 2) == 2; //~ ERROR expected argument mode &&
|
||||
}
|
||||
|
@ -4,12 +4,12 @@ fn foo(cond: bool) {
|
||||
|
||||
let mut z: &blk/int;
|
||||
if cond {
|
||||
z = &x;
|
||||
z = &x; //~ ERROR cannot infer an appropriate lifetime due to conflicting requirements
|
||||
} else {
|
||||
let w: &blk/int = &x;
|
||||
z = w; //~ ERROR mismatched types
|
||||
z = w;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,9 @@ enum ast {
|
||||
}
|
||||
|
||||
fn mk_add_bad1(x: &a/ast, y: &b/ast) -> ast/&a {
|
||||
add(x, y) //~ ERROR mismatched types: expected `&a/ast/&a` but found `&b/ast/&b`
|
||||
add(x, y) //~ ERROR cannot infer an appropriate lifetime
|
||||
//~^ ERROR cannot infer an appropriate lifetime
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ enum ast {
|
||||
}
|
||||
|
||||
fn mk_add_bad2(x: &a/ast, y: &a/ast, z: &ast) -> ast {
|
||||
add(x, y) //~ ERROR mismatched types: expected `ast/&` but found `ast/&a`
|
||||
add(x, y)
|
||||
//~^ ERROR cannot infer an appropriate lifetime
|
||||
//~^^ ERROR cannot infer an appropriate lifetime
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
}
|
||||
|
10
src/test/compile-fail/regions-escape-bound-fn-2.rs
Normal file
10
src/test/compile-fail/regions-escape-bound-fn-2.rs
Normal file
@ -0,0 +1,10 @@
|
||||
fn with_int(f: fn(x: &int)) {
|
||||
let x = 3;
|
||||
f(&x);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut x = none;
|
||||
//~^ ERROR reference is not valid outside of its lifetime
|
||||
with_int(|y| x = some(y));
|
||||
}
|
9
src/test/compile-fail/regions-escape-bound-fn.rs
Normal file
9
src/test/compile-fail/regions-escape-bound-fn.rs
Normal file
@ -0,0 +1,9 @@
|
||||
fn with_int(f: fn(x: &int)) {
|
||||
let x = 3;
|
||||
f(&x);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut x: option<&int> = none; //~ ERROR cannot infer
|
||||
with_int(|y| x = some(y));
|
||||
}
|
@ -14,8 +14,9 @@ fn with<R: deref>(f: fn(x: &int) -> R) -> int {
|
||||
|
||||
fn return_it() -> int {
|
||||
with(|o| o)
|
||||
//~^ ERROR reference is not valid outside of its lifetime, &
|
||||
//~^^ ERROR reference is not valid outside of its lifetime, &
|
||||
//~^ ERROR reference is not valid outside of its lifetime
|
||||
//~^^ ERROR reference is not valid outside of its lifetime
|
||||
//~^^^ ERROR cannot infer an appropriate lifetime
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -17,7 +17,7 @@ mod argparse {
|
||||
|
||||
impl Flag {
|
||||
fn set_desc(self, s: &str) -> Flag {
|
||||
Flag { //~ ERROR mismatched types
|
||||
Flag { //~ ERROR cannot infer an appropriate lifetime
|
||||
name: self.name,
|
||||
desc: s,
|
||||
max_count: self.max_count,
|
||||
|
@ -6,7 +6,7 @@ fn with<T>(f: fn(x: &int) -> T) -> T {
|
||||
|
||||
fn manip(x: &a/int) -> int {
|
||||
let z = do with |y| { select(x, y) };
|
||||
//~^ ERROR reference is not valid outside of its lifetime
|
||||
//~^ ERROR cannot infer an appropriate lifetime
|
||||
*z
|
||||
}
|
||||
|
||||
|
@ -2,17 +2,17 @@ fn ignore<T>(t: T) {}
|
||||
|
||||
fn nested(x: &x/int) {
|
||||
let y = 3;
|
||||
let mut ay = &y;
|
||||
let mut ay = &y; //~ ERROR cannot infer an appropriate lifetime
|
||||
|
||||
ignore(fn&(z: &z/int) {
|
||||
ay = x;
|
||||
ay = &y;
|
||||
ay = z; //~ ERROR mismatched types
|
||||
ay = &y; //~ ERROR cannot infer an appropriate lifetime
|
||||
ay = z;
|
||||
});
|
||||
|
||||
ignore(fn&(z: &z/int) -> &z/int {
|
||||
if false { return x; } //~ ERROR mismatched types
|
||||
if false { return ay; } //~ ERROR mismatched types
|
||||
if false { return ay; }
|
||||
return z;
|
||||
});
|
||||
}
|
||||
|
@ -9,20 +9,14 @@ fn nested(x: &x/int) { // (1)
|
||||
z: &z/int) -> &z/int) // A fresh region `z` (3)
|
||||
-> &x/int {
|
||||
|
||||
if false { return z(x, x, x); } //~ ERROR mismatched types: expected `&y/int` but found `&x/int`
|
||||
if false { return z(x, x, y); } //~ ERROR mismatched types: expected `&y/int` but found `&x/int`
|
||||
//~^ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
if false { return z(x, y, x); }
|
||||
if false { return z(x, y, y); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
if false { return z(y, x, x); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
//~^ ERROR mismatched types: expected `&y/int` but found `&x/int`
|
||||
if false { return z(y, x, y); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
//~^ ERROR mismatched types: expected `&y/int` but found `&x/int`
|
||||
//~^^ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
if false { return z(y, y, x); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
if false { return z(y, y, y); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
//~^ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
fail;
|
||||
|
||||
if false { return z(x, y, y); }
|
||||
//~^ ERROR cannot infer an appropriate lifetime
|
||||
|
||||
return z(y, x, x);
|
||||
//~^ ERROR mismatched types: expected `&x/int` but found `&y/int`
|
||||
//~^^ ERROR mismatched types: expected `&y/int` but found `&x/int`
|
||||
}
|
||||
) |foo| {
|
||||
|
||||
@ -40,8 +34,13 @@ fn nested(x: &x/int) { // (1)
|
||||
//
|
||||
// let f: &x/int = foo(&z, &z, |_x, _y, z| z ); // ERROR mismatched types: expected `&x/int` but found
|
||||
|
||||
foo(x, &z, |x, _y, _z| x ); //~ ERROR mismatched types: expected `&z/int` but found `&x/int`
|
||||
foo(x, &z, |_x, y, _z| y ); //~ ERROR mismatched types: expected `&z/int` but found `&
|
||||
foo(x, &z, |x, _y, _z| x); //~ ERROR mismatched types: expected `&z/int` but found `&x/int`
|
||||
|
||||
// Note: originally I had foo(x, &z, ...) here, but in that
|
||||
// case the region inferencer deduced that this was valid if
|
||||
// &y==&static, and so inference would succeed but borrow
|
||||
// check would fail because the lifetime of &z is not &static.
|
||||
foo(x, x, |_x, y, _z| y); //~ ERROR cannot infer an appropriate lifetime
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,8 @@ impl has_ctxt: get_ctxt {
|
||||
|
||||
fn make_gc() -> get_ctxt {
|
||||
let ctxt = { v: 22u };
|
||||
let hc = { c: &ctxt };
|
||||
return hc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&`
|
||||
let hc = { c: &ctxt }; //~ ERROR illegal borrow
|
||||
return hc as get_ctxt;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -7,7 +7,7 @@ fn make_gc1(gc: get_ctxt/&a) -> get_ctxt/&b {
|
||||
}
|
||||
|
||||
fn make_gc2(gc: get_ctxt/&a) -> get_ctxt/&b {
|
||||
return gc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&b` but found `@get_ctxt/&a`
|
||||
return gc as get_ctxt; //~ ERROR cannot infer an appropriate lifetime
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// error-pattern: reference is not valid outside of its lifetime
|
||||
// error-pattern: cannot infer an appropriate lifetime
|
||||
use std;
|
||||
import std::sync;
|
||||
fn main() {
|
||||
|
@ -3,7 +3,7 @@ type bar = {a: int, b: uint};
|
||||
|
||||
fn want_foo(f: foo) {}
|
||||
fn have_bar(b: bar) {
|
||||
want_foo(b); //~ ERROR (in field `b`, int vs uint)
|
||||
want_foo(b); //~ ERROR (in field `b`, expected int but found uint)
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -3,7 +3,7 @@ type bar = @foo;
|
||||
|
||||
fn want_foo(f: foo) {}
|
||||
fn have_bar(b: bar) {
|
||||
want_foo(b); //~ ERROR (record vs @-ptr)
|
||||
want_foo(b); //~ ERROR (expected record but found @-ptr)
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -17,8 +17,8 @@ fn main() {
|
||||
let a = &"aaaa";
|
||||
let b = &"bbbb";
|
||||
|
||||
// let c = &"cccc";
|
||||
// let cc = &"ccccc";
|
||||
let c = &"cccc";
|
||||
let cc = &"ccccc";
|
||||
|
||||
log(debug, a);
|
||||
|
||||
@ -30,9 +30,6 @@ fn main() {
|
||||
|
||||
log(debug, b);
|
||||
|
||||
// FIXME #3138: So then, why don't these ones work?
|
||||
|
||||
/*
|
||||
assert a < c;
|
||||
assert a <= c;
|
||||
assert a != c;
|
||||
@ -48,5 +45,4 @@ fn main() {
|
||||
assert cc > c;
|
||||
|
||||
log(debug, cc);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
|
||||
|
||||
fn test_fn() {
|
||||
type t = extern fn() -> int;
|
||||
type t = fn@() -> int;
|
||||
fn ten() -> int { return 10; }
|
||||
let rs: t = { ten };
|
||||
assert (rs() == 10);
|
||||
//assert (rs() == 10);
|
||||
}
|
||||
|
||||
fn main() { test_fn(); }
|
||||
|
Loading…
Reference in New Issue
Block a user