From b843cf2117d8be1317047d898d586c159b33a6f7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Sep 2011 17:01:35 +0200 Subject: [PATCH] Generalize expression roots in alias analysis This allows calls-returning-a-reference to count as expression roots, making it possible to return the result of such a call by reference. Issue #918 --- src/comp/middle/alias.rs | 75 ++++++++++++++------------- src/test/run-pass/ret-by-reference.rs | 9 ++++ 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index 2c1cac9ec8d..360619b73e0 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -1,7 +1,7 @@ import syntax::{ast, ast_util}; import ast::{ident, fn_ident, node_id, def_id}; -import mut::{expr_root, mut_field, deref, field, index, unbox}; +import mut::{mut_field, deref, field, index, unbox}; import syntax::codemap::span; import syntax::visit; import visit::vt; @@ -89,7 +89,7 @@ fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt) { ast::expr_put(val) { alt val { some(ex) { - let root = expr_root(cx.tcx, ex, false); + let root = expr_root(*cx, ex, false); if mut_field(root.ds) { cx.tcx.sess.span_err(ex.span, "result of put must be" + @@ -177,38 +177,13 @@ fn add_bindings_for_let(cx: ctx, &bs: [binding], loc: @ast::local) { cx.tcx.sess.span_err (loc.span, "can not move into a by-reference binding"); } - let root = expr_root(cx.tcx, init.expr, false); - let outer_ds = *root.ds; + let root = expr_root(cx, init.expr, false); let root_var = path_def_id(cx, root.ex); - let is_temp = is_none(root_var); - if is_temp { - alt root.ex.node { - ast::expr_call(f, args) { - 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_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 mut { - outer_ds = [@{mut: true, kind: unbox, - outer_t: ty::expr_ty(cx.tcx, arg)}]; - } - outer_ds = *arg_root.ds + outer_ds; - } - } - } - } - _ {} - } - } - if is_temp { + if is_none(root_var) { cx.tcx.sess.span_err(loc.span, "a reference binding can't be \ rooted in a temporary"); } - for proot in *pattern_roots(cx.tcx, outer_ds, loc.node.pat) { + for proot in *pattern_roots(cx.tcx, *root.ds, loc.node.pat) { let bnd = mk_binding(cx, proot.id, proot.span, root_var, inner_mut(proot.ds)); // Don't implicitly copy explicit references @@ -252,7 +227,7 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] { let i = 0u; for arg_t: ty::arg in arg_ts { let arg = args[i]; - let root = expr_root(cx.tcx, arg, false); + let root = expr_root(cx, arg, false); if arg_t.mode == ast::by_mut_ref { alt path_def(cx, arg) { some(def) { @@ -340,11 +315,13 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] { 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 root = expr_root(cx, expr, false); let bad = none; let mut_field = mut_field(root.ds); alt path_def(cx, root.ex) { - none. { bad = some("a 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; @@ -400,7 +377,7 @@ fn check_ret_ref(cx: ctx, sc: scope, mut: bool, arg_node_id: node_id, fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope, v: vt) { v.visit_expr(input, sc, v); - let root = expr_root(cx.tcx, input, true); + let root = expr_root(cx, input, true); for a: ast::arm in arms { let new_bs = sc.bs; let root_var = path_def_id(cx, root.ex); @@ -448,7 +425,7 @@ fn check_for_each(cx: ctx, local: @ast::local, call: @ast::expr, fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk, sc: scope, v: vt) { v.visit_expr(seq, sc, v); - let root = expr_root(cx.tcx, seq, false); + let root = expr_root(cx, seq, false); // If this is a mutable vector, don't allow it to be touched. let seq_t = ty::expr_ty(cx.tcx, seq); @@ -695,6 +672,34 @@ fn pattern_roots(tcx: ty::ctxt, base: [deref], pat: @ast::pat) ret @set; } +// Wraps the expr_root in mut.rs to also handle roots that exist through +// return-by-reference +fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool) -> + {ex: @ast::expr, ds: @[deref]} { + let base_root = mut::expr_root(cx.tcx, ex, autoderef); + if is_none(path_def_id(cx, base_root.ex)) { + alt base_root.ex.node { + ast::expr_call(f, args) { + 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_root = expr_root(cx, arg, false); + ret {ex: arg_root.ex, + ds: @(*arg_root.ds + + (mut ? [@{mut: true, kind: unbox, + outer_t: ty::expr_ty(cx.tcx, arg)}] : []) + + *base_root.ds)}; + } + _ {} + } + } + _ {} + } + } + ret base_root; +} + fn inner_mut(ds: @[deref]) -> [ty::t] { for d: deref in *ds { if d.mut { ret [d.outer_t]; } } ret []; diff --git a/src/test/run-pass/ret-by-reference.rs b/src/test/run-pass/ret-by-reference.rs index 6f098160e6a..1c2a3b9e50c 100644 --- a/src/test/run-pass/ret-by-reference.rs +++ b/src/test/run-pass/ret-by-reference.rs @@ -10,6 +10,10 @@ fn get_mut(a: {mutable x: @int}, _b: int) -> &!0 @int { ret a.x; } +fn get_deep(a: {mutable y: {mutable x: @int}}) -> &!@int { + ret get_mut(a.y, 1); +} + fn main() { let x = some(@50); let &y = get(x); @@ -20,4 +24,9 @@ fn main() { let &box = get_mut(y, 4); assert *box == 50; assert *get_mut({mutable x: @70}, 5) == 70; + + let u = {mutable y: {mutable x: @10}}; + let &deep = get_deep(u); + assert *deep == 10; + assert *get_deep({mutable y: {mutable x: @11}}) + 2 == 13; }