diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index 360619b73e0..10ddb097101 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -72,7 +72,9 @@ fn visit_fn(cx: @ctx, f: ast::_fn, _tp: [ast::ty_param], _sp: span, "return implicitly"); } let ret_info = alt f.decl.cf { - ast::return_ref(mut, n_arg) { by_ref(mut, f.decl.inputs[n_arg].id) } + ast::return_ref(mut, n_arg) { + by_ref(mut, f.decl.inputs[n_arg - 1u].id) + } _ { other } }; v.visit_block(f.body, {bs: bs, ret_info: ret_info}, v); @@ -220,7 +222,9 @@ fn cant_copy(cx: ctx, b: binding) -> bool { fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] { let fty = ty::type_autoderef(cx.tcx, ty::expr_ty(cx.tcx, f)); - let ret_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(cx.tcx, fty)); + let by_ref = alt ty::ty_fn_ret_style(cx.tcx, fty) { + ast::return_ref(_, arg_n) { arg_n } _ { 0u } + }; let arg_ts = ty::ty_fn_args(cx.tcx, fty); let mut_roots: [{arg: uint, node: node_id}] = []; let bindings = []; @@ -246,7 +250,9 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] { mutable ok: valid, mutable copied: alt arg_t.mode { ast::by_move. { copied } - ast::by_ref. { ret_ref ? not_allowed : not_copied } + ast::by_ref. { + i + 1u == by_ref ? not_allowed : not_copied + } ast::by_mut_ref. { not_allowed } }}]; i += 1u; @@ -683,7 +689,7 @@ fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool) -> let fty = ty::type_autoderef(cx.tcx, ty::expr_ty(cx.tcx, f)); alt ty::ty_fn_ret_style(cx.tcx, fty) { ast::return_ref(mut, arg_n) { - let arg = args[arg_n]; + let arg = args[arg_n - 1u]; let arg_root = expr_root(cx, arg, false); ret {ex: arg_root.ex, ds: @(*arg_root.ds + diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 42d57031d64..8184376d789 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -3557,9 +3557,11 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty0: TypeRef, // - create_llargs_for_fn_args. // - new_fn_ctxt // - trans_args -fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t, +fn trans_args(cx: @block_ctxt, outer_cx: @block_ctxt, llenv: ValueRef, + gen: option::t, lliterbody: option::t, es: [@ast::expr], fn_ty: ty::t) -> {bcx: @block_ctxt, + outer_cx: @block_ctxt, args: [ValueRef], retslot: ValueRef, to_zero: [{v: ValueRef, t: ty::t}], @@ -3574,7 +3576,8 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t, let ccx = bcx_ccx(cx); let tcx = ccx.tcx; let bcx: @block_ctxt = cx; - let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(tcx, fn_ty)); + let ret_style = ty::ty_fn_ret_style(tcx, fn_ty); + let by_ref = ast_util::ret_by_ref(ret_style); // Arg 0: Output pointer. // FIXME: test case looks like @@ -3583,6 +3586,7 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t, // This means an earlier arg was divergent. // So this arg can't be evaluated. ret {bcx: bcx, + outer_cx: outer_cx, args: [], retslot: C_nil(), to_zero: to_zero, @@ -3652,13 +3656,18 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t, // So this arg can't be evaluated. break; } - let r = - trans_arg_expr(bcx, args[i], arg_tys[i], to_zero, to_revoke, e); - bcx = r.bcx; + let is_referenced = alt ret_style { + ast::return_ref(_, arg_n) { i + 1u == arg_n } + _ { false } + }; + let r = trans_arg_expr(is_referenced ? outer_cx : bcx, + args[i], arg_tys[i], to_zero, to_revoke, e); + if is_referenced { outer_cx = r.bcx; } else { bcx = r.bcx; } llargs += [r.val]; i += 1u; } ret {bcx: bcx, + outer_cx: outer_cx, args: llargs, retslot: llretslot, to_zero: to_zero, @@ -3675,14 +3684,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr, let fn_ty = ty::type_autoderef(bcx_tcx(in_cx), fn_expr_ty); let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(bcx_tcx(in_cx), fn_ty)); - // Things that return by reference must put their arguments (FIXME only - // the referenced arguments) into the outer scope, so that they are still - // alive when the return value is used. - let cx = if by_ref { in_cx } else { - let cx = new_scope_block_ctxt(in_cx, "call"); - Br(in_cx, cx.llbb); - cx - }; + let cx = new_scope_block_ctxt(in_cx, "call"); let f_res = trans_lval_gen(cx, f); let bcx = f_res.res.bcx; @@ -3710,7 +3712,8 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr, let ret_ty = ty::node_id_to_type(bcx_tcx(cx), id); let args_res = - trans_args(bcx, llenv, f_res.generic, lliterbody, args, fn_ty); + trans_args(bcx, in_cx, llenv, f_res.generic, lliterbody, args, fn_ty); + Br(args_res.outer_cx, cx.llbb); bcx = args_res.bcx; let llargs = args_res.args; let llretslot = args_res.retslot; diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 5e66ba88126..dffa10ff831 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -448,16 +448,19 @@ fn parse_ret_ty(p: parser, n_args: uint) -> (ast::ret_style, @ast::ty) { if n_args == 0u { p.fatal("can not return reference from argument-less fn"); } - let mut_root = eat(p, token::NOT), arg = 0u; + let mut_root = eat(p, token::NOT), arg = 1u; alt p.peek() { token::LIT_INT(val) { p.bump(); arg = val as uint; } _ { if n_args > 1u { p.fatal("must specify referenced parameter"); } } } - if arg >= n_args { + if arg > n_args { p.fatal("referenced argument does not exist"); } + if arg == 0u { + p.fatal("referenced argument can't be 0"); + } style = ast::return_ref(mut_root, arg); }; (style, parse_ty(p, false)) diff --git a/src/test/compile-fail/ret-by-reference-wrong-param.rs b/src/test/compile-fail/ret-by-reference-wrong-param.rs index 41014bb17d7..7c479124805 100644 --- a/src/test/compile-fail/ret-by-reference-wrong-param.rs +++ b/src/test/compile-fail/ret-by-reference-wrong-param.rs @@ -1,6 +1,6 @@ // error-pattern:can not return a reference to the wrong parameter -fn f(a: int, b: int) -> &1 int { +fn f(a: int, b: int) -> &2 int { ret a; } diff --git a/src/test/run-pass/ret-by-reference.rs b/src/test/run-pass/ret-by-reference.rs index 1c2a3b9e50c..7e430c97115 100644 --- a/src/test/run-pass/ret-by-reference.rs +++ b/src/test/run-pass/ret-by-reference.rs @@ -6,7 +6,7 @@ fn get<@T>(opt: option) -> &T { } } -fn get_mut(a: {mutable x: @int}, _b: int) -> &!0 @int { +fn get_mut(a: {mutable x: @int}, _b: int) -> &!1 @int { ret a.x; }