Add syntax and representation for return-by-mutably-rooted-ref

This will be used in the near future to decide what can safely
be done with the returned reference.

Issue #918
This commit is contained in:
Marijn Haverbeke 2011-09-14 17:18:48 +02:00
parent 1cabe37155
commit 93de2f0b74
11 changed files with 55 additions and 34 deletions

View File

@ -21,8 +21,6 @@ type str_def = fn(str) -> ast::def_id;
type pstate =
{data: @[u8], crate: int, mutable pos: uint, len: uint, tcx: ty::ctxt};
tag ty_or_bang { a_ty(ty::t); a_bang; }
fn peek(st: @pstate) -> u8 { ret st.data[st.pos]; }
fn next(st: @pstate) -> u8 {
@ -54,10 +52,12 @@ fn parse_ty_data(data: @[u8], crate_num: int, pos: uint, len: uint,
ret result;
}
fn parse_ty_or_bang(st: @pstate, sd: str_def) -> ty_or_bang {
fn parse_ret_ty(st: @pstate, sd: str_def) -> (ast::ret_style, ty::t) {
alt peek(st) as char {
'!' { next(st); ret a_bang; }
_ { ret a_ty(parse_ty(st, sd)); }
'!' { 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)) }
_ { (ast::return_val, parse_ty(st, sd)) }
}
}
@ -387,12 +387,8 @@ fn parse_ty_fn(st: @pstate, sd: str_def) ->
}
st.pos += 1u; // eat the ']'
let cs = parse_constrs(st, sd);
alt parse_ty_or_bang(st, sd) {
a_bang. {
ret {args: inputs, ty: ty::mk_bot(st.tcx), cf: ast::noreturn, cs: cs};
}
a_ty(t) { ret {args: inputs, ty: t, cf: ast::return_val, cs: cs}; }
}
let (ret_style, ret_ty) = parse_ret_ty(st, sd);
ret {args: inputs, ty: ret_ty, cf: ret_style, cs: cs};
}

View File

@ -220,6 +220,10 @@ fn enc_ty_fn(w: io::writer, cx: @ctxt, args: [ty::arg], out: ty::t,
}
alt cf {
noreturn. { w.write_char('!'); }
return_ref(mut) {
w.write_char(mut ? '^' : '&');
enc_ty(w, cx, out);
}
_ { enc_ty(w, cx, out); }
}
}

View File

@ -66,7 +66,7 @@ fn visit_fn(cx: @ctx, f: ast::_fn, _tp: [ast::ty_param], _sp: span,
// Non capturing functions start out fresh.
_ { [] }
};
if f.decl.cf == ast::return_ref && !is_none(f.body.node.expr) {
if ast_util::ret_by_ref(f.decl.cf) && !is_none(f.body.node.expr) {
// FIXME this will be easier to lift once have DPS
cx.tcx.sess.span_err(option::get(f.body.node.expr).span,
"reference-returning functions may not " +
@ -118,8 +118,13 @@ fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
check_assign(cx, dest, src, sc, v);
}
ast::expr_ret(oexpr) {
if sc.ret_style == ast::return_ref && !is_none(oexpr) {
check_ret_ref(*cx, sc, option::get(oexpr));
if !is_none(oexpr) {
alt sc.ret_style {
ast::return_ref(mut) {
check_ret_ref(*cx, sc, mut, option::get(oexpr));
}
_ {}
}
}
handled = false;
}
@ -176,7 +181,7 @@ 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 = ty::ty_fn_ret_style(cx.tcx, fty) == ast::return_ref;
let ret_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(cx.tcx, fty));
let arg_ts = ty::ty_fn_args(cx.tcx, fty);
let mut_roots: [{arg: uint, node: node_id}] = [];
let bindings = [];
@ -266,7 +271,7 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] {
ret bindings;
}
fn check_ret_ref(cx: ctx, sc: scope, expr: @ast::expr) {
fn check_ret_ref(cx: ctx, sc: scope, mut: bool, expr: @ast::expr) {
let root = expr_root(cx.tcx, expr, false);
let bad = none;
let mut_field = mut_field(root.ds);
@ -312,7 +317,7 @@ fn check_ret_ref(cx: ctx, sc: scope, expr: @ast::expr) {
// FIXME allow references to constants and static items?
_ { bad = some("non-local value"); }
}
if mut_field { bad = some("mutable field"); }
if mut_field && !mut { bad = some("mutable field"); }
alt bad {
some(name) {
cx.tcx.sess.span_err(expr.span, "can not return a reference " +

View File

@ -101,7 +101,7 @@ fn type_of_fn(cx: @crate_ctxt, sp: span, proto: ast::proto,
// Given a function type and a count of ty params, construct an llvm type
fn type_of_fn_from_ty(cx: @crate_ctxt, sp: span, fty: ty::t,
ty_param_count: uint) -> TypeRef {
let by_ref = ty::ty_fn_ret_style(cx.tcx, fty) == ast::return_ref;
let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(cx.tcx, fty));
ret type_of_fn(cx, sp, ty::ty_fn_proto(cx.tcx, fty),
false, by_ref, ty::ty_fn_args(cx.tcx, fty),
ty::ty_fn_ret(cx.tcx, fty), ty_param_count);
@ -2969,7 +2969,7 @@ fn trans_field(cx: @block_ctxt, sp: span, v: ValueRef, t0: ty::t,
let v = GEP(r.bcx, vtbl, [C_int(0), C_int(ix as int)]);
let tcx = bcx_tcx(cx);
let fn_ty: ty::t = ty::method_ty_to_fn_ty(tcx, methods[ix]);
let ret_ref = ty::ty_fn_ret_style(tcx, fn_ty) == ast::return_ref;
let ret_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(tcx, fn_ty));
let ll_fn_ty =
type_of_fn(bcx_ccx(cx), sp, ty::ty_fn_proto(tcx, fn_ty),
true, ret_ref, ty::ty_fn_args(tcx, fn_ty),
@ -3532,7 +3532,7 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, gen: option::t<generic_info>,
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;
let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(tcx, fn_ty));
// Arg 0: Output pointer.
// FIXME: test case looks like
@ -3629,7 +3629,8 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
// with trans_call.
let fn_expr_ty = ty::expr_ty(bcx_tcx(in_cx), f);
let fn_ty = ty::type_autoderef(bcx_tcx(in_cx), fn_expr_ty);
let by_ref = ty::ty_fn_ret_style(bcx_tcx(in_cx), fn_ty) == ast::return_ref;
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.
@ -4391,7 +4392,7 @@ fn trans_ret(cx: @block_ctxt, e: option::t<@ast::expr>) -> result {
let t = ty::expr_ty(bcx_tcx(cx), x);
let lv = trans_lval(cx, x);
bcx = lv.res.bcx;
if cx.fcx.ret_style == ast::return_ref {
if ast_util::ret_by_ref(cx.fcx.ret_style) {
assert lv.is_mem;
Store(bcx, lv.res.val, cx.fcx.llretptr);
} else {
@ -5364,7 +5365,7 @@ fn decl_fn_and_pair_full(ccx: @crate_ctxt, sp: span, path: [str], _flav: str,
alt ty::struct(ccx.tcx, node_type) {
ty::ty_fn(proto, inputs, output, rs, _) {
llfty = type_of_fn(ccx, sp, proto, false,
rs == ast::return_ref, inputs, output,
ast_util::ret_by_ref(rs), inputs, output,
vec::len(ty_params));
}
_ { ccx.sess.bug("decl_fn_and_pair(): fn item doesn't have fn type!"); }

View File

@ -882,7 +882,7 @@ fn process_normal_mthd(cx: @local_ctxt, m: @ast::method, self_ty: ty::t,
alt ty::struct(cx.ccx.tcx, node_id_type(cx.ccx, m.node.id)) {
ty::ty_fn(proto, inputs, output, rs, _) {
llfnty = type_of_fn(cx.ccx, m.span, proto, true,
rs == ast::return_ref, inputs, output,
ast_util::ret_by_ref(rs), inputs, output,
vec::len(ty_params));
}
}
@ -933,7 +933,7 @@ fn populate_self_stack(bcx: @block_ctxt, self_stack: ValueRef,
fn type_of_meth(ccx: @crate_ctxt, sp: span, m: @ty::method,
tps: [ast::ty_param]) -> TypeRef {
type_of_fn(ccx, sp, m.proto, true, m.cf == ast::return_ref,
type_of_fn(ccx, sp, m.proto, true, ast_util::ret_by_ref(m.cf),
m.inputs, m.output, vec::len(tps))
}

View File

@ -2471,7 +2471,7 @@ 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(_) { "return-by-reference" }
}
}
ret to_str(actual) + " function found where " + to_str(expect) +

View File

@ -381,7 +381,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;
return_ref(bool);
}
type _fn = {decl: fn_decl, proto: proto, body: blk};

View File

@ -213,6 +213,13 @@ fn ternary_to_if(e: @expr) -> @expr {
}
}
fn ret_by_ref(style: ret_style) -> bool {
alt style {
return_ref(_) { true }
_ { false }
}
}
// Local Variables:
// mode: rust
// fill-column: 78;

View File

@ -445,7 +445,7 @@ 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;
style = ast::return_ref(eat(p, token::NOT));
};
(style, parse_ty(p, false))
}

View File

@ -1224,7 +1224,10 @@ fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl, constrs: [@ast::constr]) {
if decl.output.node != ast::ty_nil {
space_if_not_bol(s);
word_space(s, "->");
if decl.cf == ast::return_ref { word(s.s, "&"); }
alt decl.cf {
ast::return_ref(mut) { word(s.s, mut ? "&!" : "&"); }
_ {}
}
print_type(s, decl.output);
}
}
@ -1423,7 +1426,10 @@ fn print_ty_fn(s: ps, proto: ast::proto, id: option::t<ast::ident>,
if cf == ast::noreturn {
word_nbsp(s, "!")
} else {
if cf == ast::return_ref { word(s.s, "&"); }
alt cf {
ast::return_ref(mut) { word(s.s, mut ? "&!" : "&"); }
_ {}
}
print_type(s, output);
}
end(s);

View File

@ -59,11 +59,13 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
s += ")";
if struct(cx, output) != ty_nil {
s += " -> ";
if cf == ast::noreturn {
s += "!";
} else {
if cf == ast::return_ref { s += "&"; }
alt cf {
ast::noreturn. { s += "!"; }
ast::return_ref(mut) {
s += mut ? "&!" : "&";
s += ty_to_str(cx, output);
}
ast::return_val. { s += ty_to_str(cx, output); }
}
}
s += constrs_str(constrs);