Accept returning refs rooted in an arg from a by-ref funtion

Issue #918
This commit is contained in:
Marijn Haverbeke 2011-09-14 16:19:15 +02:00
parent a3c449df74
commit c6619f9ce0
3 changed files with 49 additions and 21 deletions

View file

@ -119,7 +119,7 @@ fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
}
ast::expr_ret(oexpr) {
if sc.ret_style == ast::return_ref && !is_none(oexpr) {
check_ret_ref(*cx, option::get(oexpr));
check_ret_ref(*cx, sc, option::get(oexpr));
}
handled = false;
}
@ -266,18 +266,53 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] {
ret bindings;
}
fn check_ret_ref(cx: ctx, expr: @ast::expr) {
fn check_ret_ref(cx: ctx, sc: scope, expr: @ast::expr) {
let root = expr_root(cx.tcx, expr, false);
let bad = none;
let mut_field = mut_field(root.ds);
alt path_def(cx, root.ex) {
none. { bad = some("temporary"); }
some(ast::def_arg(_, mode)) {
if mode == ast::by_move { bad = some("move-mode parameter"); }
if mut_field(root.ds) { bad = some("mutable field"); }
some(ast::def_local(did)) | some(ast::def_binding(did)) |
some(ast::def_arg(did, _)) {
let cur_node = did.node;
while true {
alt cx.tcx.items.find(cur_node) {
some(ast_map::node_arg(arg)) {
if arg.mode == ast::by_move {
bad = some("move-mode parameter");
}
break;
}
_ {}
}
alt vec::find({|b| b.node_id == cur_node}, sc.bs) {
some(b) {
if vec::len(b.unsafe_tys) > 0u {
mut_field = true;
break;
}
if is_none(b.root_var) {
bad = some("function-local value");
break;
}
if b.copied == copied {
bad = some("implicitly copied reference");
break;
}
b.copied = not_allowed;
cur_node = option::get(b.root_var);
}
none. {
bad = some("function-local value");
break;
}
}
}
}
// FIXME allow references to constants and static items?
_ { bad = some("non-argument value"); }
_ { bad = some("non-local value"); }
}
if mut_field { bad = some("mutable field"); }
alt bad {
some(name) {
cx.tcx.sess.span_err(expr.span, "can not return a reference " +

View file

@ -347,7 +347,6 @@ fn visit_fn_with_scope(e: @env, f: ast::_fn, tp: [ast::ty_param], sp: span,
// here's where we need to set up the mapping
// for f's constrs in the table.
for c: @ast::constr in f.decl.constraints { resolve_constr(e, c, sc, v); }
visit::visit_fn(f, tp, sp, name, id,
cons(scope_fn(f.decl, f.proto, tp), @sc), v);

View file

@ -3530,7 +3530,8 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t<generic_info>,
let to_zero = [];
let to_revoke = [];
let tcx = bcx_tcx(cx);
let ccx = bcx_ccx(cx);
let tcx = ccx.tcx;
let bcx: @block_ctxt = cx;
let by_ref = ty::ty_fn_ret_style(tcx, fn_ty) == ast::return_ref;
// Arg 0: Output pointer.
@ -3549,7 +3550,7 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t<generic_info>,
}
let retty = ty::ty_fn_ret(tcx, fn_ty);
let llretslot_res = if by_ref {
rslt(cx, alloca(cx, T_ptr(type_of_or_i8(cx, retty))))
rslt(cx, alloca(cx, T_ptr(type_of_or_i8(bcx, retty))))
} else { alloc_ty(bcx, retty) };
bcx = llretslot_res.bcx;
let llretslot = llretslot_res.val;
@ -3568,7 +3569,8 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t<generic_info>,
// type deep in a structure -- which the caller has a concrete view
// of. If so, cast the caller's view of the restlot to the callee's
// view, for the sake of making a type-compatible call.
let llretty = T_ptr(type_of_inner(bcx_ccx(bcx), bcx.sp, retty));
let llretty = T_ptr(type_of_inner(ccx, bcx.sp, retty));
if by_ref { llretty = T_ptr(llretty); }
llargs += [PointerCast(cx, llretslot, llretty)];
} else { llargs += [llretslot]; }
@ -3588,7 +3590,7 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t<generic_info>,
let lli =
if ty::type_contains_params(tcx, retty) {
let body_ty = ty::mk_iter_body_fn(tcx, retty);
let body_llty = type_of_inner(bcx_ccx(cx), cx.sp, body_ty);
let body_llty = type_of_inner(ccx, cx.sp, body_ty);
PointerCast(bcx, lli, T_ptr(body_llty))
} else { lli };
llargs += [Load(cx, lli)];
@ -3600,7 +3602,7 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t<generic_info>,
// First we figure out the caller's view of the types of the arguments.
// This will be needed if this is a generic call, because the callee has
// to cast her view of the arguments to the caller's view.
let arg_tys = type_of_explicit_args(bcx_ccx(cx), cx.sp, args);
let arg_tys = type_of_explicit_args(ccx, cx.sp, args);
let i = 0u;
for e: @ast::expr in es {
if is_terminated(bcx) {
@ -3631,15 +3633,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
let cx = new_scope_block_ctxt(in_cx, "call");
Br(in_cx, cx.llbb);
let f_res = trans_lval_gen(cx, f);
let fn_ty: ty::t;
alt f_res.method_ty {
some(meth) {
// self-call
fn_ty = meth;
}
_ { fn_ty = ty::expr_ty(bcx_tcx(cx), f); }
}
let fn_ty = ty::expr_ty(bcx_tcx(cx), f);
let bcx = f_res.res.bcx;
let faddr = f_res.res.val;