about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2011-09-14 16:19:15 +0200
committerMarijn Haverbeke <marijnh@gmail.com>2011-09-14 16:19:15 +0200
commitc6619f9ce04f98b4ec78fd856fd098d88fa7ec9f (patch)
tree0781bc9a4d9d467e87ee25854ea0153b401567e4
parenta3c449df7426ea2f34be15fb5312a2620f871668 (diff)
downloadrust-c6619f9ce04f98b4ec78fd856fd098d88fa7ec9f.tar.gz
rust-c6619f9ce04f98b4ec78fd856fd098d88fa7ec9f.zip
Accept returning refs rooted in an arg from a by-ref funtion
Issue #918
-rw-r--r--src/comp/middle/alias.rs47
-rw-r--r--src/comp/middle/resolve.rs1
-rw-r--r--src/comp/middle/trans.rs22
3 files changed, 49 insertions, 21 deletions
diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs
index bd613b06574..ae71acdaa11 100644
--- a/src/comp/middle/alias.rs
+++ b/src/comp/middle/alias.rs
@@ -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 " +
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 37370b0b4f0..4068c2e7c8e 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -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);
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 63cae740cc4..e1f3ae190a4 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -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;