move regionck into typeck, in the process fixing a bug or two
This commit is contained in:
parent
fa5cc5bcd0
commit
ab735320b4
12 changed files with 229 additions and 138 deletions
|
|
@ -193,8 +193,6 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
|
|||
let (root_map, mutbl_map) = time(
|
||||
time_passes, "borrow checking",
|
||||
bind middle::borrowck::check_crate(ty_cx, method_map, crate));
|
||||
time(time_passes, "region checking",
|
||||
bind middle::regionck::check_crate(ty_cx, crate));
|
||||
let (copy_map, ref_map) =
|
||||
time(time_passes, "alias checking",
|
||||
bind middle::alias::check_crate(ty_cx, crate));
|
||||
|
|
|
|||
|
|
@ -364,8 +364,7 @@ fn resolve_local(local: @ast::local, cx: ctxt, visitor: visit::vt<ctxt>) {
|
|||
|
||||
fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
|
||||
// Items create a new outer block scope as far as we're concerned.
|
||||
let new_cx: ctxt = {closure_parent: some(item.id),
|
||||
parent: some(item.id) with cx};
|
||||
let new_cx: ctxt = {closure_parent: none, parent: none with cx};
|
||||
visit::visit_item(item, new_cx, visitor);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* The region checking pass. Ensures that region-annotated pointers never
|
||||
* outlive their referents.
|
||||
*/
|
||||
|
||||
import driver::session::session;
|
||||
import middle::ty;
|
||||
import std::map::hashmap;
|
||||
import syntax::{ast, visit};
|
||||
import util::ppaux;
|
||||
|
||||
fn check_expr(expr: @ast::expr,
|
||||
&&tcx: ty::ctxt,
|
||||
visitor: visit::vt<ty::ctxt>) {
|
||||
visit::visit_expr(expr, tcx, visitor);
|
||||
|
||||
let t = ty::expr_ty(tcx, expr);
|
||||
if !ty::type_has_regions(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 |
|
||||
ty::re_var(_) {
|
||||
/* 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 */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
|
||||
let visitor = visit::mk_vt(@{
|
||||
visit_expr: check_expr
|
||||
with *visit::default_visitor()
|
||||
});
|
||||
visit::visit_crate(*crate, tcx, visitor);
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ export ty_uint, mk_uint, mk_mach_uint;
|
|||
export ty_uniq, mk_uniq, mk_imm_uniq, type_is_unique_box;
|
||||
export ty_var, mk_var, type_is_var;
|
||||
export ty_self, mk_self, type_has_self;
|
||||
export region, bound_region;
|
||||
export region, bound_region, encl_region;
|
||||
export get, type_has_params, type_needs_infer, type_has_regions;
|
||||
export type_has_resources, type_id;
|
||||
export tbox_has_flag;
|
||||
|
|
@ -685,6 +685,15 @@ fn default_arg_mode_for_ty(ty: ty::t) -> ast::rmode {
|
|||
else { ast::by_ref }
|
||||
}
|
||||
|
||||
// Returns the narrowest lifetime enclosing the evaluation of the expression
|
||||
// with id `id`.
|
||||
fn encl_region(cx: ctxt, id: ast::node_id) -> ty::region {
|
||||
alt cx.region_map.parents.find(id) {
|
||||
some(encl_scope) {ty::re_scope(encl_scope)}
|
||||
none {ty::re_static}
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_ty(ty: t, f: fn(t)) {
|
||||
maybe_walk_ty(ty, {|t| f(t); true});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -630,6 +630,7 @@ impl methods for @fn_ctxt {
|
|||
fn next_region_var() -> ty::region {
|
||||
ret ty::re_var(self.next_region_var_id());
|
||||
}
|
||||
|
||||
fn report_mismatched_types(sp: span, e: ty::t, a: ty::t,
|
||||
err: ty::type_err) {
|
||||
self.ccx.tcx.sess.span_err(
|
||||
|
|
@ -648,6 +649,10 @@ impl methods for @fn_ctxt {
|
|||
infer::mk_eqty(self.infcx, sub, sup)
|
||||
}
|
||||
|
||||
fn mk_subr(sub: ty::region, sup: ty::region) -> result<(), ty::type_err> {
|
||||
infer::mk_subr(self.infcx, sub, sup)
|
||||
}
|
||||
|
||||
fn require_unsafe(sp: span, op: str) {
|
||||
alt self.purity {
|
||||
ast::unsafe_fn {/*ok*/}
|
||||
|
|
@ -796,47 +801,6 @@ fn require_same_types(
|
|||
}
|
||||
}
|
||||
|
||||
mod demand {
|
||||
// Requires that the two types unify, and prints an error message if they
|
||||
// don't.
|
||||
fn suptype(fcx: @fn_ctxt, sp: span,
|
||||
expected: ty::t, actual: ty::t) {
|
||||
|
||||
// n.b.: order of actual, expected is reversed
|
||||
alt infer::mk_subty(fcx.infcx, actual, expected) {
|
||||
result::ok(()) { /* ok */ }
|
||||
result::err(err) {
|
||||
fcx.report_mismatched_types(sp, expected, actual, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eqtype(fcx: @fn_ctxt, sp: span,
|
||||
expected: ty::t, actual: ty::t) {
|
||||
|
||||
alt infer::mk_eqty(fcx.infcx, actual, expected) {
|
||||
result::ok(()) { /* ok */ }
|
||||
result::err(err) {
|
||||
fcx.report_mismatched_types(sp, expected, actual, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that the type `actual` can be assigned to `expected`.
|
||||
fn assign(fcx: @fn_ctxt, sp: span, borrow_scope: ast::node_id,
|
||||
expected: ty::t, expr: @ast::expr) {
|
||||
let expr_ty = fcx.expr_ty(expr);
|
||||
let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
|
||||
alt infer::mk_assignty(fcx.infcx, anmnt, expr_ty, expected) {
|
||||
result::ok(()) { /* ok */ }
|
||||
result::err(err) {
|
||||
fcx.report_mismatched_types(sp, expected, expr_ty, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the two types unify and false if they don't.
|
||||
fn are_compatible(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> bool {
|
||||
alt fcx.mk_eqty(expected, actual) {
|
||||
|
|
@ -1594,8 +1558,7 @@ fn lookup_field_ty(tcx: ty::ctxt, class_id: ast::def_id,
|
|||
*/
|
||||
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);
|
||||
ty::encl_region(fcx.ccx.tcx, expr.id)
|
||||
}
|
||||
|
||||
fn deref(fcx: @fn_ctxt, base: @ast::expr) -> ty::region {
|
||||
|
|
@ -1970,7 +1933,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
let ret_ty = ty::ty_fn_ret(fty);
|
||||
let arg_tys = vec::map(ty::ty_fn_args(fty)) {|a| a.ty };
|
||||
|
||||
check_fn(fcx.ccx, proto, decl, body, expr.id,
|
||||
check_fn(fcx.ccx, proto, decl, body,
|
||||
ret_ty, arg_tys, is_loop_body, some(fcx),
|
||||
fcx.self_ty);
|
||||
}
|
||||
|
|
@ -2780,6 +2743,7 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
|
|||
let cty = fcx.expr_ty(e);
|
||||
let declty = fcx.ccx.tcx.tcache.get(local_def(id)).ty;
|
||||
demand::suptype(fcx, e.span, declty, cty);
|
||||
regionck::regionck_expr(fcx, e);
|
||||
writeback::resolve_type_vars_in_expr(fcx, e);
|
||||
}
|
||||
|
||||
|
|
@ -2984,7 +2948,7 @@ fn check_bare_fn(ccx: @crate_ctxt,
|
|||
let fty = ty::node_id_to_type(ccx.tcx, id);
|
||||
let ret_ty = ty::ty_fn_ret(fty);
|
||||
let arg_tys = vec::map(ty::ty_fn_args(fty)) {|a| a.ty };
|
||||
check_fn(ccx, ast::proto_bare, decl, body, id,
|
||||
check_fn(ccx, ast::proto_bare, decl, body,
|
||||
ret_ty, arg_tys, false, none, self_ty);
|
||||
}
|
||||
|
||||
|
|
@ -2992,7 +2956,6 @@ fn check_fn(ccx: @crate_ctxt,
|
|||
proto: ast::proto,
|
||||
decl: ast::fn_decl,
|
||||
body: ast::blk,
|
||||
fid: ast::node_id,
|
||||
ret_ty: ty::t,
|
||||
arg_tys: [ty::t],
|
||||
indirect_ret: bool,
|
||||
|
|
@ -3012,7 +2975,7 @@ fn check_fn(ccx: @crate_ctxt,
|
|||
let old_isr = option::map_default(old_fcx, @nil) {
|
||||
|fcx| fcx.in_scope_regions };
|
||||
collect_bound_regions_in_tys(tcx, old_isr, all_tys) {
|
||||
|br| ty::re_free(fid, br) }
|
||||
|br| ty::re_free(body.node.id, br) }
|
||||
};
|
||||
|
||||
// Replace the bound regions that appear in the arg tys, ret ty, etc with
|
||||
|
|
@ -3108,6 +3071,7 @@ fn check_fn(ccx: @crate_ctxt,
|
|||
// resolved when the enclosing scope finishes up.
|
||||
if option::is_none(old_fcx) {
|
||||
vtable::resolve_in_block(fcx, body);
|
||||
regionck::regionck_fn(fcx, decl, body);
|
||||
writeback::resolve_type_vars_in_fn(fcx, decl, body);
|
||||
}
|
||||
|
||||
|
|
|
|||
39
src/rustc/middle/typeck/demand.rs
Normal file
39
src/rustc/middle/typeck/demand.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Requires that the two types unify, and prints an error message if they
|
||||
// don't.
|
||||
fn suptype(fcx: @fn_ctxt, sp: span,
|
||||
expected: ty::t, actual: ty::t) {
|
||||
|
||||
// n.b.: order of actual, expected is reversed
|
||||
alt infer::mk_subty(fcx.infcx, actual, expected) {
|
||||
result::ok(()) { /* ok */ }
|
||||
result::err(err) {
|
||||
fcx.report_mismatched_types(sp, expected, actual, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eqtype(fcx: @fn_ctxt, sp: span,
|
||||
expected: ty::t, actual: ty::t) {
|
||||
|
||||
alt infer::mk_eqty(fcx.infcx, actual, expected) {
|
||||
result::ok(()) { /* ok */ }
|
||||
result::err(err) {
|
||||
fcx.report_mismatched_types(sp, expected, actual, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that the type `actual` can be assigned to `expected`.
|
||||
fn assign(fcx: @fn_ctxt, sp: span, borrow_scope: ast::node_id,
|
||||
expected: ty::t, expr: @ast::expr) {
|
||||
let expr_ty = fcx.expr_ty(expr);
|
||||
let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
|
||||
alt infer::mk_assignty(fcx.infcx, anmnt, expr_ty, expected) {
|
||||
result::ok(()) { /* ok */ }
|
||||
result::err(err) {
|
||||
fcx.report_mismatched_types(sp, expected, expr_ty, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1512,20 +1512,18 @@ impl of combine for lub {
|
|||
|
||||
(f @ ty::re_free(f_id, _), ty::re_scope(s_id)) |
|
||||
(ty::re_scope(s_id), f @ ty::re_free(f_id, _)) {
|
||||
// For LUB, generally the scope is within the fn and
|
||||
// the free region is a parameter to the fn. In that case,
|
||||
// the free region will always live as long as the fn,
|
||||
// which is longer than the scope.
|
||||
//
|
||||
// However, with nested fns, it can happen that the
|
||||
// scope surrounds the fn itself. In that case, we do
|
||||
// not know which will live longer---it depends on the
|
||||
// value provided for the free region in any given
|
||||
// call. And so we must just back off to re_static as
|
||||
// the LUB.
|
||||
// A "free" region can be interpreted as "some region
|
||||
// at least as big as the block f_id". So, we can
|
||||
// reasonably compare free regions and scopes:
|
||||
let rm = self.infcx().tcx.region_map;
|
||||
alt region::nearest_common_ancestor(rm, f_id, s_id) {
|
||||
// if the free region's scope `f_id` is bigger than
|
||||
// the scope region `s_id`, then the LUB is the free
|
||||
// region itself:
|
||||
some(r_id) if r_id == f_id { ok(f) }
|
||||
|
||||
// otherwise, we don't know what the free region is,
|
||||
// so we must conservatively say the LUB is static:
|
||||
_ { ok(ty::re_static) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1703,15 +1701,11 @@ impl of combine for glb {
|
|||
|
||||
(ty::re_free(f_id, _), s @ ty::re_scope(s_id)) |
|
||||
(s @ ty::re_scope(s_id), ty::re_free(f_id, _)) {
|
||||
// For GLB, generally the scope is within the fn and
|
||||
// the free region is a parameter to the fn. In that case,
|
||||
// the scope is always shorter than the free region.
|
||||
//
|
||||
// However, with nested fns, it can happen that the
|
||||
// scope surrounds the fn itself. In that case, we do
|
||||
// not know which will live longer---it depends on the
|
||||
// value provided for the free region in any given
|
||||
// call. And so we cannot give a GLB.
|
||||
// Free region is something "at least as big as
|
||||
// `f_id`." If we find that the scope `f_id` is bigger
|
||||
// than the scope `s_id`, then we can say that the GLB
|
||||
// is the scope `s_id`. Otherwise, as we do not know
|
||||
// big the free region is precisely, the GLB is undefined.
|
||||
let rm = self.infcx().tcx.region_map;
|
||||
alt region::nearest_common_ancestor(rm, f_id, s_id) {
|
||||
some(r_id) if r_id == f_id { ok(s) }
|
||||
|
|
|
|||
132
src/rustc/middle/typeck/regionck.rs
Normal file
132
src/rustc/middle/typeck/regionck.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
|
||||
The region check is a final pass that runs over the AST after we have
|
||||
inferred the type constraints but before we have actually finalized
|
||||
the types. It's purpose is to embed some final region constraints.
|
||||
The reason that this is not done earlier is that sometimes we don't
|
||||
know whether a given type will be a region pointer or not until this
|
||||
phase.
|
||||
|
||||
In particular, we ensure that, if the type of an expression or
|
||||
variable is `&r.T`, then the expression or variable must occur within
|
||||
the region scope `r`.
|
||||
|
||||
*/
|
||||
|
||||
import util::ppaux;
|
||||
import syntax::print::pprust;
|
||||
|
||||
fn regionck_expr(fcx: @fn_ctxt, e: @ast::expr) {
|
||||
let v = regionck_visitor(fcx);
|
||||
v.visit_expr(e, fcx, v);
|
||||
}
|
||||
|
||||
fn regionck_fn(fcx: @fn_ctxt,
|
||||
_decl: ast::fn_decl,
|
||||
blk: ast::blk) {
|
||||
let v = regionck_visitor(fcx);
|
||||
v.visit_block(blk, fcx, v);
|
||||
}
|
||||
|
||||
type rvt = visit::vt<@fn_ctxt>;
|
||||
|
||||
fn regionck_visitor(_fcx: @fn_ctxt) -> rvt {
|
||||
visit::mk_vt(@{visit_item: visit_item,
|
||||
visit_stmt: visit_stmt,
|
||||
visit_expr: visit_expr,
|
||||
visit_block: visit_block,
|
||||
visit_pat: visit_pat,
|
||||
visit_local: visit_local
|
||||
with *visit::default_visitor()})
|
||||
}
|
||||
|
||||
fn visit_item(_item: @ast::item, &&_fcx: @fn_ctxt, _v: rvt) {
|
||||
// Ignore items
|
||||
}
|
||||
|
||||
fn visit_local(l: @ast::local, &&fcx: @fn_ctxt, v: rvt) {
|
||||
visit::visit_local(l, fcx, v);
|
||||
}
|
||||
|
||||
fn visit_pat(p: @ast::pat, &&fcx: @fn_ctxt, v: rvt) {
|
||||
visit::visit_pat(p, fcx, v);
|
||||
}
|
||||
|
||||
fn visit_block(b: ast::blk, &&fcx: @fn_ctxt, v: rvt) {
|
||||
visit::visit_block(b, fcx, v);
|
||||
}
|
||||
|
||||
fn visit_expr(e: @ast::expr, &&fcx: @fn_ctxt, v: rvt) {
|
||||
#debug["visit_expr(e=%s)", pprust::expr_to_str(e)];
|
||||
|
||||
visit_ty(fcx.expr_ty(e), e.id, e.span, fcx);
|
||||
visit::visit_expr(e, fcx, v);
|
||||
}
|
||||
|
||||
fn visit_stmt(s: @ast::stmt, &&fcx: @fn_ctxt, v: rvt) {
|
||||
visit::visit_stmt(s, fcx, v);
|
||||
}
|
||||
|
||||
fn visit_ty(ty: ty::t,
|
||||
id: ast::node_id,
|
||||
span: span,
|
||||
fcx: @fn_ctxt) {
|
||||
|
||||
// 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.
|
||||
let ty = alt infer::resolve_deep(fcx.infcx, ty, false) {
|
||||
result::err(_) { ret; }
|
||||
result::ok(ty) { ty }
|
||||
};
|
||||
|
||||
// find the region where this expr evaluation is taking place
|
||||
let tcx = fcx.ccx.tcx;
|
||||
let encl_region = ty::encl_region(tcx, id);
|
||||
|
||||
#debug["visit_ty(ty=%s, id=%d, encl_region=%s)",
|
||||
ppaux::ty_to_str(tcx, ty),
|
||||
id,
|
||||
ppaux::region_to_str(tcx, encl_region)];
|
||||
|
||||
// Otherwise, look at the type and see if it is a region pointer.
|
||||
if !ty::type_has_regions(ty) { ret; }
|
||||
ty::walk_regions_and_ty(
|
||||
tcx, ty,
|
||||
{ |r| constrain_region(fcx, encl_region, span, r); },
|
||||
{ |t| ty::type_has_regions(t) });
|
||||
|
||||
fn constrain_region(fcx: @fn_ctxt,
|
||||
encl_region: ty::region,
|
||||
span: span,
|
||||
region: ty::region) {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
|
||||
#debug["constrain_region(encl_region=%s, region=%s)",
|
||||
ppaux::region_to_str(tcx, encl_region),
|
||||
ppaux::region_to_str(tcx, region)];
|
||||
|
||||
alt region {
|
||||
ty::re_bound(_) {
|
||||
// a bound region is one which appears inside an fn type.
|
||||
// (e.g., the `&` in `fn(&T)`). Such regions need not be
|
||||
// constrained by `encl_region` as they are placeholders
|
||||
// for regions that are as-yet-unknown.
|
||||
ret;
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
|
||||
alt fcx.mk_subr(encl_region, region) {
|
||||
result::err(_) {
|
||||
tcx.sess.span_err(
|
||||
span,
|
||||
#fmt["reference is not valid outside \
|
||||
of its lifetime, %s",
|
||||
ppaux::region_to_str(tcx, region)]);
|
||||
}
|
||||
result::ok(()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,16 +150,19 @@ fn visit_item(_item: @ast::item, _wbcx: wb_ctxt, _v: wb_vt) {
|
|||
// Ignore items
|
||||
}
|
||||
|
||||
fn mk_visitor() -> visit::vt<wb_ctxt> {
|
||||
visit::mk_vt(@{visit_item: visit_item,
|
||||
visit_stmt: visit_stmt,
|
||||
visit_expr: visit_expr,
|
||||
visit_block: visit_block,
|
||||
visit_pat: visit_pat,
|
||||
visit_local: visit_local
|
||||
with *visit::default_visitor()})
|
||||
}
|
||||
|
||||
fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool {
|
||||
let wbcx = {fcx: fcx, mut success: true};
|
||||
let visit =
|
||||
visit::mk_vt(@{visit_item: visit_item,
|
||||
visit_stmt: visit_stmt,
|
||||
visit_expr: visit_expr,
|
||||
visit_block: visit_block,
|
||||
visit_pat: visit_pat,
|
||||
visit_local: visit_local
|
||||
with *visit::default_visitor()});
|
||||
let visit = mk_visitor();
|
||||
visit.visit_expr(e, wbcx, visit);
|
||||
ret wbcx.success;
|
||||
}
|
||||
|
|
@ -168,14 +171,7 @@ fn resolve_type_vars_in_fn(fcx: @fn_ctxt,
|
|||
decl: ast::fn_decl,
|
||||
blk: ast::blk) -> bool {
|
||||
let wbcx = {fcx: fcx, mut success: true};
|
||||
let visit =
|
||||
visit::mk_vt(@{visit_item: visit_item,
|
||||
visit_stmt: visit_stmt,
|
||||
visit_expr: visit_expr,
|
||||
visit_block: visit_block,
|
||||
visit_pat: visit_pat,
|
||||
visit_local: visit_local
|
||||
with *visit::default_visitor()});
|
||||
let visit = mk_visitor();
|
||||
visit.visit_block(blk, wbcx, visit);
|
||||
for decl.inputs.each {|arg|
|
||||
resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ mod middle {
|
|||
mod ast_map;
|
||||
mod resolve;
|
||||
mod typeck {
|
||||
mod regionck;
|
||||
mod demand;
|
||||
mod infer;
|
||||
mod astconv;
|
||||
mod collect;
|
||||
|
|
@ -72,7 +74,6 @@ mod middle {
|
|||
mod capture;
|
||||
mod pat_util;
|
||||
mod region;
|
||||
mod regionck;
|
||||
mod const_eval;
|
||||
mod astencode;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ fn foo(x: &uint) -> &uint { x }
|
|||
fn main() {
|
||||
let p = @3u;
|
||||
let r = foo(p);
|
||||
//!^ ERROR reference is not valid
|
||||
assert *p == *r;
|
||||
//!^ ERROR reference is not valid
|
||||
}
|
||||
|
|
|
|||
7
src/test/compile-fail/regions-escape-into-other-fn.rs
Normal file
7
src/test/compile-fail/regions-escape-into-other-fn.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fn foo(x: &uint) -> &uint { x }
|
||||
fn bar(x: &uint) -> uint { *x }
|
||||
|
||||
fn main() {
|
||||
let p = @3u;
|
||||
bar(foo(p)); //! ERROR reference is not valid
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue