prevent regions from escaping in ifaces; remove &r.T syntax
This commit is contained in:
@@ -272,7 +272,7 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
|
||||
w.write_char('I');
|
||||
w.write_uint(id.to_uint());
|
||||
}
|
||||
ty::ty_param(id, did) {
|
||||
ty::ty_param({idx: id, def_id: did}) {
|
||||
w.write_char('p');
|
||||
w.write_str(cx.ds(did));
|
||||
w.write_char('|');
|
||||
|
||||
@@ -115,7 +115,7 @@ fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
|
||||
fn check_for_box(cx: ctx, id: node_id, fv: option<@freevar_entry>,
|
||||
is_move: bool, var_t: ty::t, sp: span) {
|
||||
// all captured data must be owned
|
||||
if !check_owned(cx, var_t, sp) { ret; }
|
||||
if !check_owned(cx.tcx, var_t, sp) { ret; }
|
||||
|
||||
// copied in data must be copyable, but moved in data can be anything
|
||||
let is_implicit = fv.is_some();
|
||||
@@ -217,7 +217,13 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
alt e.node {
|
||||
expr_assign(_, ex) |
|
||||
expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |
|
||||
expr_ret(some(ex)) | expr_cast(ex, _) { maybe_copy(cx, ex); }
|
||||
expr_ret(some(ex)) {
|
||||
maybe_copy(cx, ex);
|
||||
}
|
||||
expr_cast(source, _) {
|
||||
maybe_copy(cx, source);
|
||||
check_cast_for_escaping_regions(cx, source, e);
|
||||
}
|
||||
expr_copy(expr) { check_copy_ex(cx, expr, false); }
|
||||
// Vector add copies, but not "implicitly"
|
||||
expr_assign_op(_, _, ex) { check_copy_ex(cx, ex, false) }
|
||||
@@ -440,15 +446,92 @@ fn check_send(cx: ctx, ty: ty::t, sp: span) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_owned(cx: ctx, ty: ty::t, sp: span) -> bool {
|
||||
if !ty::kind_is_owned(ty::type_kind(cx.tcx, ty)) {
|
||||
cx.tcx.sess.span_err(sp, ~"not an owned value");
|
||||
// note: also used from middle::typeck::regionck!
|
||||
fn check_owned(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool {
|
||||
if !ty::kind_is_owned(ty::type_kind(tcx, ty)) {
|
||||
alt ty::get(ty).struct {
|
||||
ty::ty_param(*) {
|
||||
tcx.sess.span_err(sp, ~"value may contain borrowed \
|
||||
pointers; use `owned` bound");
|
||||
}
|
||||
_ {
|
||||
tcx.sess.span_err(sp, ~"value may contain borrowed \
|
||||
pointers");
|
||||
}
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// This is rather subtle. When we are casting a value to a
|
||||
/// instantiated iface like `a as iface/&r`, regionck already ensures
|
||||
/// that any borrowed pointers that appear in the type of `a` are
|
||||
/// bounded by `&r`. However, it is possible that there are *type
|
||||
/// parameters* in the type of `a`, and those *type parameters* may
|
||||
/// have borrowed pointers within them. We have to guarantee that the
|
||||
/// regions which appear in those type parameters are not obscured.
|
||||
///
|
||||
/// Therefore, we ensure that one of three conditions holds:
|
||||
///
|
||||
/// (1) The iface instance cannot escape the current fn. This is
|
||||
/// guaranteed if the region bound `&r` is some scope within the fn
|
||||
/// itself. This case is safe because whatever borrowed pointers are
|
||||
/// found within the type parameter, they must enclose the fn body
|
||||
/// itself.
|
||||
///
|
||||
/// (2) The type parameter appears in the type of the iface. For
|
||||
/// example, if the type parameter is `T` and the iface type is
|
||||
/// `deque<T>`, then whatever borrowed ptrs may appear in `T` also
|
||||
/// appear in `deque<T>`.
|
||||
///
|
||||
/// (3) The type parameter is owned (and therefore does not contain
|
||||
/// borrowed ptrs).
|
||||
fn check_cast_for_escaping_regions(
|
||||
cx: ctx,
|
||||
source: @expr,
|
||||
target: @expr) {
|
||||
|
||||
// Determine what type we are casting to; if it is not an iface, then no
|
||||
// worries.
|
||||
let target_ty = ty::expr_ty(cx.tcx, target);
|
||||
let target_substs = alt ty::get(target_ty).struct {
|
||||
ty::ty_trait(_, substs) => {substs}
|
||||
_ => { ret; /* not a cast to a trait */ }
|
||||
};
|
||||
|
||||
// Check, based on the region associated with the iface, whether it can
|
||||
// possibly escape the enclosing fn item (note that all type parameters
|
||||
// must have been declared on the enclosing fn item):
|
||||
alt target_substs.self_r {
|
||||
some(ty::re_scope(*)) => { ret; /* case (1) */ }
|
||||
none | some(ty::re_static) | some(ty::re_free(*)) => {}
|
||||
some(ty::re_bound(*)) | some(ty::re_var(*)) => {
|
||||
cx.tcx.sess.span_bug(
|
||||
source.span,
|
||||
#fmt["bad region found in kind: %?", target_substs.self_r]);
|
||||
}
|
||||
}
|
||||
|
||||
// Assuming the iface instance can escape, then ensure that each parameter
|
||||
// either appears in the iface type or is owned:
|
||||
let target_params = ty::param_tys_in_type(target_ty);
|
||||
let source_ty = ty::expr_ty(cx.tcx, source);
|
||||
do ty::walk_ty(source_ty) |ty| {
|
||||
alt ty::get(ty).struct {
|
||||
ty::ty_param(source_param) => {
|
||||
if target_params.contains(source_param) {
|
||||
/* case (2) */
|
||||
} else {
|
||||
check_owned(cx.tcx, ty, source.span); /* case (3) */
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
|
||||
@@ -1317,7 +1317,7 @@ enum copy_action { INIT, DROP_EXISTING, }
|
||||
fn type_is_structural_or_param(t: ty::t) -> bool {
|
||||
if ty::type_is_structural(t) { ret true; }
|
||||
alt ty::get(t).struct {
|
||||
ty::ty_param(_, _) { ret true; }
|
||||
ty::ty_param(*) { ret true; }
|
||||
_ { ret false; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ impl methods for reflector {
|
||||
ty::ty_trait(_, _) { self.leaf(~"trait") }
|
||||
ty::ty_var(_) { self.leaf(~"var") }
|
||||
ty::ty_var_integral(_) { self.leaf(~"var_integral") }
|
||||
ty::ty_param(n, _) { self.visit(~"param", ~[self.c_uint(n)]) }
|
||||
ty::ty_param(p) { self.visit(~"param", ~[self.c_uint(p.idx)]) }
|
||||
ty::ty_self { self.leaf(~"self") }
|
||||
ty::ty_type { self.leaf(~"type") }
|
||||
ty::ty_opaque_box { self.leaf(~"opaque_box") }
|
||||
|
||||
@@ -137,8 +137,8 @@ fn type_needs_inner(cx: ctx, use: uint, ty: ty::t,
|
||||
}
|
||||
false
|
||||
}
|
||||
ty::ty_param(n, _) {
|
||||
cx.uses[n] |= use;
|
||||
ty::ty_param(p) {
|
||||
cx.uses[p.idx] |= use;
|
||||
false
|
||||
}
|
||||
_ { true }
|
||||
|
||||
@@ -166,6 +166,7 @@ export terr_regions_differ, terr_mutability, terr_purity_mismatch;
|
||||
export terr_proto_mismatch;
|
||||
export terr_ret_style_mismatch;
|
||||
export purity_to_str;
|
||||
export param_tys_in_type;
|
||||
|
||||
// Data types
|
||||
|
||||
@@ -312,6 +313,8 @@ type fn_ty = {purity: ast::purity,
|
||||
output: t,
|
||||
ret_style: ret_style};
|
||||
|
||||
type param_ty = {idx: uint, def_id: def_id};
|
||||
|
||||
// See discussion at head of region.rs
|
||||
enum region {
|
||||
re_bound(bound_region),
|
||||
@@ -370,7 +373,7 @@ enum sty {
|
||||
ty_var(tv_vid), // type variable during typechecking
|
||||
ty_var_integral(tvi_vid), // type variable during typechecking, for
|
||||
// integral types only
|
||||
ty_param(uint, def_id), // type parameter
|
||||
ty_param(param_ty), // type parameter
|
||||
ty_self, // special, implicit `self` type parameter
|
||||
|
||||
// "Fake" types, used for trans purposes
|
||||
@@ -579,7 +582,7 @@ fn mk_t_with_id(cx: ctxt, st: sty, o_def_id: option<ast::def_id>) -> t {
|
||||
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) |
|
||||
ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) |
|
||||
ty_opaque_box {}
|
||||
ty_param(_, _) { flags |= has_params as uint; }
|
||||
ty_param(_) { flags |= has_params as uint; }
|
||||
ty_var(_) | ty_var_integral(_) { flags |= needs_infer as uint; }
|
||||
ty_self { flags |= has_self as uint; }
|
||||
ty_enum(_, substs) | ty_class(_, substs) | ty_trait(_, substs) {
|
||||
@@ -713,7 +716,9 @@ fn mk_var_integral(cx: ctxt, v: tvi_vid) -> t {
|
||||
|
||||
fn mk_self(cx: ctxt) -> t { mk_t(cx, ty_self) }
|
||||
|
||||
fn mk_param(cx: ctxt, n: uint, k: def_id) -> t { mk_t(cx, ty_param(n, k)) }
|
||||
fn mk_param(cx: ctxt, n: uint, k: def_id) -> t {
|
||||
mk_t(cx, ty_param({idx: n, def_id: k}))
|
||||
}
|
||||
|
||||
fn mk_type(cx: ctxt) -> t { mk_t(cx, ty_type) }
|
||||
|
||||
@@ -761,7 +766,7 @@ fn maybe_walk_ty(ty: t, f: fn(t) -> bool) {
|
||||
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
|
||||
ty_estr(_) | ty_type | ty_opaque_box | ty_self |
|
||||
ty_opaque_closure_ptr(_) | ty_var(_) | ty_var_integral(_) |
|
||||
ty_param(_, _) {
|
||||
ty_param(_) {
|
||||
}
|
||||
ty_box(tm) | ty_evec(tm, _) | ty_unboxed_vec(tm) |
|
||||
ty_ptr(tm) | ty_rptr(_, tm) {
|
||||
@@ -982,7 +987,7 @@ fn subst_tps(cx: ctxt, tps: ~[t], typ: t) -> t {
|
||||
let tb = ty::get(typ);
|
||||
if !tbox_has_flag(tb, has_params) { ret typ; }
|
||||
alt tb.struct {
|
||||
ty_param(idx, _) { tps[idx] }
|
||||
ty_param(p) { tps[p.idx] }
|
||||
sty { fold_sty_to_ty(cx, sty, |t| subst_tps(cx, tps, t)) }
|
||||
}
|
||||
}
|
||||
@@ -1019,7 +1024,7 @@ fn subst(cx: ctxt,
|
||||
let tb = get(typ);
|
||||
if !tbox_has_flag(tb, needs_subst) { ret typ; }
|
||||
alt tb.struct {
|
||||
ty_param(idx, _) {substs.tps[idx]}
|
||||
ty_param(p) {substs.tps[p.idx]}
|
||||
ty_self {substs.self_ty.get()}
|
||||
_ {
|
||||
fold_regions_and_ty(
|
||||
@@ -1608,8 +1613,8 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
|
||||
lowest
|
||||
}
|
||||
|
||||
ty_param(_, did) {
|
||||
param_bounds_to_kind(cx.ty_param_bounds.get(did.node))
|
||||
ty_param(p) {
|
||||
param_bounds_to_kind(cx.ty_param_bounds.get(p.def_id.node))
|
||||
}
|
||||
|
||||
// self is a special type parameter that can only appear in ifaces; it
|
||||
@@ -1666,7 +1671,7 @@ fn is_instantiable(cx: ctxt, r_ty: t) -> bool {
|
||||
ty_fn(_) |
|
||||
ty_var(_) |
|
||||
ty_var_integral(_) |
|
||||
ty_param(_, _) |
|
||||
ty_param(_) |
|
||||
ty_self |
|
||||
ty_type |
|
||||
ty_opaque_box |
|
||||
@@ -1855,7 +1860,7 @@ fn type_is_pod(cx: ctxt, ty: t) -> bool {
|
||||
ty_evec(mt, vstore_fixed(_)) | ty_unboxed_vec(mt) {
|
||||
result = type_is_pod(cx, mt.ty);
|
||||
}
|
||||
ty_param(_, _) { result = false; }
|
||||
ty_param(_) { result = false; }
|
||||
ty_opaque_closure_ptr(_) { result = true; }
|
||||
ty_class(did, substs) {
|
||||
result = vec::any(lookup_class_fields(cx, did), |f| {
|
||||
@@ -1899,7 +1904,7 @@ fn type_is_c_like_enum(cx: ctxt, ty: t) -> bool {
|
||||
|
||||
fn type_param(ty: t) -> option<uint> {
|
||||
alt get(ty).struct {
|
||||
ty_param(id, _) { ret some(id); }
|
||||
ty_param(p) { ret some(p.idx); }
|
||||
_ {/* fall through */ }
|
||||
}
|
||||
ret none;
|
||||
@@ -2038,7 +2043,7 @@ fn hash_type_structure(st: sty) -> uint {
|
||||
ty_self { 28u }
|
||||
ty_var(v) { hash_uint(29u, v.to_uint()) }
|
||||
ty_var_integral(v) { hash_uint(30u, v.to_uint()) }
|
||||
ty_param(pid, did) { hash_def(hash_uint(31u, pid), did) }
|
||||
ty_param(p) { hash_def(hash_uint(31u, p.idx), p.def_id) }
|
||||
ty_type { 32u }
|
||||
ty_bot { 34u }
|
||||
ty_ptr(mt) { hash_subty(35u, mt.ty) }
|
||||
@@ -2219,6 +2224,22 @@ fn method_idx(id: ast::ident, meths: ~[method]) -> option<uint> {
|
||||
ret none;
|
||||
}
|
||||
|
||||
/// Returns a vector containing the indices of all type parameters that appear
|
||||
/// in `ty`. The vector may contain duplicates. Probably should be converted
|
||||
/// to a bitset or some other representation.
|
||||
fn param_tys_in_type(ty: t) -> ~[param_ty] {
|
||||
let mut rslt = ~[];
|
||||
do walk_ty(ty) |ty| {
|
||||
alt get(ty).struct {
|
||||
ty_param(p) {
|
||||
vec::push(rslt, p);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
rslt
|
||||
}
|
||||
|
||||
fn occurs_check(tcx: ctxt, sp: span, vid: tv_vid, rt: t) {
|
||||
|
||||
// Returns a vec of all the type variables occurring in `ty`. It may
|
||||
@@ -2341,7 +2362,7 @@ fn ty_sort_str(cx: ctxt, t: t) -> ~str {
|
||||
ty_tup(_) { ~"tuple" }
|
||||
ty_var(_) { ~"variable" }
|
||||
ty_var_integral(_) { ~"integral variable" }
|
||||
ty_param(_, _) { ~"type parameter" }
|
||||
ty_param(_) { ~"type parameter" }
|
||||
ty_self { ~"self" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ fn get_region_reporting_err(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
|
||||
fn ast_region_to_region<AC: ast_conv, RS: region_scope>(
|
||||
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 = alt a_r.node {
|
||||
@@ -79,7 +79,7 @@ fn ast_region_to_region<AC: ast_conv, RS: region_scope>(
|
||||
get_region_reporting_err(self.tcx(), span, res)
|
||||
}
|
||||
|
||||
fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy>(
|
||||
fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy owned>(
|
||||
self: AC, rscope: RS, did: ast::def_id,
|
||||
path: @ast::path) -> ty_param_substs_and_ty {
|
||||
|
||||
@@ -128,7 +128,7 @@ fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy>(
|
||||
{substs: substs, ty: ty::subst(tcx, substs, decl_ty)}
|
||||
}
|
||||
|
||||
fn ast_path_to_ty<AC: ast_conv, RS: region_scope copy>(
|
||||
fn ast_path_to_ty<AC: ast_conv, RS: region_scope copy owned>(
|
||||
self: AC,
|
||||
rscope: RS,
|
||||
did: ast::def_id,
|
||||
@@ -151,10 +151,10 @@ const NO_TPS: uint = 2u;
|
||||
// Parses the programmer's textual representation of a type into our
|
||||
// internal notion of a type. `getter` is a function that returns the type
|
||||
// corresponding to a definition ID:
|
||||
fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
|
||||
fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy owned>(
|
||||
self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t {
|
||||
|
||||
fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy>(
|
||||
fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy owned>(
|
||||
self: AC, rscope: RS, mt: ast::mt) -> ty::mt {
|
||||
|
||||
ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl};
|
||||
@@ -162,7 +162,7 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
|
||||
|
||||
// Handle @, ~, and & being able to mean estrs and evecs.
|
||||
// If a_seq_ty is a str or a vec, make it an estr/evec
|
||||
fn mk_maybe_vstore<AC: ast_conv, RS: region_scope copy>(
|
||||
fn mk_maybe_vstore<AC: ast_conv, RS: region_scope copy owned>(
|
||||
self: AC, rscope: RS, a_seq_ty: ast::mt, vst: ty::vstore,
|
||||
constr: fn(ty::mt) -> ty::t) -> ty::t {
|
||||
|
||||
@@ -351,7 +351,7 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
|
||||
ret typ;
|
||||
}
|
||||
|
||||
fn ty_of_arg<AC: ast_conv, RS: region_scope copy>(
|
||||
fn ty_of_arg<AC: ast_conv, RS: region_scope copy owned>(
|
||||
self: AC, rscope: RS, a: ast::arg,
|
||||
expected_ty: option<ty::arg>) -> ty::arg {
|
||||
|
||||
@@ -394,7 +394,7 @@ fn ty_of_arg<AC: ast_conv, RS: region_scope copy>(
|
||||
type expected_tys = option<{inputs: ~[ty::arg],
|
||||
output: ty::t}>;
|
||||
|
||||
fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy>(
|
||||
fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy owned>(
|
||||
self: AC, rscope: RS,
|
||||
proto: ast::proto,
|
||||
decl: ast::fn_decl,
|
||||
|
||||
@@ -2154,7 +2154,7 @@ fn check_bounds_are_used(ccx: @crate_ctxt,
|
||||
|_r| {},
|
||||
|t| {
|
||||
alt ty::get(t).struct {
|
||||
ty::ty_param(idx, _) { tps_used[idx] = true; }
|
||||
ty::ty_param({idx, _}) { tps_used[idx] = true; }
|
||||
_ { }
|
||||
}
|
||||
true
|
||||
|
||||
@@ -87,8 +87,8 @@ class lookup {
|
||||
loop {
|
||||
// First, see whether this is an interface-bounded parameter.
|
||||
alt ty::get(self.self_ty).struct {
|
||||
ty::ty_param(n, did) {
|
||||
self.add_candidates_from_param(n, did);
|
||||
ty::ty_param(p) {
|
||||
self.add_candidates_from_param(p.idx, p.def_id);
|
||||
}
|
||||
ty::ty_trait(did, substs) {
|
||||
self.add_candidates_from_trait(did, substs);
|
||||
|
||||
@@ -20,23 +20,59 @@ this point a bit better.
|
||||
import util::ppaux;
|
||||
import syntax::print::pprust;
|
||||
import infer::{resolve_type, resolve_all, force_all,
|
||||
resolve_rvar, force_rvar};
|
||||
resolve_rvar, force_rvar, fres};
|
||||
import middle::kind::check_owned;
|
||||
|
||||
type rcx = @{fcx: @fn_ctxt, mut errors_reported: uint};
|
||||
type rvt = visit::vt<rcx>;
|
||||
enum rcx { rcx_({fcx: @fn_ctxt, mut errors_reported: uint}) }
|
||||
type rvt = visit::vt<@rcx>;
|
||||
|
||||
impl methods for @rcx {
|
||||
/// Try to resolve the type for the given node.
|
||||
///
|
||||
/// Note one important point: we do not attempt to resolve *region
|
||||
/// variables* here. This is because regionck is essentially adding
|
||||
/// constraints to those region variables and so may yet influence
|
||||
/// how they are resolved.
|
||||
///
|
||||
/// Consider this silly example:
|
||||
///
|
||||
/// fn borrow(x: &int) -> &int {x}
|
||||
/// fn foo(x: @int) -> int { /* block: B */
|
||||
/// let b = borrow(x); /* region: <R0> */
|
||||
/// *b
|
||||
/// }
|
||||
///
|
||||
/// Here, the region of `b` will be `<R0>`. `<R0>` is constrainted
|
||||
/// to be some subregion of the block B and some superregion of
|
||||
/// the call. If we forced it now, we'd choose the smaller region
|
||||
/// (the call). But that would make the *b illegal. Since we don't
|
||||
/// resolve, the type of b will be `&<R0>.int` and then `*b` will require
|
||||
/// that `<R0>` be bigger than the let and the `*b` expression, so we
|
||||
/// 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))
|
||||
}
|
||||
|
||||
/// Try to resolve the type for the given node.
|
||||
fn resolve_node_type(id: ast::node_id) -> fres<ty::t> {
|
||||
self.resolve_type(self.fcx.node_ty(id))
|
||||
}
|
||||
}
|
||||
|
||||
fn regionck_expr(fcx: @fn_ctxt, e: @ast::expr) {
|
||||
let rcx = @{fcx:fcx, mut errors_reported: 0u};
|
||||
let rcx = rcx_({fcx:fcx, mut errors_reported: 0u});
|
||||
let v = regionck_visitor();
|
||||
v.visit_expr(e, rcx, v);
|
||||
v.visit_expr(e, @rcx, v);
|
||||
}
|
||||
|
||||
fn regionck_fn(fcx: @fn_ctxt,
|
||||
_decl: ast::fn_decl,
|
||||
blk: ast::blk) {
|
||||
let rcx = @{fcx:fcx, mut errors_reported: 0u};
|
||||
let rcx = rcx_({fcx:fcx, mut errors_reported: 0u});
|
||||
let v = regionck_visitor();
|
||||
v.visit_block(blk, rcx, v);
|
||||
v.visit_block(blk, @rcx, v);
|
||||
}
|
||||
|
||||
fn regionck_visitor() -> rvt {
|
||||
@@ -49,11 +85,11 @@ fn regionck_visitor() -> rvt {
|
||||
with *visit::default_visitor()})
|
||||
}
|
||||
|
||||
fn visit_item(_item: @ast::item, &&_rcx: rcx, _v: rvt) {
|
||||
fn visit_item(_item: @ast::item, &&_rcx: @rcx, _v: rvt) {
|
||||
// Ignore items
|
||||
}
|
||||
|
||||
fn visit_local(l: @ast::local, &&rcx: rcx, v: rvt) {
|
||||
fn visit_local(l: @ast::local, &&rcx: @rcx, v: rvt) {
|
||||
let e = rcx.errors_reported;
|
||||
v.visit_pat(l.node.pat, rcx, v);
|
||||
if e != rcx.errors_reported {
|
||||
@@ -66,7 +102,7 @@ fn visit_local(l: @ast::local, &&rcx: rcx, v: rvt) {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_pat(p: @ast::pat, &&rcx: rcx, v: rvt) {
|
||||
fn visit_pat(p: @ast::pat, &&rcx: @rcx, v: rvt) {
|
||||
let fcx = rcx.fcx;
|
||||
alt p.node {
|
||||
ast::pat_ident(path, _)
|
||||
@@ -80,11 +116,11 @@ fn visit_pat(p: @ast::pat, &&rcx: rcx, v: rvt) {
|
||||
visit::visit_pat(p, rcx, v);
|
||||
}
|
||||
|
||||
fn visit_block(b: ast::blk, &&rcx: rcx, v: rvt) {
|
||||
fn visit_block(b: ast::blk, &&rcx: @rcx, v: rvt) {
|
||||
visit::visit_block(b, rcx, v);
|
||||
}
|
||||
|
||||
fn visit_expr(e: @ast::expr, &&rcx: rcx, v: rvt) {
|
||||
fn visit_expr(e: @ast::expr, &&rcx: @rcx, v: rvt) {
|
||||
#debug["visit_expr(e=%s)", pprust::expr_to_str(e)];
|
||||
|
||||
alt e.node {
|
||||
@@ -99,6 +135,41 @@ fn visit_expr(e: @ast::expr, &&rcx: rcx, v: rvt) {
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
|
||||
ast::expr_cast(source, _) {
|
||||
// Determine if we are casting `source` to an iface instance.
|
||||
// If so, we have to be sure that the type of the source obeys
|
||||
// the iface's region bound.
|
||||
//
|
||||
// Note: there is a subtle point here concerning type
|
||||
// parameters. It is possible that the type of `source`
|
||||
// contains type parameters, which in turn may contain regions
|
||||
// that are not visible to us (only the caller knows about
|
||||
// them). The kind checker is ultimately responsible for
|
||||
// guaranteeing region safety in that particular case. There
|
||||
// is an extensive comment on the function
|
||||
// check_cast_for_escaping_regions() in kind.rs explaining how
|
||||
// it goes about doing that.
|
||||
alt rcx.resolve_node_type(e.id) {
|
||||
result::err(_) => { ret; /* typeck will fail anyhow */ }
|
||||
result::ok(target_ty) => {
|
||||
alt ty::get(target_ty).struct {
|
||||
ty::ty_trait(_, substs) {
|
||||
let iface_region = alt substs.self_r {
|
||||
some(r) => {r}
|
||||
none => {ty::re_static}
|
||||
};
|
||||
let source_ty = rcx.fcx.expr_ty(source);
|
||||
constrain_regions_in_type(rcx, iface_region,
|
||||
e.span, source_ty);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
_ { }
|
||||
}
|
||||
|
||||
@@ -106,7 +177,7 @@ fn visit_expr(e: @ast::expr, &&rcx: rcx, v: rvt) {
|
||||
visit::visit_expr(e, rcx, v);
|
||||
}
|
||||
|
||||
fn visit_stmt(s: @ast::stmt, &&rcx: rcx, v: rvt) {
|
||||
fn visit_stmt(s: @ast::stmt, &&rcx: @rcx, v: rvt) {
|
||||
visit::visit_stmt(s, rcx, v);
|
||||
}
|
||||
|
||||
@@ -114,37 +185,13 @@ fn visit_stmt(s: @ast::stmt, &&rcx: rcx, v: rvt) {
|
||||
// references a region that is not in scope for that node. Returns
|
||||
// false if an error is reported; this is used to cause us to cut off
|
||||
// region checking for that subtree to avoid reporting tons of errors.
|
||||
fn visit_node(id: ast::node_id, span: span, rcx: rcx) -> bool {
|
||||
fn visit_node(id: ast::node_id, span: span, rcx: @rcx) -> bool {
|
||||
let fcx = rcx.fcx;
|
||||
|
||||
// Try to resolve the type. If we encounter an error, then typeck
|
||||
// is going to fail anyway, so just stop here and let typeck
|
||||
// report errors later on in the writeback phase.
|
||||
//
|
||||
// Note one important point: we do not attempt to resolve *region
|
||||
// variables* here. This is because regionck is essentially adding
|
||||
// constraints to those region variables and so may yet influence
|
||||
// how they are resolved.
|
||||
//
|
||||
// Consider this silly example:
|
||||
//
|
||||
// fn borrow(x: &int) -> &int {x}
|
||||
// fn foo(x: @int) -> int { /* block: B */
|
||||
// let b = borrow(x); /* region: <R0> */
|
||||
// *b
|
||||
// }
|
||||
//
|
||||
// Here, the region of `b` will be `<R0>`. `<R0>` is constrainted
|
||||
// to be some subregion of the block B and some superregion of
|
||||
// the call. If we forced it now, we'd choose the smaller region
|
||||
// (the call). But that would make the *b illegal. Since we don't
|
||||
// resolve, the type of b will be `&<R0>.int` and then `*b` will require
|
||||
// that `<R0>` be bigger than the let and the `*b` expression, so we
|
||||
// will effectively resolve `<R0>` to be the block B.
|
||||
let ty0 = fcx.node_ty(id);
|
||||
let ty = alt resolve_type(fcx.infcx, ty0,
|
||||
(resolve_all | force_all) -
|
||||
(resolve_rvar | force_rvar)) {
|
||||
let ty = alt rcx.resolve_node_type(id) {
|
||||
result::err(_) { ret true; }
|
||||
result::ok(ty) { ty }
|
||||
};
|
||||
@@ -153,21 +200,29 @@ 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, ty0=%s)",
|
||||
#debug["visit_node(ty=%s, id=%d, encl_region=%s)",
|
||||
ppaux::ty_to_str(tcx, ty),
|
||||
id,
|
||||
ppaux::region_to_str(tcx, encl_region),
|
||||
ppaux::ty_to_str(tcx, ty0)];
|
||||
ppaux::region_to_str(tcx, encl_region)];
|
||||
|
||||
// Otherwise, look at the type and see if it is a region pointer.
|
||||
ret constrain_regions_in_type(rcx, encl_region, span, ty);
|
||||
}
|
||||
|
||||
fn constrain_regions_in_type(
|
||||
rcx: @rcx,
|
||||
encl_region: ty::region,
|
||||
span: span,
|
||||
ty: ty::t) -> bool {
|
||||
|
||||
let e = rcx.errors_reported;
|
||||
ty::walk_regions_and_ty(
|
||||
tcx, ty,
|
||||
rcx.fcx.ccx.tcx, ty,
|
||||
|r| constrain_region(rcx, encl_region, span, r),
|
||||
|t| ty::type_has_regions(t));
|
||||
ret (e == rcx.errors_reported);
|
||||
|
||||
fn constrain_region(rcx: rcx,
|
||||
fn constrain_region(rcx: @rcx,
|
||||
encl_region: ty::region,
|
||||
span: span,
|
||||
region: ty::region) {
|
||||
|
||||
@@ -64,7 +64,7 @@ fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
|
||||
};
|
||||
let ty = fixup_ty(fcx, sp, ty);
|
||||
alt ty::get(ty).struct {
|
||||
ty::ty_param(n, did) {
|
||||
ty::ty_param({idx: n, def_id: did}) {
|
||||
let mut n_bound = 0u;
|
||||
for vec::each(*tcx.ty_param_bounds.get(did.node)) |bound| {
|
||||
alt bound {
|
||||
|
||||
@@ -69,7 +69,9 @@ fn collect_item_types(ccx: @crate_ctxt, crate: @ast::crate) {
|
||||
}
|
||||
|
||||
impl methods for @crate_ctxt {
|
||||
fn to_ty<RS: region_scope copy>(rs: RS, ast_ty: @ast::ty) -> ty::t {
|
||||
fn to_ty<RS: region_scope copy owned>(
|
||||
rs: RS, ast_ty: @ast::ty) -> ty::t {
|
||||
|
||||
ast_ty_to_ty(self, rs, ast_ty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ export resolve_type, resolve_region;
|
||||
export resolve_borrowings;
|
||||
export methods; // for infer_ctxt
|
||||
export unify_methods; // for infer_ctxt
|
||||
export fixup_err, fixup_err_to_str;
|
||||
export fres, fixup_err, fixup_err_to_str;
|
||||
export assignment;
|
||||
export root, to_str;
|
||||
export int_ty_set_all;
|
||||
@@ -1789,7 +1789,7 @@ fn super_tys<C:combine>(
|
||||
}
|
||||
}
|
||||
|
||||
(ty::ty_param(a_n, _), ty::ty_param(b_n, _)) if a_n == b_n {
|
||||
(ty::ty_param(a_p), ty::ty_param(b_p)) if a_p.idx == b_p.idx {
|
||||
ok(a)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ impl of region_scope for type_rscope {
|
||||
}
|
||||
|
||||
enum anon_rscope = {anon: ty::region, base: region_scope};
|
||||
fn in_anon_rscope<RS: region_scope copy>(self: RS, r: ty::region)
|
||||
fn in_anon_rscope<RS: region_scope copy owned>(self: RS, r: ty::region)
|
||||
-> @anon_rscope {
|
||||
@anon_rscope({anon: r, base: self as region_scope})
|
||||
}
|
||||
@@ -52,7 +52,8 @@ impl of region_scope for @anon_rscope {
|
||||
}
|
||||
|
||||
enum binding_rscope = {base: region_scope};
|
||||
fn in_binding_rscope<RS: region_scope copy>(self: RS) -> @binding_rscope {
|
||||
fn in_binding_rscope<RS: region_scope copy owned>(self: RS)
|
||||
-> @binding_rscope {
|
||||
let base = self as region_scope;
|
||||
@binding_rscope({base: base})
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
|
||||
}
|
||||
ty_var(v) { v.to_str() }
|
||||
ty_var_integral(v) { v.to_str() }
|
||||
ty_param(id, _) {
|
||||
ty_param({idx: id, _}) {
|
||||
~"'" + str::from_bytes(~[('a' as u8) + (id as u8)])
|
||||
}
|
||||
ty_self { ~"self" }
|
||||
|
||||
Reference in New Issue
Block a user