Rollback return-by-reference

It's proving too inflexible, so I'm ripping out the extra complexity
in the hope that regions will, at some point, provide something
similar.

Closes #918
This commit is contained in:
Marijn Haverbeke 2011-11-23 10:56:10 +01:00
parent acbc4aa9f8
commit 9fb3719ded
23 changed files with 50 additions and 370 deletions

View File

@ -55,11 +55,6 @@ 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)) }
'&' | '^' {
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)) }
}
}

View File

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

View File

@ -28,10 +28,8 @@ type binding = @{node_id: node_id,
unsafe_tys: [unsafe_ty],
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_info: ret_info,
invalid: @mutable list<@invalid>};
fn mk_binding(cx: ctx, id: node_id, span: span, root_var: option::t<node_id>,
@ -67,7 +65,7 @@ fn check_crate(tcx: ty::ctxt, crate: @ast::crate) -> (copy_map, ref_map) {
visit_expr: bind visit_expr(cx, _, _, _),
visit_block: bind visit_block(cx, _, _, _)
with *visit::default_visitor::<scope>()};
let sc = {bs: [], ret_info: other, invalid: @mutable list::nil};
let sc = {bs: [], invalid: @mutable list::nil};
visit::visit_crate(*crate, sc, visit::mk_vt(v));
tcx.sess.abort_if_errors();
ret (cx.copy_map, cx.ref_map);
@ -84,24 +82,12 @@ fn visit_fn(cx: @ctx, f: ast::_fn, _tp: [ast::ty_param], sp: span,
}
}
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
err(*cx, option::get(f.body.node.expr).span,
"reference-returning functions may not return implicitly");
}
let ret_info = alt f.decl.cf {
ast::return_ref(mut, n_arg) {
by_ref(mut, f.decl.inputs[n_arg - 1u].id)
}
_ { other }
};
// Blocks need to obey any restrictions from the enclosing scope, and may
// be called multiple times.
if f.proto == ast::proto_block {
let sc = {ret_info: ret_info with sc};
check_loop(*cx, sc) {|| v.visit_block(f.body, sc, v);}
} else {
let sc = {bs: [], ret_info: ret_info, invalid: @mutable list::nil};
let sc = {bs: [], invalid: @mutable list::nil};
v.visit_block(f.body, sc, v);
}
}
@ -134,17 +120,6 @@ fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
ast::expr_assign(dest, src) | ast::expr_assign_op(_, dest, src) {
check_assign(cx, dest, src, sc, v);
}
ast::expr_ret(oexpr) {
if !is_none(oexpr) {
alt sc.ret_info {
by_ref(mut, arg_node_id) {
check_ret_ref(*cx, sc, mut, arg_node_id, option::get(oexpr));
}
_ {}
}
}
handled = false;
}
ast::expr_if(c, then, els) { check_if(c, then, els, sc, v); }
ast::expr_while(_, _) | ast::expr_do_while(_, _) {
check_loop(*cx, sc) {|| visit::visit_expr(ex, sc, v); }
@ -237,9 +212,6 @@ fn cant_copy(cx: ctx, b: binding) -> bool {
fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
-> [binding] {
let fty = ty::expr_ty(cx.tcx, f);
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 = [];
@ -265,7 +237,7 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
mutable copied: alt arg_t.mode {
ast::by_move. | ast::by_copy. { copied }
ast::by_mut_ref. { not_allowed }
_ { i + 1u == by_ref ? not_allowed : not_copied }
_ { not_copied }
}}];
i += 1u;
}
@ -338,69 +310,6 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
ret bindings;
}
fn check_ret_ref(cx: ctx, sc: scope, mut: bool, arg_node_id: node_id,
expr: @ast::expr) {
let root = expr_root(cx, expr, false);
let bad = none;
let mut_field = !is_none(root.mut);
alt path_def(cx, root.ex) {
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;
while true {
alt cx.tcx.items.find(cur_node) {
some(ast_map::node_arg(arg, _)) {
if arg.mode == ast::by_move {
bad = some("a move-mode parameter");
}
if arg.mode == ast::by_copy {
bad = some("a copy-mode parameter");
}
if cur_node != arg_node_id {
bad = some("the wrong parameter");
}
break;
}
_ {}
}
alt vec::find({|b| b.node_id == cur_node}, sc.bs) {
some(b) {
if vec::len(b.unsafe_tys) > 0u {
mut_field = true;
break;
}
if is_none(b.root_var) {
bad = some("a function-local value");
break;
}
if b.copied == copied {
bad = some("an implicitly copied reference");
break;
}
b.copied = not_allowed;
cur_node = option::get(b.root_var);
}
none. {
bad = some("a function-local value");
break;
}
}
}
}
_ { bad = some("a non-local value"); }
}
if mut_field && !mut { bad = some("a mutable field"); }
alt bad {
some(name) {
err(cx, expr.span, "can not return a reference to " + name);
}
_ {}
}
}
fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
v: vt<scope>) {
v.visit_expr(input, sc, v);
@ -733,22 +642,6 @@ fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool)
_ {}
}
}
ast::expr_call(f, args, _) {
let fty = 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 - 1u];
let arg_root = expr_root(cx, arg, false);
if mut {
let ret_ty = ty::expr_ty(cx.tcx, base_root.ex);
unsafe_ty = some(mut_contains(ret_ty));
}
if !is_none(arg_root.mut) { unsafe_ty = arg_root.mut; }
ret {ex: arg_root.ex, mut: unsafe_ty};
}
_ {}
}
}
_ {}
}
ret {ex: base_root.ex, mut: unsafe_ty};

View File

@ -30,19 +30,16 @@ type rval_map = std::map::hashmap<node_id, ()>;
type ctx = {tcx: ty::ctxt,
rval_map: rval_map,
last_uses: last_use::last_uses,
mutable ret_by_ref: bool};
last_uses: last_use::last_uses};
fn check_crate(tcx: ty::ctxt, last_uses: last_use::last_uses,
crate: @crate) -> rval_map {
let ctx = {tcx: tcx,
rval_map: std::map::new_int_hash(),
last_uses: last_uses,
mutable ret_by_ref: false};
last_uses: last_uses};
let visit = visit::mk_vt(@{
visit_expr: check_expr,
visit_stmt: check_stmt,
visit_fn: visit_fn
visit_stmt: check_stmt
with *visit::default_visitor()
});
visit::visit_crate(*crate, ctx, visit);
@ -55,7 +52,7 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
expr_assign(_, ex) | expr_assign_op(_, _, ex) |
expr_block({node: {expr: some(ex), _}, _}) |
expr_unary(box(_), ex) | expr_unary(uniq(_), ex) { maybe_copy(cx, ex); }
expr_ret(some(ex)) { if !cx.ret_by_ref { maybe_copy(cx, ex); } }
expr_ret(some(ex)) { maybe_copy(cx, ex); }
expr_copy(expr) { check_copy_ex(cx, expr, false); }
// Vector add copies.
expr_binary(add., ls, rs) { maybe_copy(cx, ls); maybe_copy(cx, rs); }
@ -138,14 +135,6 @@ fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) {
visit::visit_stmt(stmt, cx, v);
}
fn visit_fn(f: _fn, tps: [ty_param], sp: span, ident: fn_ident,
id: node_id, cx: ctx, v: visit::vt<ctx>) {
let old_ret = cx.ret_by_ref;
cx.ret_by_ref = ast_util::ret_by_ref(f.decl.cf);
visit::visit_fn(f, tps, sp, ident, id, cx, v);
cx.ret_by_ref = old_ret;
}
fn maybe_copy(cx: ctx, ex: @expr) {
check_copy_ex(cx, ex, true);
}

View File

@ -80,14 +80,14 @@ fn type_of_explicit_args(cx: @crate_ctxt, sp: span, inputs: [ty::arg]) ->
// - new_fn_ctxt
// - trans_args
fn type_of_fn(cx: @crate_ctxt, sp: span,
is_method: bool, ret_ref: bool, inputs: [ty::arg],
is_method: bool, inputs: [ty::arg],
output: ty::t, ty_param_count: uint)
: non_ty_var(cx, output) -> TypeRef {
let atys: [TypeRef] = [];
// Arg 0: Output pointer.
let out_ty = T_ptr(type_of_inner(cx, sp, output));
atys += [ret_ref ? T_ptr(out_ty) : out_ty];
atys += [out_ty];
// Arg 1: Env (closure-bindings / self-obj)
if is_method {
@ -108,13 +108,12 @@ fn type_of_fn(cx: @crate_ctxt, sp: span,
fn type_of_fn_from_ty(cx: @crate_ctxt, sp: span, fty: ty::t,
ty_param_count: uint)
: returns_non_ty_var(cx, fty) -> TypeRef {
let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(cx.tcx, fty));
// FIXME: Check should be unnecessary, b/c it's implied
// by returns_non_ty_var(t). Make that a postcondition
// (see Issue #586)
let ret_ty = ty::ty_fn_ret(cx.tcx, fty);
check non_ty_var(cx, ret_ty);
ret type_of_fn(cx, sp, false, by_ref, ty::ty_fn_args(cx.tcx, fty),
ret type_of_fn(cx, sp, false, ty::ty_fn_args(cx.tcx, fty),
ret_ty, ty_param_count);
}
@ -3046,11 +3045,10 @@ fn trans_object_field_inner(bcx: @block_ctxt, o: ValueRef,
let v = GEPi(bcx, vtbl, [0, ix as int]);
let fn_ty: ty::t = ty::method_ty_to_fn_ty(tcx, mths[ix]);
let ret_ty = ty::ty_fn_ret(tcx, fn_ty);
let ret_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(tcx, fn_ty));
// FIXME: constrain ty_obj?
check non_ty_var(ccx, ret_ty);
let ll_fn_ty = type_of_fn(ccx, bcx.sp, true, ret_ref,
let ll_fn_ty = type_of_fn(ccx, bcx.sp, true,
ty::ty_fn_args(tcx, fn_ty), ret_ty, 0u);
v = Load(bcx, PointerCast(bcx, v, T_ptr(T_ptr(ll_fn_ty))));
ret {bcx: bcx, mthptr: v, objptr: o};
@ -3656,16 +3654,14 @@ 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, outer_cx: @block_ctxt, llenv: ValueRef,
fn trans_args(cx: @block_ctxt, llenv: ValueRef,
gen: option::t<generic_info>, es: [@ast::expr], fn_ty: ty::t,
dest: dest)
-> {bcx: @block_ctxt,
outer_cx: @block_ctxt,
args: [ValueRef],
retslot: ValueRef,
to_zero: [{v: ValueRef, t: ty::t}],
to_revoke: [{v: ValueRef, t: ty::t}],
ret_ref: bool} {
to_revoke: [{v: ValueRef, t: ty::t}]} {
let args: [ty::arg] = ty::ty_fn_args(bcx_tcx(cx), fn_ty);
let llargs: [ValueRef] = [];
@ -3676,8 +3672,6 @@ fn trans_args(cx: @block_ctxt, outer_cx: @block_ctxt, llenv: ValueRef,
let ccx = bcx_ccx(cx);
let tcx = ccx.tcx;
let bcx = cx;
let ret_style = ty::ty_fn_ret_style(tcx, fn_ty);
let ret_ref = ast_util::ret_by_ref(ret_style);
let retty = ty::ty_fn_ret(tcx, fn_ty), full_retty = retty;
alt gen {
@ -3691,18 +3685,14 @@ fn trans_args(cx: @block_ctxt, outer_cx: @block_ctxt, llenv: ValueRef,
}
// Arg 0: Output pointer.
let llretty = type_of_or_i8(bcx, full_retty);
let llretslot = if ret_ref {
alloca(cx, T_ptr(llretty))
} else {
alt dest {
ignore. {
if ty::type_is_nil(tcx, retty) {
llvm::LLVMGetUndef(T_ptr(llretty))
} else { alloca(cx, llretty) }
}
save_in(dst) { dst }
by_val(_) { alloca(cx, llretty) }
}
let llretslot = alt dest {
ignore. {
if ty::type_is_nil(tcx, retty) {
llvm::LLVMGetUndef(T_ptr(llretty))
} else { alloca(cx, llretty) }
}
save_in(dst) { dst }
by_val(_) { alloca(cx, llretty) }
};
if ty::type_contains_params(tcx, retty) {
@ -3713,7 +3703,6 @@ fn trans_args(cx: @block_ctxt, outer_cx: @block_ctxt, llenv: ValueRef,
// view, for the sake of making a type-compatible call.
check non_ty_var(ccx, retty);
let llretty = T_ptr(type_of_inner(ccx, bcx.sp, retty));
if ret_ref { llretty = T_ptr(llretty); }
llargs += [PointerCast(cx, llretslot, llretty)];
} else { llargs += [llretslot]; }
@ -3729,25 +3718,19 @@ fn trans_args(cx: @block_ctxt, outer_cx: @block_ctxt, llenv: ValueRef,
// This will be needed if this is a generic call, because the callee has
// to cast her view of the arguments to the caller's view.
let arg_tys = type_of_explicit_args(ccx, cx.sp, args);
let i = 0u, outer_cx = outer_cx;
let i = 0u;
for e: @ast::expr in es {
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; }
let r = trans_arg_expr(bcx, args[i], arg_tys[i], to_zero, to_revoke,
e);
bcx = r.bcx;
llargs += [r.val];
i += 1u;
}
ret {bcx: bcx,
outer_cx: outer_cx,
args: llargs,
retslot: llretslot,
to_zero: to_zero,
to_revoke: to_revoke,
ret_ref: ret_ref};
to_revoke: to_revoke};
}
fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
@ -3764,6 +3747,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
//NDM }
let cx = new_scope_block_ctxt(in_cx, "call");
Br(in_cx, cx.llbb);
let f_res = trans_callee(cx, f);
let bcx = f_res.bcx;
@ -3789,8 +3773,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
let ret_ty = ty::node_id_to_type(tcx, id);
let args_res =
trans_args(bcx, in_cx, llenv, f_res.generic, args, fn_expr_ty, dest);
Br(args_res.outer_cx, cx.llbb);
trans_args(bcx, llenv, f_res.generic, args, fn_expr_ty, dest);
bcx = args_res.bcx;
let llargs = args_res.args;
let llretslot = args_res.retslot;
@ -3803,8 +3786,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
args_res.to_revoke);
alt dest {
ignore. {
if llvm::LLVMIsUndef(llretslot) != lib::llvm::True &&
!args_res.ret_ref {
if llvm::LLVMIsUndef(llretslot) != lib::llvm::True {
bcx = drop_ty(bcx, llretslot, ret_ty);
}
}
@ -4445,16 +4427,7 @@ fn trans_cont(sp: span, cx: @block_ctxt) -> @block_ctxt {
fn trans_ret(bcx: @block_ctxt, e: option::t<@ast::expr>) -> @block_ctxt {
let cleanup_cx = bcx, bcx = bcx;
alt e {
some(x) {
if ast_util::ret_by_ref(bcx.fcx.ret_style) {
let {bcx: cx, val, kind} = trans_lval(bcx, x);
assert kind == owned;
Store(cx, val, bcx.fcx.llretptr);
bcx = cx;
} else {
bcx = trans_expr_save_in(bcx, x, bcx.fcx.llretptr);
}
}
some(x) { bcx = trans_expr_save_in(bcx, x, bcx.fcx.llretptr); }
_ {}
}
// run all cleanups and back out.
@ -5604,7 +5577,7 @@ fn create_main_wrapper(ccx: @crate_ctxt, sp: span, main_llfn: ValueRef,
let nt = ty::mk_nil(ccx.tcx);
check non_ty_var(ccx, nt);
let llfty = type_of_fn(ccx, sp, false, false, [vecarg_ty], nt, 0u);
let llfty = type_of_fn(ccx, sp, false, [vecarg_ty], nt, 0u);
let llfdecl = decl_fn(ccx.llmod, "_rust_main",
lib::llvm::LLVMCCallConv, llfty);
@ -5700,7 +5673,7 @@ fn native_fn_wrapper_type(cx: @crate_ctxt, sp: span, ty_param_count: uint,
alt ty::struct(cx.tcx, x) {
ty::ty_native_fn(args, out) {
check non_ty_var(cx, out);
ret type_of_fn(cx, sp, false, false, args, out, ty_param_count);
ret type_of_fn(cx, sp, false, args, out, ty_param_count);
}
}
}

View File

@ -321,7 +321,7 @@ fn get_res_dtor(ccx: @crate_ctxt, sp: span, did: ast::def_id, inner_t: ty::t)
let nil_res = ty::mk_nil(ccx.tcx);
// FIXME: Silly check -- mk_nil should have a postcondition
check non_ty_var(ccx, nil_res);
let f_t = type_of_fn(ccx, sp, false, false,
let f_t = type_of_fn(ccx, sp, false,
[{mode: ast::by_ref, ty: inner_t}],
nil_res, params);
ret trans::get_extern_const(ccx.externs, ccx.llmod,
@ -430,14 +430,14 @@ fn find_scope_cx(cx: @block_ctxt) -> @block_ctxt {
// Accessors
// TODO: When we have overloading, simplify these names!
pure fn bcx_tcx(bcx: @block_ctxt) -> &ty::ctxt { ret bcx.fcx.lcx.ccx.tcx; }
pure fn bcx_ccx(bcx: @block_ctxt) -> &@crate_ctxt { ret bcx.fcx.lcx.ccx; }
pure fn bcx_lcx(bcx: @block_ctxt) -> &@local_ctxt { ret bcx.fcx.lcx; }
pure fn bcx_fcx(bcx: @block_ctxt) -> &@fn_ctxt { ret bcx.fcx; }
pure fn fcx_ccx(fcx: @fn_ctxt) -> &@crate_ctxt { ret fcx.lcx.ccx; }
pure fn fcx_tcx(fcx: @fn_ctxt) -> &ty::ctxt { ret fcx.lcx.ccx.tcx; }
pure fn lcx_ccx(lcx: @local_ctxt) -> &@crate_ctxt { ret lcx.ccx; }
pure fn ccx_tcx(ccx: @crate_ctxt) -> &ty::ctxt { ret ccx.tcx; }
pure fn bcx_tcx(bcx: @block_ctxt) -> ty::ctxt { ret bcx.fcx.lcx.ccx.tcx; }
pure fn bcx_ccx(bcx: @block_ctxt) -> @crate_ctxt { ret bcx.fcx.lcx.ccx; }
pure fn bcx_lcx(bcx: @block_ctxt) -> @local_ctxt { ret bcx.fcx.lcx; }
pure fn bcx_fcx(bcx: @block_ctxt) -> @fn_ctxt { ret bcx.fcx; }
pure fn fcx_ccx(fcx: @fn_ctxt) -> @crate_ctxt { ret fcx.lcx.ccx; }
pure fn fcx_tcx(fcx: @fn_ctxt) -> ty::ctxt { ret fcx.lcx.ccx.tcx; }
pure fn lcx_ccx(lcx: @local_ctxt) -> @crate_ctxt { ret lcx.ccx; }
pure fn ccx_tcx(ccx: @crate_ctxt) -> ty::ctxt { ret ccx.tcx; }
// LLVM type constructors.
fn T_void() -> TypeRef {

View File

@ -879,8 +879,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(_, inputs, output, rs, _) {
check non_ty_var(ccx, output);
llfnty = type_of_fn(ccx, m.span, true,
ast_util::ret_by_ref(rs), inputs, output,
llfnty = type_of_fn(ccx, m.span, true, inputs, output,
vec::len(ty_params));
}
}
@ -933,8 +932,7 @@ fn type_of_meth(ccx: @crate_ctxt, sp: span, m: @ty::method,
tps: [ast::ty_param]) -> TypeRef {
let out_ty = m.output;
check non_ty_var(ccx, out_ty);
type_of_fn(ccx, sp, true, ast_util::ret_by_ref(m.cf),
m.inputs, out_ty, vec::len(tps))
type_of_fn(ccx, sp, true, m.inputs, out_ty, vec::len(tps))
}
//

View File

@ -1670,10 +1670,6 @@ fn expr_is_lval(tcx: ty::ctxt, e: @ast::expr) -> bool {
ty_rec(_) { true }
}
}
ast::expr_call(f, _, _) {
let fty = expr_ty(tcx, f);
ast_util::ret_by_ref(ty_fn_ret_style(tcx, fty))
}
_ { false }
}
}
@ -2623,10 +2619,6 @@ 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(mut, arg) {
#fmt("return-by-%sreference on arg %u",
mut ? "mutable-" : "", arg)
}
}
}
ret to_str(actual) + " function found where " + to_str(expect) +

View File

@ -397,7 +397,6 @@ 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, uint);
}
type _fn = {decl: fn_decl, proto: proto, body: blk};

View File

@ -223,13 +223,6 @@ fn ternary_to_if(e: @expr) -> @expr {
}
}
fn ret_by_ref(style: ret_style) -> bool {
alt style {
return_ref(_, _) { true }
_ { false }
}
}
fn ty_param_kind(tp: ty_param) -> kind { tp.kind }
fn compare_lit(a: @lit, b: @lit) -> int {

View File

@ -295,7 +295,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, vec::len(inputs.node));
let (ret_style, ret_ty) = parse_ret_ty(p);
ret ast::ty_fn(proto, inputs.node, ret_ty, ret_style, constrs);
}
@ -439,34 +439,12 @@ fn parse_ty_postfix(orig_t: ast::ty_, p: parser, colons_before_params: bool)
}
}
fn parse_ret_ty(p: parser, n_args: uint) -> (ast::ret_style, @ast::ty) {
fn parse_ret_ty(p: parser) -> (ast::ret_style, @ast::ty) {
ret if eat(p, token::RARROW) {
let lo = p.get_lo_pos();
if eat(p, token::NOT) {
(ast::noreturn, @spanned(lo, p.get_last_hi_pos(), ast::ty_bot))
} else {
let style = ast::return_val;
if eat(p, token::BINOP(token::AND)) {
if n_args == 0u {
p.fatal("can not return reference from argument-less fn");
}
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 {
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))
}
} else { (ast::return_val, parse_ty(p, false)) }
} else {
let pos = p.get_lo_pos();
(ast::return_val, @spanned(pos, pos, ast::ty_nil))
@ -1791,7 +1769,7 @@ fn parse_fn_decl(p: parser, purity: ast::purity, il: ast::inlineness) ->
p.bump();
constrs = parse_constrs({|x| parse_ty_constr(inputs.node, x) }, p);
}
let (ret_style, ret_ty) = parse_ret_ty(p, vec::len(inputs.node));
let (ret_style, ret_ty) = parse_ret_ty(p);
ret {inputs: inputs.node,
output: ret_ty,
purity: purity,

View File

@ -1130,15 +1130,6 @@ 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, "->");
alt decl.cf {
ast::return_ref(mut, arg) {
word(s.s, mut ? "&!" : "&");
if vec::len(decl.inputs) > 1u {
word_space(s, std::uint::str(arg));
}
}
_ {}
}
print_type(s, decl.output);
}
}
@ -1336,18 +1327,8 @@ fn print_ty_fn(s: ps, proto: ast::proto, id: option::t<ast::ident>,
space_if_not_bol(s);
ibox(s, indent_unit);
word_space(s, "->");
if cf == ast::noreturn {
word_nbsp(s, "!");
} else {
alt cf {
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);
}
if cf == ast::noreturn { word_nbsp(s, "!"); }
else { print_type(s, output); }
end(s);
}
word(s.s, ast_ty_fn_constrs_str(constrs));

View File

@ -62,11 +62,6 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
s += " -> ";
alt cf {
ast::noreturn. { s += "!"; }
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); }
}
}

View File

@ -30,7 +30,7 @@ Failure:
Fails if the value equals `none`.
*/
fn get<T>(opt: t<T>) -> &T {
fn get<copy T>(opt: t<T>) -> T {
alt opt { some(x) { ret x; } none. { fail "option none"; } }
}

View File

@ -1,9 +0,0 @@
// error-pattern:a reference binding can't be rooted in a temporary
fn f(a: {x: int}) -> &int {
ret a.x;
}
fn main() {
let &_a = f({x: 4});
}

View File

@ -1,8 +0,0 @@
// error-pattern:can not return a reference to a function-local value
fn f(a: {mutable x: int}) -> &int {
let x = {y: 4};
ret x.y;
}
fn main() {}

View File

@ -1,7 +0,0 @@
// error-pattern:can not return a reference to a mutable field
fn f(a: {mutable x: int}) -> &int {
ret a.x;
}
fn main() {}

View File

@ -1,12 +0,0 @@
// error-pattern:taking the value of x will invalidate reference a
fn f(a: {mutable x: int}) -> &!int {
ret a.x;
}
fn main() {
let x = {mutable x: 4};
let &a = f(x);
x;
a;
}

View File

@ -1,12 +0,0 @@
// error-pattern:overwriting x will invalidate reference a
fn f(a: {x: {mutable x: int}}) -> &{mutable x: int} {
ret a.x;
}
fn main() {
let x = {x: {mutable x: 4}};
let &a = f(x);
x = {x: {mutable x: 5}};
a;
}

View File

@ -1,7 +0,0 @@
// error-pattern:must specify referenced parameter
fn f(a: int, b: int) -> &int {
ret a;
}
fn main() {}

View File

@ -1,7 +0,0 @@
// error-pattern:can not return a reference to a temporary
fn f(a: int) -> &int {
ret 10;
}
fn main() {}

View File

@ -1,7 +0,0 @@
// error-pattern:can not return a reference to the wrong parameter
fn f(a: int, b: int) -> &2 int {
ret a;
}
fn main() {}

View File

@ -1,32 +0,0 @@
tag option<T> { some(T); none; }
fn get<T>(opt: option<T>) -> &T {
alt opt {
some(x) { ret x; }
}
}
fn get_mut(a: {mutable x: @int}, _b: int) -> &!1 @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);
assert *y == 50;
assert get(some(10)) == 10;
let y = {mutable x: @50};
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;
}