diff --git a/src/comp/metadata/tydecode.rs b/src/comp/metadata/tydecode.rs index b4ea33a12778..250522dda4fe 100644 --- a/src/comp/metadata/tydecode.rs +++ b/src/comp/metadata/tydecode.rs @@ -55,8 +55,11 @@ fn parse_ty_data(data: @[u8], crate_num: int, pos: uint, len: uint, fn parse_ret_ty(st: @pstate, sd: str_def) -> (ast::ret_style, ty::t) { alt peek(st) as char { '!' { next(st); (ast::noreturn, ty::mk_bot(st.tcx)) } - '&' { next(st); (ast::return_ref(false), parse_ty(st, sd)) } - '^' { next(st); (ast::return_ref(true), parse_ty(st, sd)) } + '&' | '^' { + let mut = next(st) == '^' as u8; + let arg = next(st) as uint; + (ast::return_ref(mut, arg), parse_ty(st, sd)) + } _ { (ast::return_val, parse_ty(st, sd)) } } } diff --git a/src/comp/metadata/tyencode.rs b/src/comp/metadata/tyencode.rs index 234968cd97c9..99f171907dee 100644 --- a/src/comp/metadata/tyencode.rs +++ b/src/comp/metadata/tyencode.rs @@ -220,8 +220,9 @@ fn enc_ty_fn(w: io::writer, cx: @ctxt, args: [ty::arg], out: ty::t, } alt cf { noreturn. { w.write_char('!'); } - return_ref(mut) { + return_ref(mut, arg) { w.write_char(mut ? '^' : '&'); + w.write_bytes([arg as u8]); enc_ty(w, cx, out); } _ { enc_ty(w, cx, out); } diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index 35733d6e3f67..2c1cac9ec8db 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -22,8 +22,10 @@ type binding = @{node_id: node_id, unsafe_tys: [ty::t], mutable ok: valid, mutable copied: copied}; + +tag ret_info { by_ref(bool, node_id); other; } // FIXME it may be worthwhile to use a linked list of bindings instead -type scope = {bs: [binding], ret_style: ast::ret_style}; +type scope = {bs: [binding], ret_info: ret_info}; fn mk_binding(cx: ctx, id: node_id, span: span, root_var: option::t, unsafe: [ty::t]) -> binding { @@ -49,8 +51,7 @@ fn check_crate(tcx: ty::ctxt, crate: @ast::crate) -> copy_map { visit_expr: bind visit_expr(cx, _, _, _), visit_block: bind visit_block(cx, _, _, _) with *visit::default_visitor::()}; - visit::visit_crate(*crate, {bs: [], ret_style: ast::return_val}, - visit::mk_vt(v)); + visit::visit_crate(*crate, {bs: [], ret_info: other}, visit::mk_vt(v)); tcx.sess.abort_if_errors(); ret cx.copy_map; } @@ -70,7 +71,11 @@ fn visit_fn(cx: @ctx, f: ast::_fn, _tp: [ast::ty_param], _sp: span, "reference-returning functions may not " + "return implicitly"); } - v.visit_block(f.body, {bs: bs, ret_style: f.decl.cf}, v); + let ret_info = alt f.decl.cf { + ast::return_ref(mut, n_arg) { by_ref(mut, f.decl.inputs[n_arg].id) } + _ { other } + }; + v.visit_block(f.body, {bs: bs, ret_info: ret_info}, v); } fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt) { @@ -117,9 +122,9 @@ fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt) { } ast::expr_ret(oexpr) { if !is_none(oexpr) { - alt sc.ret_style { - ast::return_ref(mut) { - check_ret_ref(*cx, sc, mut, option::get(oexpr)); + alt sc.ret_info { + by_ref(mut, arg_node_id) { + check_ret_ref(*cx, sc, mut, arg_node_id, option::get(oexpr)); } _ {} } @@ -180,20 +185,20 @@ fn add_bindings_for_let(cx: ctx, &bs: [binding], loc: @ast::local) { alt root.ex.node { ast::expr_call(f, args) { let fty = ty::type_autoderef(cx.tcx, ty::expr_ty(cx.tcx, f)); - let ret_style = ty::ty_fn_ret_style(cx.tcx, fty); - if ast_util::ret_by_ref(ret_style) { - // FIXME pick right arg - let arg = args[0]; + alt ty::ty_fn_ret_style(cx.tcx, fty) { + ast::return_ref(mut, arg_n) { + let arg = args[arg_n]; let arg_root = expr_root(cx.tcx, arg, false); root_var = path_def_id(cx, arg_root.ex); if !is_none(root_var) { is_temp = false; - if ret_style == ast::return_ref(true) { + if mut { outer_ds = [@{mut: true, kind: unbox, outer_t: ty::expr_ty(cx.tcx, arg)}]; } outer_ds = *arg_root.ds + outer_ds; } + } } } _ {} @@ -333,12 +338,13 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] { ret bindings; } -fn check_ret_ref(cx: ctx, sc: scope, mut: bool, expr: @ast::expr) { +fn check_ret_ref(cx: ctx, sc: scope, mut: bool, arg_node_id: node_id, + 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"); } + none. { bad = some("a temporary"); } some(ast::def_local(did, _)) | some(ast::def_binding(did)) | some(ast::def_arg(did, _)) { let cur_node = did.node; @@ -346,7 +352,10 @@ fn check_ret_ref(cx: ctx, sc: scope, mut: bool, expr: @ast::expr) { alt cx.tcx.items.find(cur_node) { some(ast_map::node_arg(arg, _)) { if arg.mode == ast::by_move { - bad = some("move-mode parameter"); + bad = some("a move-mode parameter"); + } + if cur_node != arg_node_id { + bad = some("the wrong parameter"); } break; } @@ -359,31 +368,30 @@ fn check_ret_ref(cx: ctx, sc: scope, mut: bool, expr: @ast::expr) { break; } if is_none(b.root_var) { - bad = some("function-local value"); + bad = some("a function-local value"); break; } if b.copied == copied { - bad = some("implicitly copied reference"); + bad = some("an implicitly copied reference"); break; } b.copied = not_allowed; cur_node = option::get(b.root_var); } none. { - bad = some("function-local value"); + bad = some("a function-local value"); break; } } } } - // FIXME allow references to constants and static items? - _ { bad = some("non-local value"); } + _ { bad = some("a non-local value"); } } - if mut_field && !mut { bad = some("mutable field"); } + if mut_field && !mut { bad = some("a mutable field"); } alt bad { some(name) { - cx.tcx.sess.span_err(expr.span, "can not return a reference " + - "to a " + name); + cx.tcx.sess.span_err(expr.span, "can not return a reference to " + + name); } _ {} } @@ -402,8 +410,7 @@ fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope, for pat in a.pats { for proot in *pattern_roots(cx.tcx, *root.ds, pat) { let canon_id = pat_id_map.get(proot.name); - // FIXME I wanted to use a block, but that hit a - // typestate bug. + // FIXME I wanted to use a block here, but that hit bug #913 fn match(x: info, canon: node_id) -> bool { x.id == canon } alt vec::find(bind match(_, canon_id), binding_info) { some(s) { s.unsafe += inner_mut(proot.ds); } diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 6fbe21e60de0..a71811fccf74 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -2459,7 +2459,10 @@ fn type_err_to_str(err: ty::type_err) -> str { alt s { ast::noreturn. { "non-returning" } ast::return_val. { "return-by-value" } - ast::return_ref(_) { "return-by-reference" } + ast::return_ref(mut, arg) { + #fmt("return-by-%sreference on arg %u", + mut ? "mutable-" : "", arg) + } } } ret to_str(actual) + " function found where " + to_str(expect) + diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index 7f64b8de6993..5a34d1a4c161 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -383,7 +383,7 @@ tag ret_style { noreturn; // functions with return type _|_ that always // raise an error or exit (i.e. never return to the caller) return_val; // everything else - return_ref(bool); + return_ref(bool, uint); } type _fn = {decl: fn_decl, proto: proto, body: blk}; diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs index 17c8ac2cef15..aebf9b3e680f 100644 --- a/src/comp/syntax/ast_util.rs +++ b/src/comp/syntax/ast_util.rs @@ -215,7 +215,7 @@ fn ternary_to_if(e: @expr) -> @expr { fn ret_by_ref(style: ret_style) -> bool { alt style { - return_ref(_) { true } + return_ref(_, _) { true } _ { false } } } diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 4a449c25a97f..b2da68b0cd95 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -285,7 +285,7 @@ fn parse_ty_fn(proto: ast::proto, p: parser) -> ast::ty_ { // FIXME: there's no syntax for this right now anyway // auto constrs = parse_constrs(~[], p); let constrs: [@ast::constr] = []; - let (ret_style, ret_ty) = parse_ret_ty(p); + let (ret_style, ret_ty) = parse_ret_ty(p, vec::len(inputs.node)); ret ast::ty_fn(proto, inputs.node, ret_ty, ret_style, constrs); } @@ -437,7 +437,7 @@ fn parse_ty_postfix(orig_t: ast::ty_, p: parser, colons_before_params: bool) } } -fn parse_ret_ty(p: parser) -> (ast::ret_style, @ast::ty) { +fn parse_ret_ty(p: parser, n_args: uint) -> (ast::ret_style, @ast::ty) { ret if eat(p, token::RARROW) { let lo = p.get_lo_pos(); if eat(p, token::NOT) { @@ -445,7 +445,20 @@ fn parse_ret_ty(p: parser) -> (ast::ret_style, @ast::ty) { } else { let style = ast::return_val; if eat(p, token::BINOP(token::AND)) { - style = ast::return_ref(eat(p, token::NOT)); + if n_args == 0u { + p.fatal("can not return reference from argument-less fn"); + } + let mut_root = eat(p, token::NOT), arg = 0u; + 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 { + p.fatal("referenced argument does not exist"); + } + style = ast::return_ref(mut_root, arg); }; (style, parse_ty(p, false)) } @@ -1734,7 +1747,7 @@ fn parse_fn_decl(p: parser, purity: ast::purity, il: ast::inlineness) -> p.bump(); constrs = parse_constrs(bind parse_ty_constr(inputs.node, _), p); } - let (ret_style, ret_ty) = parse_ret_ty(p); + let (ret_style, ret_ty) = parse_ret_ty(p, vec::len(inputs.node)); ret {inputs: inputs.node, output: ret_ty, purity: purity, diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs index b6964b429f51..d6af92cca0db 100644 --- a/src/comp/syntax/print/pprust.rs +++ b/src/comp/syntax/print/pprust.rs @@ -1145,7 +1145,12 @@ fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl, constrs: [@ast::constr]) { space_if_not_bol(s); word_space(s, "->"); alt decl.cf { - ast::return_ref(mut) { word(s.s, mut ? "&!" : "&"); } + ast::return_ref(mut, arg) { + word(s.s, mut ? "&!" : "&"); + if vec::len(decl.inputs) > 1u { + word(s.s, std::uint::str(arg)); + } + } _ {} } print_type(s, decl.output); @@ -1347,7 +1352,10 @@ fn print_ty_fn(s: ps, proto: ast::proto, id: option::t, word_nbsp(s, "!"); } else { alt cf { - ast::return_ref(mut) { word(s.s, mut ? "&!" : "&"); } + ast::return_ref(mut, arg) { + word(s.s, mut ? "&!" : "&"); + if vec::len(inputs) > 1u { word(s.s, std::uint::str(arg)); } + } _ {} } print_type(s, output); diff --git a/src/comp/util/ppaux.rs b/src/comp/util/ppaux.rs index 7e84c08a1d0e..44cf92ea1b98 100644 --- a/src/comp/util/ppaux.rs +++ b/src/comp/util/ppaux.rs @@ -61,8 +61,9 @@ fn ty_to_str(cx: ctxt, typ: t) -> str { s += " -> "; alt cf { ast::noreturn. { s += "!"; } - ast::return_ref(mut) { + ast::return_ref(mut, arg) { s += mut ? "&!" : "&"; + if vec::len(inputs) > 1u { s += std::uint::str(arg); } s += ty_to_str(cx, output); } ast::return_val. { s += ty_to_str(cx, output); }