first stab at type checking for borrow: not integrated into trans

This commit is contained in:
Niko Matsakis 2012-04-12 21:59:33 -07:00
parent c2fe288900
commit 27f1c2b742
7 changed files with 392 additions and 349 deletions

View File

@ -15,6 +15,7 @@ export infer_ctxt;
export new_infer_ctxt;
export mk_subty;
export mk_eqty;
export mk_assignty;
export resolve_type_structure;
export fixup_vars;
export resolve_var;
@ -76,6 +77,14 @@ fn mk_eqty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
indent {|| cx.commit {|| cx.eq_tys(a, b) } }.to_ures()
}
fn mk_assignty(cx: infer_ctxt, encl_node_id: ast::node_id,
a: ty::t, b: ty::t) -> ures {
#debug["mk_assignty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
indent {|| cx.commit {||
cx.assign_tys(encl_node_id, a, b)
} }.to_ures()
}
fn compare_tys(tcx: ty::ctxt, a: ty::t, b: ty::t) -> ures {
let infcx = new_infer_ctxt(tcx);
mk_eqty(infcx, a, b)
@ -677,12 +686,67 @@ impl resolve_methods for infer_ctxt {
// Type assignment
//
// True if rvalues of type `a` can be assigned to lvalues of type `b`.
// This may cause borrowing to the region scope for `encl_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 (@MT_a, &rb.MT_b)
// (resp. ~MT_a). 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 @MT_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
// &[const T2] where T1 <: T2. Basically we would require that @[mut
// T1] <: @[const T2]. Next we require that the region for the
// enclosing scope be a superregion of the region r. These two checks
// together guarantee that the type A would be a subtype of the type B
// if the @ were converted to a region r.
//
// You might wonder why we don't just make the type &e.MT_a where e is
// the enclosing region and check that &e.MT_a <: B. The reason is
// that the type @MT_a is (generally) just a *lower-bound*, so this
// would be imposing @MT_a also as the upper-bound on type A. But
// this upper-bound might be stricter than what is truly needed.
impl assignment for infer_ctxt {
fn assign_tys(a: ty::t, b: ty::t,
encl_blk_id: ast::node_id) -> ures {
fn assign_tys(encl_node_id: ast::node_id,
a: ty::t, b: ty::t) -> ures {
#debug["assign_tys(%s, %s)", a.to_str(self), b.to_str(self)];
fn select(fst: option<ty::t>, snd: option<ty::t>) -> option<ty::t> {
alt fst {
some(t) { some(t) }
none {
alt snd {
some(t) { some(t) }
none { none }
}
}
}
}
#debug["assign_tys(encl_node_id=%?, %s -> %s)",
encl_node_id, a.to_str(self), b.to_str(self)];
let _r = indenter();
alt (ty::get(a).struct, ty::get(b).struct) {
@ -691,57 +755,75 @@ impl assignment for infer_ctxt {
}
(ty::ty_var(a_id), ty::ty_var(b_id)) {
let {root:_, bounds:a_bounds} = self.get(self.vb, a_id);
let {root:_, bounds:b_bounds} = self.get(self.vb, b_id);
self.assign_vars_or_sub(a, b, a_bounds, b_bounds, encl_blk_id)
let {root:_, bounds: a_bounds} = self.get(self.vb, a_id);
let {root:_, bounds: b_bounds} = self.get(self.vb, b_id);
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(encl_node_id, a, b, a_bnd, b_bnd)
}
(ty::ty_var(a_id), _) {
let {root:_, bounds:a_bounds} = self.get(self.vb, a_id);
let b_bounds = {lb: some(b), ub: none};
self.assign_vars_or_sub(a, b, a_bounds, b_bounds, encl_blk_id)
let a_bnd = select(a_bounds.ub, a_bounds.lb);
self.assign_tys_or_sub(encl_node_id, a, b, a_bnd, some(b))
}
(_, ty::ty_var(b_id)) {
let a_bounds = {lb: none, ub: some(a)};
let {root:_, bounds: b_bounds} = self.get(self.vb, b_id);
self.assign_vars_or_sub(a, b, a_bounds, b_bounds, encl_blk_id)
let b_bnd = select(b_bounds.lb, b_bounds.ub);
self.assign_tys_or_sub(encl_node_id, a, b, some(a), b_bnd)
}
(ty::ty_box(a_mt), ty::ty_rptr(_, _)) |
(ty::ty_uniq(a_mt), ty::ty_rptr(_, _)) {
let a_r = ty::re_scope(encl_blk_id);
let a_ty = ty::mk_rptr(self.tcx, a_r, a_mt);
self.sub_tys(a_ty, b).to_ures()
}
_ {
self.sub_tys(a, b).to_ures()
(_, _) {
self.assign_tys_or_sub(encl_node_id, a, b, some(a), some(b))
}
}
}
fn assign_tys_or_sub(a: ty::t, b: ty::t,
a_b: ty::t, b_b: ty::t,
encl_blk_id: ast::node_id) -> ures {
self.try {||
self.assign_tys(a_b, b_b, encl_blk_id)
}.chain_err {|_e|
fn assign_tys_or_sub(
encl_node_id: ast::node_id,
a: ty::t, b: ty::t,
a_bnd: option<ty::t>, b_bnd: option<ty::t>) -> ures {
#debug["assign_tys_or_sub(encl_node_id=%?, %s -> %s, %s -> %s)",
encl_node_id, a.to_str(self), b.to_str(self),
a_bnd.to_str(self), b_bnd.to_str(self)];
let _r = indenter();
alt (a_bnd, b_bnd) {
(some(a_bnd), some(b_bnd)) {
alt (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, mt_b);
self.crosspolinate(encl_node_id, a, nr_b, r_b)
}
(ty::ty_uniq(mt_a), ty::ty_rptr(r_b, mt_b)) {
let nr_b = ty::mk_uniq(self.tcx, mt_b);
self.crosspolinate(encl_node_id, a, nr_b, r_b)
}
_ {
self.sub_tys(a, b)
}
}
}
_ {
self.sub_tys(a, b)
}
}
}
fn assign_vars_or_sub(a: ty::t, b: ty::t,
a_bounds: bounds<ty::t>, b_bounds: bounds<ty::t>,
encl_blk_id: ast::node_id) -> ures {
fn crosspolinate(encl_node_id: ast::node_id,
a: ty::t, nr_b: ty::t, r_b: ty::region) -> ures {
alt (a_bounds.ub, b_bounds.lb) {
(some(a_ub), some(b_lb)) {
self.assign_tys_or_sub(a, b, a_ub, b_lb, encl_blk_id)
}
_ {
self.sub_tys(a, b).to_ures()
}
#debug["crosspolinate(encl_node_id=%?, a=%s, nr_b=%s, r_b=%s)",
encl_node_id, a.to_str(self), nr_b.to_str(self),
r_b.to_str(self)];
indent {||
self.sub_tys(a, nr_b).then {||
let r_a = ty::re_scope(encl_node_id);
sub(self).contraregions(r_a, r_b).to_ures()
}
}
}
}

View File

@ -149,6 +149,8 @@ enum parent {
pa_block(ast::node_id),
pa_nested_fn(ast::node_id),
pa_item(ast::node_id),
pa_call(ast::node_id),
pa_alt(ast::node_id),
pa_crate
}
@ -166,12 +168,6 @@ type region_map = {
local_blocks: hashmap<ast::node_id,ast::node_id>,
/* Mapping from an AST type node to the region that `&` resolves to. */
ast_type_to_inferred_region: hashmap<ast::node_id,ty::region>,
/*
* Mapping from an address-of operator or alt expression to its containing
* region (usually ty::region_scope(block). This is used as the region if
* the operand is an rvalue.
*/
rvalue_to_region: hashmap<ast::node_id,ty::region>
};
type region_scope = @{
@ -274,21 +270,7 @@ type ctxt = {
scope: region_scope,
/*
* A list of local IDs that will be parented to the next block we
* traverse. This is used when resolving `alt` statements. Since we see
* the pattern before the associated block, upon seeing a pattern we must
* parent all the bindings in that pattern to the next block we see.
*/
mut queued_locals: [ast::node_id],
parent: parent,
/* True if we're within the pattern part of an alt, false otherwise. */
in_alt: bool,
/* The next parameter ID. */
mut next_param_id: uint
parent: parent
};
// Returns true if `subscope` is equal to or is lexically nested inside
@ -361,7 +343,9 @@ fn get_inferred_region(cx: ctxt, sp: syntax::codemap::span) -> ty::region {
ret alt cx.parent {
pa_fn_item(_) | pa_nested_fn(_) { ty::re_bound(ty::br_anon) }
pa_block(block_id) { ty::re_scope(block_id) }
pa_block(node_id) | pa_call(node_id) | pa_alt(node_id) {
ty::re_scope(node_id)
}
pa_item(_) { ty::re_bound(ty::br_anon) }
pa_crate { cx.sess.span_bug(sp, "inferred region at crate level?!"); }
}
@ -411,15 +395,39 @@ fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
visit::visit_ty(ty, cx, visitor);
}
fn record_parent(cx: ctxt, child_id: ast::node_id) {
fn opt_parent_id(cx: ctxt) -> option<ast::node_id> {
alt cx.parent {
pa_fn_item(parent_id) |
pa_item(parent_id) |
pa_block(parent_id) |
pa_nested_fn(parent_id) {
cx.region_map.parents.insert(child_id, parent_id);
}
pa_crate { /* no-op */ }
pa_fn_item(parent_id) |
pa_item(parent_id) |
pa_block(parent_id) |
pa_alt(parent_id) |
pa_call(parent_id) |
pa_nested_fn(parent_id) {
some(parent_id)
}
pa_crate {
none
}
}
}
fn parent_id(cx: ctxt, span: span) -> ast::node_id {
alt opt_parent_id(cx) {
none {
cx.sess.span_bug(span, "crate should not be parent here");
}
some(parent_id) {
parent_id
}
}
}
fn record_parent(cx: ctxt, child_id: ast::node_id) {
alt opt_parent_id(cx) {
none { /* no-op */ }
some(parent_id) {
cx.region_map.parents.insert(child_id, parent_id);
}
}
}
@ -427,98 +435,68 @@ fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
// Record the parent of this block.
record_parent(cx, blk.node.id);
// Resolve queued locals to this block.
for cx.queued_locals.each {|local_id|
cx.region_map.local_blocks.insert(local_id, blk.node.id);
}
// Descend.
let new_cx: ctxt = {parent: pa_block(blk.node.id),
scope: cx.scope.body_subscope(blk.node.id),
mut queued_locals: [],
in_alt: false with cx};
scope: cx.scope.body_subscope(blk.node.id)
with cx};
visit::visit_block(blk, new_cx, visitor);
}
fn resolve_arm(arm: ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
let new_cx: ctxt = {mut queued_locals: [], in_alt: true with cx};
visit::visit_arm(arm, new_cx, visitor);
visit::visit_arm(arm, cx, visitor);
}
fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
alt pat.node {
ast::pat_ident(path, _) {
let defn_opt = cx.def_map.find(pat.id);
alt defn_opt {
some(ast::def_variant(_,_)) {
/* Nothing to do; this names a variant. */
}
_ {
/*
* This names a local. Enqueue it or bind it to the
* containing block, depending on whether we're in an alt
* or not.
*/
if cx.in_alt {
vec::push(cx.queued_locals, pat.id);
} else {
alt cx.parent {
pa_block(block_id) {
let local_blocks = cx.region_map.local_blocks;
local_blocks.insert(pat.id, block_id);
}
_ {
cx.sess.span_bug(pat.span,
"unexpected parent");
}
}
}
}
}
ast::pat_ident(path, _) {
let defn_opt = cx.def_map.find(pat.id);
alt defn_opt {
some(ast::def_variant(_,_)) {
/* Nothing to do; this names a variant. */
}
_ {
/* This names a local. Bind it to the containing scope. */
let local_blocks = cx.region_map.local_blocks;
local_blocks.insert(pat.id, parent_id(cx, pat.span));
}
}
_ { /* no-op */ }
}
_ { /* no-op */ }
}
visit::visit_pat(pat, cx, visitor);
}
fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
record_parent(cx, expr.id);
alt expr.node {
ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
record_parent(cx, expr.id);
let new_cx = {parent: pa_nested_fn(expr.id),
scope: cx.scope.binding_subscope(expr.id),
in_alt: false with cx};
visit::visit_expr(expr, new_cx, visitor);
}
ast::expr_vstore(e, ast::vstore_slice(r)) {
let region = resolve_region_binding(cx, e.span, r);
cx.region_map.rvalue_to_region.insert(e.id, region);
}
ast::expr_addr_of(_, subexpr) | ast::expr_alt(subexpr, _, _) {
// Record the block that this expression appears in, in case the
// operand is an rvalue.
alt cx.parent {
pa_block(blk_id) {
let region = ty::re_scope(blk_id);
cx.region_map.rvalue_to_region.insert(subexpr.id, region);
}
_ { cx.sess.span_bug(expr.span, "expr outside of block?!"); }
}
visit::visit_expr(expr, cx, visitor);
}
_ { visit::visit_expr(expr, cx, visitor); }
ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
let new_cx = {parent: pa_nested_fn(expr.id),
scope: cx.scope.binding_subscope(expr.id)
with cx};
visit::visit_expr(expr, new_cx, visitor);
}
ast::expr_call(_, _, _) {
let new_cx = {parent: pa_call(expr.id),
scope: cx.scope.binding_subscope(expr.id)
with cx};
visit::visit_expr(expr, new_cx, visitor);
}
ast::expr_alt(subexpr, _, _) {
let new_cx = {parent: pa_alt(expr.id),
scope: cx.scope.binding_subscope(expr.id)
with cx};
visit::visit_expr(expr, new_cx, visitor);
}
_ {
visit::visit_expr(expr, cx, visitor);
}
}
}
fn resolve_local(local: @ast::local, cx: ctxt, visitor: visit::vt<ctxt>) {
alt cx.parent {
pa_block(blk_id) {
let region = ty::re_scope(blk_id);
cx.region_map.rvalue_to_region.insert(local.node.id, region);
}
_ { cx.sess.span_bug(local.span, "local outside of block?!"); }
}
cx.region_map.local_blocks.insert(
local.node.id, parent_id(cx, local.span));
visit::visit_local(local, cx, visitor);
}
@ -542,9 +520,7 @@ fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
};
let new_cx: ctxt = {parent: parent,
scope: scope,
in_alt: false,
mut next_param_id: 0u
scope: scope
with cx};
visit::visit_item(item, new_cx, visitor);
@ -558,13 +534,9 @@ fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
ast_type_to_region: map::int_hash(),
local_blocks: map::int_hash(),
ast_type_to_inferred_region:
map::int_hash(),
rvalue_to_region: map::int_hash()},
map::int_hash()},
scope: root_scope(0),
mut queued_locals: [],
parent: pa_crate,
in_alt: false,
mut next_param_id: 0u};
parent: pa_crate};
let visitor = visit::mk_vt(@{
visit_block: resolve_block,
visit_item: resolve_item,

View File

@ -7,76 +7,45 @@ import driver::session::session;
import middle::ty;
import std::map::hashmap;
import syntax::{ast, visit};
import util::ppaux;
// An "extended region", which includes the ordinarily-unnamed reference-
// counted heap and exchange heap regions. This is used to detect borrowing.
enum region_ext {
re_rc,
re_exheap,
re_region(ty::region)
}
fn check_expr(expr: @ast::expr,
&&tcx: ty::ctxt,
visitor: visit::vt<ty::ctxt>) {
visit::visit_expr(expr, tcx, visitor);
type ctxt = {
tcx: ty::ctxt,
enclosing_block: option<ast::node_id>
};
fn check_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
let t = ty::expr_ty(cx.tcx, expr);
if ty::type_has_rptrs(t) {
ty::walk_ty(t) { |t|
alt ty::get(t).struct {
ty::ty_rptr(region, _) {
alt region {
ty::re_bound(_) | ty::re_free(_, _) | ty::re_static {
/* ok */
}
ty::re_scope(rbi) {
let referent_block_id = rbi;
let enclosing_block_id = alt cx.enclosing_block {
none {
cx.tcx.sess.span_bug(expr.span,
"block region " +
"type outside a " +
"block?!");
}
some(eb) { eb }
};
if !region::scope_contains(cx.tcx.region_map,
referent_block_id,
enclosing_block_id) {
cx.tcx.sess.span_err(expr.span, "reference " +
"escapes its block");
}
}
ty::re_default | ty::re_var(_) {
cx.tcx.sess.span_bug(expr.span,
"unresolved region");
}
let t = ty::expr_ty(tcx, expr);
if !ty::type_has_rptrs(t) { ret; }
ty::walk_ty(t) { |t|
alt ty::get(t).struct {
ty::ty_rptr(region, _) {
alt region {
ty::re_bound(_) | ty::re_free(_, _) | ty::re_static {
/* ok */
}
ty::re_scope(id) {
if !region::scope_contains(tcx.region_map, id, expr.id) {
tcx.sess.span_err(
expr.span,
#fmt["reference is not valid outside of %s",
ppaux::re_scope_id_to_str(tcx, id)]);
}
}
_ { /* no-op */ }
ty::re_default | ty::re_var(_) {
tcx.sess.span_bug(expr.span, "unresolved region");
}
}
}
_ { /* no-op */ }
}
}
visit::visit_expr(expr, cx, visitor);
}
fn check_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
let new_cx: ctxt = { enclosing_block: some(blk.node.id) with cx };
visit::visit_block(blk, new_cx, visitor);
}
fn check_crate(ty_cx: ty::ctxt, crate: @ast::crate) {
let cx: ctxt = {tcx: ty_cx, enclosing_block: none};
fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
let visitor = visit::mk_vt(@{
visit_expr: check_expr,
visit_block: check_block
visit_expr: check_expr
with *visit::default_visitor()
});
visit::visit_crate(*crate, cx, visitor);
visit::visit_crate(*crate, tcx, visitor);
}

View File

@ -873,6 +873,15 @@ impl methods for @fn_ctxt {
fn next_ty_vars(n: uint) -> [ty::t] {
vec::from_fn(n) {|_i| self.next_ty_var() }
}
fn report_mismatched_types(sp: span, e: ty::t, a: ty::t,
err: ty::type_err) {
self.ccx.tcx.sess.span_err(
sp,
#fmt["mismatched types: expected `%s` but found `%s` (%s)",
self.ty_to_str(e),
self.ty_to_str(a),
ty::type_err_to_str(self.ccx.tcx, err)]);
}
}
fn mk_ty_params(tcx: ty::ctxt, atps: [ast::ty_param])
@ -996,7 +1005,7 @@ fn fixup_self_param(fcx: @fn_ctxt, mty: ty::t, m_substs: [ty::t],
// Simply ensure that the type parameters for the self
// type match the context.
vec::iter2(substs, m_substs) {|s, ms|
demand::simple(fcx, sp, s, ms);
demand::suptype(fcx, sp, s, ms);
}
selfty
}
@ -1406,21 +1415,26 @@ fn require_same_types(
mod demand {
// Requires that the two types unify, and prints an error message if they
// don't.
fn simple(fcx: @fn_ctxt, sp: span,
fn suptype(fcx: @fn_ctxt, sp: span,
expected: ty::t, actual: ty::t) {
alt infer::mk_subty(fcx.infcx, actual, expected) {
result::ok(()) { /* ok */ }
result::err(err) {
fcx.ccx.tcx.sess.span_err(sp,
"mismatched types: expected `" +
fcx.ty_to_str(expected) +
"` but found `" +
fcx.ty_to_str(actual) +
"` (" +
ty::type_err_to_str(
fcx.ccx.tcx, err) +
")");
fcx.report_mismatched_types(sp, expected, actual, err);
}
}
}
// Checks that the type `actual` can be assigned to `expected`, borrowing
// to `encl_node_id` if necessary.
fn assign(fcx: @fn_ctxt, sp: span, encl_node_id: ast::node_id,
expected: ty::t, actual: ty::t) {
alt infer::mk_assignty(fcx.infcx, encl_node_id, actual, expected) {
result::ok(()) { /* ok */ }
result::err(err) {
fcx.report_mismatched_types(sp, expected, actual, err);
}
}
}
@ -1912,7 +1926,7 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
// check that the type of the value being matched is a subtype
// of the type of the pattern:
let pat_ty = fcx.node_ty(pat.id);
demand::simple(fcx, pat.span, pat_ty, expected);
demand::suptype(fcx, pat.span, pat_ty, expected);
// Get the number of arguments in this enum variant.
let arg_types = variant_arg_types(pcx.fcx.ccx, pat.span,
@ -1991,12 +2005,12 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
if !pat_util::pat_is_variant(tcx.def_map, pat) {
let vid = lookup_local(pcx.fcx, pat.span, pat.id);
let mut typ = ty::mk_var(tcx, vid);
demand::simple(pcx.fcx, pat.span, expected, typ);
demand::suptype(pcx.fcx, pat.span, expected, typ);
let canon_id = pcx.map.get(path_to_ident(name));
if canon_id != pat.id {
let tv_id = lookup_local(pcx.fcx, pat.span, canon_id);
let ct = ty::mk_var(tcx, tv_id);
demand::simple(pcx.fcx, pat.span, ct, typ);
demand::suptype(pcx.fcx, pat.span, ct, typ);
}
fcx.write_ty(pat.id, typ);
alt sub {
@ -2165,17 +2179,17 @@ fn require_pure_call(ccx: @crate_ctxt, caller_purity: ast::purity,
}
}
type unifier = fn@(@fn_ctxt, span, ty::t, ty::t);
type unifier = fn(@fn_ctxt, span, ty::t, ty::t);
fn check_expr(fcx: @fn_ctxt, expr: @ast::expr) -> bool {
fn dummy_unify(_fcx: @fn_ctxt, _sp: span,
_expected: ty::t, _actual: ty::t) {
}
ret check_expr_with_unifier(fcx, expr, dummy_unify,
ty::mk_nil(fcx.ccx.tcx));
ret check_expr_with_unifier(fcx, expr, ty::mk_nil(fcx.ccx.tcx)) {
|_fcx, _span, _t1, _t2|
/* unify is a no-op */
};
}
fn check_expr_with(fcx: @fn_ctxt, expr: @ast::expr, expected: ty::t) -> bool {
ret check_expr_with_unifier(fcx, expr, demand::simple, expected);
ret check_expr_with_unifier(fcx, expr, expected, demand::suptype);
}
// determine the `self` type, using fresh variables for all variables declared
@ -2486,55 +2500,46 @@ fn lookup_field_ty(tcx: ty::ctxt, class_id: ast::def_id,
{|f| ty::lookup_field_type(tcx, class_id, f.id, substs) })
}
/*
* Returns the region that the value named by the given expression lives in.
* The expression must have been typechecked. If the expression is not an
* lvalue, returns the block region.
*
* Note that borrowing is not detected here, because we would have to
* immediately structurally resolve too many types otherwise. Thus the
* reference-counted heap and exchange heap regions will be reported as block
* regions instead. This is cleaned up in the region checking pass.
/* Returns the region that &expr should be placed into. If expr is an
* lvalue, this will be the region of the lvalue. Otherwise, if region is
* an rvalue, the semantics are that the result is stored into a temporary
* stack position and so the resulting region will be the enclosing block.
*/
fn region_of(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region {
fn borrow(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region {
let parent_id = fcx.ccx.tcx.region_map.parents.get(expr.id);
ret ty::re_scope(parent_id);
}
fn deref(fcx: @fn_ctxt, base: @ast::expr) -> ty::region {
let base_ty = fcx.expr_ty(base);
let base_ty = structurally_resolved_type(fcx, base.span, base_ty);
alt ty::get(base_ty).struct {
ty::ty_rptr(region, _) { region }
ty::ty_box(_) | ty::ty_uniq(_) { borrow(fcx, base) }
_ { region_of(fcx, base) }
}
}
alt expr.node {
ast::expr_path(path) {
let defn = lookup_def(fcx, path.span, expr.id);
alt defn {
ast::def_local(local_id, _) |
ast::def_upvar(local_id, _, _) {
let local_blocks = fcx.ccx.tcx.region_map.local_blocks;
let local_block_id = local_blocks.get(local_id);
ret ty::re_scope(local_block_id);
}
_ {
fcx.ccx.tcx.sess.span_unimpl(expr.span,
"immortal region");
}
}
}
ast::expr_field(base, _, _) {
// FIXME: Insert borrowing!
ret region_of(fcx, base);
}
ast::expr_index(base, _) {
fcx.ccx.tcx.sess.span_unimpl(expr.span,
"regions of index operations");
}
ast::expr_unary(ast::deref, base) {
let expr_ty = fcx.expr_ty(base);
let expr_ty = structurally_resolved_type(fcx, expr.span, expr_ty);
alt ty::get(expr_ty).struct {
ty::ty_rptr(region, _) { region }
ty::ty_box(_) | ty::ty_uniq(_) {
fcx.ccx.tcx.sess.span_unimpl(expr.span, "borrowing");
}
_ { ret region_of(fcx, base); }
}
}
_ {
ret fcx.ccx.tcx.region_map.rvalue_to_region.get(expr.id);
ast::expr_path(path) {
let defn = lookup_def(fcx, path.span, expr.id);
alt defn {
ast::def_local(local_id, _) |
ast::def_upvar(local_id, _, _) {
let local_blocks = fcx.ccx.tcx.region_map.local_blocks;
let local_block_id = local_blocks.get(local_id);
ty::re_scope(local_block_id)
}
_ {
ty::re_static
}
}
}
ast::expr_field(base, _, _) { deref(fcx, base) }
ast::expr_index(base, _) { deref(fcx, base) }
ast::expr_unary(ast::deref, base) { deref(fcx, base) }
_ { borrow(fcx, expr) }
}
}
@ -2543,7 +2548,7 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt,
proto: ast::proto,
decl: ast::fn_decl,
body: ast::blk,
unify: unifier,
unifier: unifier,
expected: ty::t,
is_loop_body: bool) {
let tcx = fcx.ccx.tcx;
@ -2559,7 +2564,7 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt,
// typecheck the body so that we have more information about the
// argument types in the body. This is needed to make binops and
// record projection work on type inferred arguments.
unify(fcx, expr.span, expected, fty);
unifier(fcx, expr.span, expected, fty);
let ret_ty = ty::ty_fn_ret(fty);
let arg_tys = vec::map(ty::ty_fn_args(fty)) {|a| a.ty };
@ -2569,8 +2574,8 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt,
fcx.self_ty);
}
fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
expected: ty::t) -> bool {
fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr,
expected: ty::t, unifier: unifier) -> bool {
#debug(">> typechecking expr %d (%s)",
expr.id, syntax::print::pprust::expr_to_str(expr));
@ -2578,7 +2583,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
// A generic function to factor out common logic from call and bind
// expressions.
fn check_call_or_bind(
fcx: @fn_ctxt, sp: span, fty: ty::t,
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, fty: ty::t,
args: [option<@ast::expr>]) -> {fty: ty::t, bot: bool} {
let fty = universally_quantify_before_call(fcx, region_env(), fty);
@ -2640,9 +2645,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
_ { false }
};
if is_block == check_blocks {
let t = arg_tys[i].ty;
bot |= check_expr_with_unifier(fcx, a, demand::simple,
t);
let arg_ty = arg_tys[i].ty;
bot |= check_expr_with_unifier(fcx, a, arg_ty) {
|fcx, span, expected, actual|
demand::assign(fcx, span, call_expr_id,
expected, actual);
};
}
}
none { }
@ -2666,41 +2674,35 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
ret bot;
}
// A generic function for checking call expressions
fn check_call(fcx: @fn_ctxt, sp: span, f: @ast::expr, args: [@ast::expr])
-> {fty: ty::t, bot: bool} {
let mut args_opt_0: [option<@ast::expr>] = [];
for args.each {|arg|
args_opt_0 += [some::<@ast::expr>(arg)];
}
// A generic function for doing all of the checking for call expressions
fn check_call(fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id,
f: @ast::expr, args: [@ast::expr]) -> bool {
let bot = check_expr(fcx, f);
let mut bot = check_expr(fcx, f);
let fn_ty = fcx.expr_ty(f);
// Call the generic checker.
let fn_ty = fcx.expr_ty(f);
let {fty, bot: bot1} = check_call_or_bind(fcx, sp, fn_ty, args_opt_0);
ret {fty: fty, bot: bot | bot1};
}
let fty = {
let args_opt = args.map { |arg| some(arg) };
let r = check_call_or_bind(fcx, sp, call_expr_id,
fn_ty, args_opt);
bot |= r.bot;
r.fty
};
// A generic function for doing all of the checking for call expressions
fn check_call_full(fcx: @fn_ctxt, sp: span, id: ast::node_id,
f: @ast::expr, args: [@ast::expr]) -> bool {
let {fty, bot} = check_call(fcx, sp, f, args);
/* need to restrict oper to being an explicit expr_path if we're
inside a pure function */
// Need to restrict oper to being an explicit expr_path if we're
// inside a pure function
require_pure_call(fcx.ccx, fcx.purity, f, sp);
// Pull the return type out of the type of the function.
let {rt, bot} = alt structure_of(fcx, sp, fty) {
alt structure_of(fcx, sp, fty) {
ty::ty_fn(f) {
{rt: f.output,
bot: bot | (f.ret_style == ast::noreturn)}
bot |= (f.ret_style == ast::noreturn);
fcx.write_ty(call_expr_id, f.output);
ret bot;
}
_ { fcx.ccx.tcx.sess.span_fatal(sp, "calling non-function"); }
};
fcx.write_ty(id, rt);
ret bot;
}
}
// A generic function for checking for or for-each loops
@ -2708,7 +2710,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
element_ty: ty::t, body: ast::blk,
node_id: ast::node_id) -> bool {
let locid = lookup_local(fcx, local.span, local.node.id);
demand::simple(fcx, local.span,
demand::suptype(fcx, local.span,
ty::mk_var(fcx.ccx.tcx, locid),
element_ty);
let bot = check_decl_local(fcx, local);
@ -2728,7 +2730,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let if_t = fcx.next_ty_var();
let thn_bot = check_block(fcx, thn);
let thn_t = fcx.node_ty(thn.node.id);
demand::simple(fcx, thn.span, if_t, thn_t);
demand::suptype(fcx, thn.span, if_t, thn_t);
let els_bot = check_expr_with(fcx, els, if_t);
(if_t, thn_bot & els_bot)
}
@ -2764,7 +2766,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
some(origin) {
let {fty: method_ty, bot: bot} = {
let method_ty = fcx.node_ty(callee_id);
check_call_or_bind(fcx, op_ex.span, method_ty, args)
check_call_or_bind(fcx, op_ex.span, op_ex.id,
method_ty, args)
};
fcx.ccx.method_map.insert(op_ex.id, origin);
some((ty::ty_fn_ret(method_ty), bot))
@ -2790,7 +2793,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let t_var = fcx.next_ty_var();
let const_vec_t = ty::mk_vec(tcx, {ty: t_var,
mutbl: ast::m_const});
demand::simple(fcx, lhs.span, const_vec_t, lhs_t);
demand::suptype(fcx, lhs.span, const_vec_t, lhs_t);
let rhs_bot = check_expr_with(fcx, rhs, const_vec_t);
let result_vec_t = ty::mk_vec(tcx, {ty: t_var,
mutbl: lhs_mt.mutbl});
@ -2810,7 +2813,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
(_, _) if ty::is_binopable(tcx, lhs_t, op) {
let tvar = fcx.next_ty_var();
demand::simple(fcx, expr.span, tvar, lhs_t);
demand::suptype(fcx, expr.span, tvar, lhs_t);
let rhs_bot = check_expr_with(fcx, rhs, tvar);
let rhs_t = alt op {
ast::eq | ast::lt | ast::le | ast::ne | ast::ge |
@ -2895,8 +2898,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
};
alt vst {
ast::vstore_slice(_) {
let region = fcx.ccx.tcx.region_map.rvalue_to_region.get(ev.id);
typ = replace_default_region(tcx, region, typ);
let r = ty::re_scope(fcx.ccx.tcx.region_map.parents.get(ev.id));
typ = replace_default_region(tcx, r, typ);
}
_ { }
}
@ -2935,7 +2938,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
bot |= check_binop(fcx, expr, op, lhs, rhs);
let lhs_t = fcx.expr_ty(lhs);
let result_t = fcx.expr_ty(expr);
demand::simple(fcx, expr.span, result_t, lhs_t);
demand::suptype(fcx, expr.span, result_t, lhs_t);
// Overwrite result of check_binop...this preserves existing behavior
// but seems quite dubious with regard to user-defined methods
@ -3068,7 +3071,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
fcx.write_nil(id);
}
ast::expr_copy(a) {
bot = check_expr_with_unifier(fcx, a, unify, expected);
bot = check_expr_with_unifier(fcx, a, expected, unifier);
fcx.write_ty(id, fcx.expr_ty(a));
}
ast::expr_move(lhs, rhs) {
@ -3107,8 +3110,6 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let pattern_ty = fcx.next_ty_var();
bot = check_expr_with(fcx, discrim, pattern_ty);
let parent_region = tcx.region_map.rvalue_to_region.get(discrim.id);
// Typecheck the patterns first, so that we get types for all the
// bindings.
//let pattern_ty = fcx.expr_ty(discrim);
@ -3116,9 +3117,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let pcx = {
fcx: fcx,
map: pat_util::pat_id_map(tcx.def_map, arm.pats[0]),
alt_region: parent_region,
alt_region: ty::re_scope(expr.id),
block_region: ty::re_scope(arm.body.node.id),
pat_region: parent_region
pat_region: ty::re_scope(expr.id)
};
for arm.pats.each {|p| check_pat(pcx, p, pattern_ty);}
@ -3133,7 +3134,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
if !check_block(fcx, arm.body) { arm_non_bot = true; }
let bty = fcx.node_ty(arm.body.node.id);
demand::simple(fcx, arm.body.span, result_ty, bty);
demand::suptype(fcx, arm.body.span, result_ty, bty);
}
bot |= !arm_non_bot;
if !arm_non_bot { result_ty = ty::mk_bot(tcx); }
@ -3141,7 +3142,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
ast::expr_fn(proto, decl, body, captures) {
check_expr_fn_with_unifier(fcx, expr, proto, decl, body,
unify, expected, false);
unifier, expected, false);
capture::check_capture_clause(tcx, expr.id, proto, *captures);
}
ast::expr_fn_block(decl, body) {
@ -3154,13 +3155,13 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
expr_to_str(expr),
ty_to_str(tcx, expected));
check_expr_fn_with_unifier(fcx, expr, proto, decl, body,
unify, expected, false);
unifier, expected, false);
}
ast::expr_loop_body(b) {
let rty = structurally_resolved_type(fcx, expr.span, expected);
let (inner_ty, proto) = alt check ty::get(rty).struct {
ty::ty_fn(fty) {
demand::simple(fcx, expr.span, fty.output, ty::mk_bool(tcx));
demand::suptype(fcx, expr.span, fty.output, ty::mk_bool(tcx));
(ty::mk_fn(tcx, {output: ty::mk_nil(tcx) with fty}),
fty.proto)
}
@ -3168,7 +3169,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
alt check b.node {
ast::expr_fn_block(decl, body) {
check_expr_fn_with_unifier(fcx, b, proto, decl, body,
unify, inner_ty, true);
unifier, inner_ty, true);
}
}
let block_ty = structurally_resolved_type(
@ -3196,7 +3197,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let {fty, bot: ccob_bot} = {
let fn_ty = fcx.expr_ty(f);
check_call_or_bind(fcx, expr.span, fn_ty, args)
check_call_or_bind(fcx, expr.span, expr.id, fn_ty, args)
};
bot |= ccob_bot;
@ -3248,7 +3249,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
fcx.write_ty(id, ft);
}
ast::expr_call(f, args, _) {
bot = check_call_full(fcx, expr.span, expr.id, f, args);
bot = check_call(fcx, expr.span, expr.id, f, args);
}
ast::expr_cast(e, t) {
bot = check_expr(fcx, e);
@ -3332,7 +3333,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let mut found = false;
for base_fields.each {|bf|
if str::eq(f.node.ident, bf.ident) {
demand::simple(fcx, f.span, bf.mt.ty, f.node.mt.ty);
demand::suptype(fcx, f.span, bf.mt.ty, f.node.mt.ty);
found = true;
}
}
@ -3488,7 +3489,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
constraints: []})
};
demand::simple(fcx, expr.span,
demand::suptype(fcx, expr.span,
expected_ty, fcx.node_ty(alloc_id));
}
@ -3516,7 +3517,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
ty_to_str(tcx, fcx.expr_ty(expr)),
ty_to_str(tcx, expected));
unify(fcx, expr.span, expected, fcx.expr_ty(expr));
unifier(fcx, expr.span, expected, fcx.expr_ty(expr));
#debug("<< bot=%b", bot);
ret bot;
@ -3559,7 +3560,9 @@ fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
_ {/* fall through */ }
}
let region = fcx.ccx.tcx.region_map.rvalue_to_region.get(local.node.id);
let region =
ty::re_scope(
fcx.ccx.tcx.region_map.local_blocks.get(local.node.id));
let pcx = {
fcx: fcx,
map: pat_util::pat_id_map(fcx.ccx.tcx.def_map, local.node.pat),
@ -3603,7 +3606,7 @@ fn check_block_no_value(fcx: @fn_ctxt, blk: ast::blk) -> bool {
if !bot {
let blkty = fcx.node_ty(blk.node.id);
let nilty = ty::mk_nil(fcx.ccx.tcx);
demand::simple(fcx, blk.span, nilty, blkty);
demand::suptype(fcx, blk.span, nilty, blkty);
}
ret bot;
}
@ -3667,7 +3670,7 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
check_expr(fcx, e);
let cty = fcx.expr_ty(e);
let declty = fcx.ccx.tcx.tcache.get(local_def(id)).ty;
demand::simple(fcx, e.span, declty, cty);
demand::suptype(fcx, e.span, declty, cty);
writeback::resolve_type_vars_in_expr(fcx, e);
}
@ -3709,7 +3712,7 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
check_expr(fcx, e);
let cty = fcx.expr_ty(e);
let declty = ty::mk_int(ccx.tcx);
demand::simple(fcx, e.span, declty, cty);
demand::suptype(fcx, e.span, declty, cty);
// FIXME: issue #1417
// Also, check_expr (from check_const pass) doesn't guarantee that
// the expression in an form that eval_const_expr can handle, so
@ -3945,7 +3948,7 @@ fn check_fn(ccx: @crate_ctxt,
alt body.node.expr {
some(tail_expr) {
let tail_expr_ty = fcx.expr_ty(tail_expr);
demand::simple(fcx, tail_expr.span, fcx.ret_ty, tail_expr_ty);
demand::suptype(fcx, tail_expr.span, fcx.ret_ty, tail_expr_ty);
}
none { }
}
@ -4241,7 +4244,7 @@ mod vtable {
alt check ty::get(iface_ty).struct {
ty::ty_iface(_, tps) {
vec::iter2(tps, iface_tys,
{|a, b| demand::simple(fcx, sp, a, b);});
{|a, b| demand::suptype(fcx, sp, a, b);});
}
}
}

View File

@ -18,18 +18,36 @@ fn bound_region_to_str(_cx: ctxt, br: bound_region) -> str {
}
}
fn region_to_str(cx: ctxt, region: region) -> str {
alt region {
re_scope(node_id) {
alt cx.items.get(node_id) {
ast_map::node_block(blk) {
#fmt("&<block at %s>.", codemap::span_to_str(blk.span,
cx.sess.codemap))
}
_ { cx.sess.bug("re_block refers to non-block") }
fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> str {
alt cx.items.get(node_id) {
ast_map::node_block(blk) {
#fmt("<block at %s>",
codemap::span_to_str(blk.span, cx.sess.codemap))
}
ast_map::node_expr(expr) {
alt expr.node {
ast::expr_call(_, _, _) {
#fmt("<call at %s>",
codemap::span_to_str(expr.span, cx.sess.codemap))
}
ast::expr_alt(_, _, _) {
#fmt("<alt at %s>",
codemap::span_to_str(expr.span, cx.sess.codemap))
}
_ { cx.sess.bug(
#fmt["re_scope refers to %s",
ast_map::node_id_to_str(cx.items, node_id)]) }
}
}
_ { cx.sess.bug(
#fmt["re_scope refers to %s",
ast_map::node_id_to_str(cx.items, node_id)]) }
}
}
fn region_to_str(cx: ctxt, region: region) -> str {
alt region {
re_scope(node_id) { #fmt["&%s.", re_scope_id_to_str(cx, node_id)] }
re_bound(br) { bound_region_to_str(cx, br) }
re_free(id, br) { #fmt["{%d} %s", id, bound_region_to_str(cx, br)] }

View File

@ -1,9 +1,8 @@
// xfail-test
fn foo(x: &uint) -> &uint { x }
fn main() {
let p = @3u;
let r = foo(p);
assert *p == *r;
}
//!^ ERROR reference is not valid
}

View File

@ -2,18 +2,18 @@
// This generates a ton of error msgs at the moment.
fn broken() -> int {
let mut x = 3;
let mut y = [&x]; //! ERROR reference escapes its block
let mut y = [&x]; //! ERROR reference is not valid
while x < 10 {
let z = x;
y += [&z];
x += 1;
}
vec::foldl(0, y) {|v, p| v + *p }
//!^ ERROR reference escapes its block
//!^^ ERROR reference escapes its block
//!^^^ ERROR reference escapes its block
//!^^^^ ERROR reference escapes its block
//!^^^^^ ERROR reference escapes its block
//!^ ERROR reference is not valid
//!^^ ERROR reference is not valid
//!^^^ ERROR reference is not valid
//!^^^^ ERROR reference is not valid
//!^^^^^ ERROR reference is not valid
}
fn main() { }