about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2011-09-15 16:15:17 +0200
committerMarijn Haverbeke <marijnh@gmail.com>2011-09-15 16:15:17 +0200
commit3abe3671bd39a359de3421b062b98d3adaa1942d (patch)
treea6a95df9c3ab53b55cc902cf736b0a282be98da0
parent75eee8b194231c2fe808242a9828253125b854c8 (diff)
Require the parameter that will be referenced to be noted
Issue #918
-rw-r--r--src/comp/metadata/tydecode.rs7
-rw-r--r--src/comp/metadata/tyencode.rs3
-rw-r--r--src/comp/middle/alias.rs57
-rw-r--r--src/comp/middle/ty.rs5
-rw-r--r--src/comp/syntax/ast.rs2
-rw-r--r--src/comp/syntax/ast_util.rs2
-rw-r--r--src/comp/syntax/parse/parser.rs21
-rw-r--r--src/comp/syntax/print/pprust.rs12
-rw-r--r--src/comp/util/ppaux.rs3
9 files changed, 74 insertions, 38 deletions
diff --git a/src/comp/metadata/tydecode.rs b/src/comp/metadata/tydecode.rs
index b4ea33a1277..250522dda4f 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 234968cd97c..99f171907de 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 35733d6e3f6..2c1cac9ec8d 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<node_id>,
               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::<scope>()};
-    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<scope>) {
@@ -117,9 +122,9 @@ fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
       }
       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 6fbe21e60de..a71811fccf7 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 7f64b8de699..5a34d1a4c16 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 17c8ac2cef1..aebf9b3e680 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 4a449c25a97..b2da68b0cd9 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 b6964b429f5..d6af92cca0d 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<ast::ident>,
             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 7e84c08a1d0..44cf92ea1b9 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); }