Parse and typecheck by-value and by-ref arg specs

Add sprinkle && throughout the compiler to make it typecheck again.

Issue #1008
This commit is contained in:
Marijn Haverbeke 2011-10-06 12:26:12 +02:00
parent 4709038d64
commit f9fbd86f52
32 changed files with 230 additions and 197 deletions

View file

@ -251,10 +251,8 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] {
mutable ok: valid,
mutable copied: alt arg_t.mode {
ast::by_move. { copied }
ast::by_ref. {
i + 1u == by_ref ? not_allowed : not_copied
}
ast::by_mut_ref. { not_allowed }
_ { i + 1u == by_ref ? not_allowed : not_copied }
}}];
i += 1u;
}

View file

@ -76,7 +76,7 @@ fn map_expr(cx: ctx, ex: @expr) {
}
fn new_smallintmap_int_adapter<@V>() -> std::map::hashmap<int, V> {
let key_idx = fn (key: int) -> uint { key as uint };
let key_idx = fn (&&key: int) -> uint { key as uint };
let idx_key = fn (idx: uint) -> int { idx as int };
ret new_smallintmap_adapter(key_idx, idx_key);
}

View file

@ -11,7 +11,7 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) {
tcx.sess.abort_if_errors();
}
fn check_expr(tcx: ty::ctxt, ex: @expr, s: (), v: visit::vt<()>) {
fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) {
visit::visit_expr(ex, s, v);
alt ex.node { expr_alt(_, arms) { check_arms(tcx, arms); } _ { } }
}
@ -123,7 +123,7 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
}
}
fn check_local(tcx: ty::ctxt, loc: @local, s: (), v: visit::vt<()>) {
fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
visit::visit_local(loc, s, v);
if is_refutable(tcx, loc.node.pat) {
tcx.sess.span_err(loc.node.pat.span,

View file

@ -29,10 +29,10 @@ fn collect_freevars(def_map: resolve::def_map, walker: fn(visit::vt<int>)) ->
let seen = new_int_hash();
let refs = @mutable [];
fn ignore_item(_i: @ast::item, _depth: int, _v: visit::vt<int>) { }
fn ignore_item(_i: @ast::item, &&_depth: int, _v: visit::vt<int>) { }
let walk_expr =
lambda (expr: @ast::expr, depth: int, v: visit::vt<int>) {
lambda (expr: @ast::expr, &&depth: int, v: visit::vt<int>) {
alt expr.node {
ast::expr_fn(f) {
if f.proto == ast::proto_block ||

View file

@ -131,7 +131,7 @@ fn mk_err(cx: @ctx, span: syntax::codemap::span, msg: msg, name: str) {
});
}
fn visit_decl(cx: @ctx, d: @decl, e: (), v: visit::vt<()>) {
fn visit_decl(cx: @ctx, d: @decl, &&e: (), v: visit::vt<()>) {
visit::visit_decl(d, e, v);
alt d.node {
decl_local(locs) {
@ -148,7 +148,7 @@ fn visit_decl(cx: @ctx, d: @decl, e: (), v: visit::vt<()>) {
}
}
fn visit_expr(cx: @ctx, ex: @expr, e: (), v: visit::vt<()>) {
fn visit_expr(cx: @ctx, ex: @expr, &&e: (), v: visit::vt<()>) {
alt ex.node {
expr_call(f, args) { check_call(cx, f, args); }
expr_swap(lhs, rhs) {
@ -225,7 +225,7 @@ fn check_call(cx: @ctx, f: @expr, args: [@expr]) {
alt arg_t.mode {
by_mut_ref. { check_lval(cx, args[i], msg_mut_ref); }
by_move. { check_lval(cx, args[i], msg_move_out); }
by_ref. {}
_ {}
}
i += 1u;
}

View file

@ -1239,7 +1239,7 @@ fn mie_span(mie: mod_index_entry) -> span {
};
}
fn check_item(e: @env, i: @ast::item, x: (), v: vt<()>) {
fn check_item(e: @env, i: @ast::item, &&x: (), v: vt<()>) {
fn typaram_names(tps: [ast::ty_param]) -> [ident] {
let x: [ast::ident] = [];
for tp: ast::ty_param in tps { x += [tp.ident]; }
@ -1276,7 +1276,7 @@ fn check_pat(ch: checker, p: @ast::pat) {
}
}
fn check_arm(e: @env, a: ast::arm, x: (), v: vt<()>) {
fn check_arm(e: @env, a: ast::arm, &&x: (), v: vt<()>) {
visit::visit_arm(a, x, v);
let ch0 = checker(*e, "binding");
check_pat(ch0, a.pats[0]);
@ -1306,7 +1306,7 @@ fn check_arm(e: @env, a: ast::arm, x: (), v: vt<()>) {
}
}
fn check_block(e: @env, b: ast::blk, x: (), v: vt<()>) {
fn check_block(e: @env, b: ast::blk, &&x: (), v: vt<()>) {
visit::visit_block(b, x, v);
let values = checker(*e, "value");
let types = checker(*e, "type");
@ -1359,7 +1359,7 @@ fn check_fn(e: env, sp: span, f: ast::_fn) {
ensure_unique(e, sp, f.decl.inputs, arg_name, "argument");
}
fn check_expr(e: @env, ex: @ast::expr, x: (), v: vt<()>) {
fn check_expr(e: @env, ex: @ast::expr, &&x: (), v: vt<()>) {
alt ex.node {
ast::expr_rec(fields, _) {
fn field_name(f: ast::field) -> ident { ret f.node.ident; }
@ -1370,7 +1370,7 @@ fn check_expr(e: @env, ex: @ast::expr, x: (), v: vt<()>) {
visit::visit_expr(ex, x, v);
}
fn check_ty(e: @env, ty: @ast::ty, x: (), v: vt<()>) {
fn check_ty(e: @env, ty: @ast::ty, &&x: (), v: vt<()>) {
alt ty.node {
ast::ty_rec(fields) {
fn field_name(f: ast::ty_field) -> ident { ret f.node.ident; }

View file

@ -3699,7 +3699,7 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty0: TypeRef,
// be inspected. It's important for the value
// to have type lldestty0 (the callee's expected type).
val = llvm::LLVMGetUndef(lldestty0);
} else if arg.mode == ast::by_ref {
} else if arg.mode == ast::by_ref || arg.mode == ast::by_val {
let copied = false;
if !lv.is_mem && type_is_immediate(ccx, e_ty) {
val = do_spill_noroot(bcx, val);
@ -4439,9 +4439,9 @@ fn lval_to_dps(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
// latter group "immediates" and, in some circumstances when we know we have a
// pointer (or need one), perform load/store operations based on the
// immediate-ness of the type.
// FIXME simply call the version in ty.rs immediately
fn type_is_immediate(ccx: @crate_ctxt, t: ty::t) -> bool {
ret ty::type_is_scalar(ccx.tcx, t) || ty::type_is_boxed(ccx.tcx, t) ||
ty::type_is_unique_box(ccx.tcx, t) || ty::type_is_native(ccx.tcx, t);
ty::type_is_immediate(ccx.tcx, t)
}
fn do_spill(cx: @block_ctxt, v: ValueRef, t: ty::t) -> result {
@ -5144,7 +5144,11 @@ fn copy_args_to_allocas(fcx: @fn_ctxt, bcx: @block_ctxt, args: [ast::arg],
for aarg: ast::arg in args {
let arg_ty = arg_tys[arg_n].ty;
alt aarg.mode {
ast::by_ref. {
ast::by_move. {
add_clean(bcx, fcx.llargs.get(aarg.id), arg_ty);
}
ast::by_mut_ref. { }
_ {
let mutated =
!ignore_mut && fcx.lcx.ccx.mut_map.contains_key(aarg.id);
@ -5160,10 +5164,6 @@ fn copy_args_to_allocas(fcx: @fn_ctxt, bcx: @block_ctxt, args: [ast::arg],
add_clean(bcx, alloc, arg_ty);
}
}
ast::by_move. {
add_clean(bcx, fcx.llargs.get(aarg.id), arg_ty);
}
_ { }
}
arg_n += 1u;
}
@ -5790,7 +5790,7 @@ fn register_native_fn(ccx: @crate_ctxt, sp: span, path: [str], name: str,
}
fn convert_arg_to_i32(cx: @block_ctxt, v: ValueRef, t: ty::t,
mode: ty::mode) -> ValueRef {
if mode == ast::by_ref {
if mode == ast::by_ref || mode == ast::by_val {
if ty::type_is_integral(bcx_tcx(cx), t) {
// FIXME: would be nice to have a postcondition that says
// if a type is integral, then it has static size (#586)
@ -5845,7 +5845,7 @@ fn register_native_fn(ccx: @crate_ctxt, sp: span, path: [str], name: str,
let i = arg_n;
for arg: ty::arg in args {
let llarg = llvm::LLVMGetParam(fcx.llfn, i);
if arg.mode == ast::by_ref {
if arg.mode == ast::by_ref || arg.mode == ast::by_val {
llarg = load_if_immediate(bcx, llarg, arg.ty);
}
assert (llarg as int != 0);

View file

@ -41,7 +41,8 @@ fn trans_obj(cx: @local_ctxt, sp: span, ob: ast::_obj, ctor_id: ast::node_id,
// we're creating.
let fn_args: [ast::arg] = [];
for f: ast::obj_field in ob.fields {
fn_args += [{mode: ast::by_ref, ty: f.ty, ident: f.ident, id: f.id}];
fn_args += [{mode: ast::by_ref, ty: f.ty, ident: f.ident,
id: f.id}];
}
let fcx = new_fn_ctxt(cx, sp, llctor_decl);
@ -397,7 +398,7 @@ tag vtbl_mthd {
}
// Alphabetize ast::methods by ident. A helper for create_vtbl.
fn ast_mthd_lteq(a: @ast::method, b: @ast::method) -> bool {
fn ast_mthd_lteq(&&a: @ast::method, &&b: @ast::method) -> bool {
ret str::lteq(a.node.ident, b.node.ident);
}

View file

@ -99,10 +99,10 @@ fn find_pre_post_exprs(fcx: fn_ctxt, args: [@expr], id: node_id) {
fn get_pp(ccx: crate_ctxt, e: @expr) -> pre_and_post {
ret expr_pp(ccx, e);
}
let pps = vec::map::<@expr, pre_and_post>(bind get_pp(fcx.ccx, _), args);
let pps = vec::map_imm(bind get_pp(fcx.ccx, _), args);
set_pre_and_post(fcx.ccx, id, seq_preconds(fcx, pps),
seq_postconds(fcx, vec::map(get_post, pps)));
seq_postconds(fcx, vec::map_imm(get_post, pps)));
}
fn find_pre_post_loop(fcx: fn_ctxt, l: @local, index: @expr, body: blk,
@ -476,8 +476,8 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
}
let alt_pps = [];
for a: arm in alts { alt_pps += [do_an_alt(fcx, a)]; }
fn combine_pp(antec: pre_and_post, fcx: fn_ctxt, pp: pre_and_post,
next: pre_and_post) -> pre_and_post {
fn combine_pp(antec: pre_and_post, fcx: fn_ctxt, &&pp: pre_and_post,
&&next: pre_and_post) -> pre_and_post {
union(pp.precondition, seq_preconds(fcx, [antec, next]));
intersect(pp.postcondition, next.postcondition);
ret pp;
@ -694,7 +694,7 @@ fn find_pre_post_block(fcx: fn_ctxt, b: blk) {
*/
}
for s: @stmt in b.node.stmts { do_one_(fcx, s); }
fn do_inner_(fcx: fn_ctxt, e: @expr) { find_pre_post_expr(fcx, e); }
fn do_inner_(fcx: fn_ctxt, &&e: @expr) { find_pre_post_expr(fcx, e); }
let do_inner = bind do_inner_(fcx, _);
option::map::<@expr, ()>(do_inner, b.node.expr);

View file

@ -160,6 +160,7 @@ export type_is_native;
export type_is_nil;
export type_is_pod;
export type_is_scalar;
export type_is_immediate;
export type_is_sequence;
export type_is_signed;
export type_is_structural;
@ -916,6 +917,12 @@ pure fn type_is_scalar(cx: ctxt, ty: t) -> bool {
}
}
// FIXME maybe inline this for speed?
fn type_is_immediate(cx: ctxt, ty: t) -> bool {
ret type_is_scalar(cx, ty) || type_is_boxed(cx, ty) ||
type_is_unique_box(cx, ty) || type_is_native(cx, ty);
}
fn type_has_pointers(cx: ctxt, ty: t) -> bool {
alt cx.has_pointer_cache.find(ty) {
some(result) { ret result; }
@ -924,14 +931,8 @@ fn type_has_pointers(cx: ctxt, ty: t) -> bool {
let result = false;
alt struct(cx, ty) {
// scalar types
ty_nil. {
/* no-op */
}
ty_nil. {/* no-op */ }
ty_bot. {/* no-op */ }
ty_bool. {/* no-op */ }
ty_int. {/* no-op */ }
@ -1446,14 +1447,14 @@ fn hash_type_info(st: sty, cname_opt: option::t<str>) -> uint {
ret h;
}
fn hash_raw_ty(rt: @raw_t) -> uint { ret rt.hash; }
fn hash_raw_ty(&&rt: @raw_t) -> uint { ret rt.hash; }
fn hash_ty(typ: t) -> uint { ret typ; }
fn hash_ty(&&typ: t) -> uint { ret typ; }
// Type equality. This function is private to this module (and slow); external
// users should use `eq_ty()` instead.
fn eq_int(x: uint, y: uint) -> bool { ret x == y; }
fn eq_int(&&x: uint, &&y: uint) -> bool { ret x == y; }
fn arg_eq<T>(eq: fn(T, T) -> bool, a: @sp_constr_arg<T>, b: @sp_constr_arg<T>)
-> bool {
@ -1495,7 +1496,7 @@ fn constrs_eq(cs: [@constr], ds: [@constr]) -> bool {
// An expensive type equality function. This function is private to this
// module.
fn eq_raw_ty(a: @raw_t, b: @raw_t) -> bool {
fn eq_raw_ty(&&a: @raw_t, &&b: @raw_t) -> bool {
// Check hashes (fast path).
if a.hash != b.hash { ret false; }
@ -1518,7 +1519,7 @@ fn eq_raw_ty(a: @raw_t, b: @raw_t) -> bool {
// This is the equality function the public should use. It works as long as
// the types are interned.
fn eq_ty(a: t, b: t) -> bool { ret a == b; }
fn eq_ty(&&a: t, &&b: t) -> bool { ret a == b; }
// Type lookups
@ -1954,12 +1955,15 @@ mod unify {
let actual_input = actual_inputs[i];
// Unify the result modes.
let result_mode;
if expected_input.mode != actual_input.mode {
let result_mode = if expected_input.mode == ast::mode_infer {
actual_input.mode
} else if actual_input.mode == ast::mode_infer {
expected_input.mode
} else if expected_input.mode != actual_input.mode {
ret fn_common_res_err
(ures_err(terr_mode_mismatch(expected_input.mode,
actual_input.mode)));
} else { result_mode = expected_input.mode; }
} else { expected_input.mode };
let result = unify_step(cx, expected_input.ty, actual_input.ty);
alt result {
ures_ok(rty) { result_ins += [{mode: result_mode, ty: rty}]; }

View file

@ -224,7 +224,27 @@ fn type_is_scalar(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
// Parses the programmer's textual representation of a type into our internal
// notion of a type. `getter` is a function that returns the type
// corresponding to a definition ID:
fn default_arg_mode_for_ty(tcx: ty::ctxt, m: ast::mode,
ty: ty::t) -> ast::mode {
alt m {
ast::mode_infer. {
alt ty::struct(tcx, ty) {
ty::ty_var(_) { ast::mode_infer }
_ {
if ty::type_is_immediate(tcx, ty) { ast::by_val }
else { ast::by_ref }
}
}
}
_ { m }
}
}
fn ast_ty_to_ty(tcx: ty::ctxt, getter: ty_getter, ast_ty: @ast::ty) -> ty::t {
fn ast_arg_to_arg(tcx: ty::ctxt, getter: ty_getter, arg: ast::ty_arg)
-> {mode: ty::mode, ty: ty::t} {
let ty = ast_ty_to_ty(tcx, getter, arg.node.ty);
ret {mode: default_arg_mode_for_ty(tcx, arg.node.mode, ty), ty: ty};
}
alt tcx.ast_ty_to_ty_cache.find(ast_ty) {
some(some(ty)) { ret ty; }
some(none.) {
@ -237,10 +257,6 @@ fn ast_ty_to_ty(tcx: ty::ctxt, getter: ty_getter, ast_ty: @ast::ty) -> ty::t {
} /* go on */
tcx.ast_ty_to_ty_cache.insert(ast_ty, none::<ty::t>);
fn ast_arg_to_arg(tcx: ty::ctxt, getter: ty_getter, arg: ast::ty_arg) ->
{mode: ty::mode, ty: ty::t} {
ret {mode: arg.node.mode, ty: ast_ty_to_ty(tcx, getter, arg.node.ty)};
}
fn ast_mt_to_mt(tcx: ty::ctxt, getter: ty_getter, mt: ast::mt) -> ty::mt {
ret {ty: ast_ty_to_ty(tcx, getter, mt.ty), mut: mt.mut};
}
@ -295,7 +311,7 @@ fn ast_ty_to_ty(tcx: ty::ctxt, getter: ty_getter, ast_ty: @ast::ty) -> ty::t {
typ = ty::mk_ptr(tcx, ast_mt_to_mt(tcx, getter, mt));
}
ast::ty_tup(fields) {
let flds = vec::map(bind ast_ty_to_ty(tcx, getter, _), fields);
let flds = vec::map_imm(bind ast_ty_to_ty(tcx, getter, _), fields);
typ = ty::mk_tup(tcx, flds);
}
ast::ty_rec(fields) {
@ -550,9 +566,8 @@ mod collect {
ret tpt;
}
fn ty_of_arg(cx: @ctxt, a: ast::arg) -> ty::arg {
let f = bind getter(cx, _);
let tt = ast_ty_to_ty(cx.tcx, f, a.ty);
ret {mode: a.mode, ty: tt};
let ty = ast_ty_to_ty(cx.tcx, bind getter(cx, _), a.ty);
{mode: default_arg_mode_for_ty(cx.tcx, a.mode, ty), ty: ty}
}
fn ty_of_method(cx: @ctxt, m: @ast::method) -> ty::method {
let get = bind getter(cx, _);
@ -1216,7 +1231,7 @@ fn gather_locals(ccx: @crate_ctxt, f: ast::_fn, id: ast::node_id,
// Add explicitly-declared locals.
let visit_local =
lambda (local: @ast::local, e: (), v: visit::vt<()>) {
lambda (local: @ast::local, &&e: (), v: visit::vt<()>) {
let local_ty = ast_ty_to_ty_crate_infer(ccx, local.node.ty);
assign(local.node.id, ident_for_local(local), local_ty);
visit::visit_local(local, e, v);
@ -1224,7 +1239,7 @@ fn gather_locals(ccx: @crate_ctxt, f: ast::_fn, id: ast::node_id,
// Add pattern bindings.
let visit_pat =
lambda (p: @ast::pat, e: (), v: visit::vt<()>) {
lambda (p: @ast::pat, &&e: (), v: visit::vt<()>) {
alt p.node {
ast::pat_bind(ident) { assign(p.id, ident, none); }
_ {/* no-op */ }
@ -2022,16 +2037,16 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
write::ty_only_fixup(fcx, id, result_ty);
}
ast::expr_fn(f) {
let cx = @{tcx: tcx};
let convert = bind ast_ty_to_ty_crate_tyvar(fcx, _);
let ty_of_arg =
lambda (a: ast::arg) -> ty::arg {
let tt = ast_ty_to_ty_crate_tyvar(fcx, a.ty);
ret {mode: a.mode, ty: tt};
};
let fty =
collect::ty_of_fn_decl(cx, convert, ty_of_arg, f.decl, f.proto,
[], none).ty;
let ty_of_arg = lambda (a: ast::arg) -> ty::arg {
let tt = ast_ty_to_ty_crate_tyvar(fcx, a.ty);
ret {mode: default_arg_mode_for_ty(fcx.ccx.tcx, a.mode, tt),
ty: tt};
};
let cx = @{tcx: tcx};
let fty = collect::ty_of_fn_decl(cx, convert, ty_of_arg, f.decl,
f.proto, [], none).ty;
write::ty_only_fixup(fcx, id, fty);
// Unify the type of the function with the expected type before we
@ -2041,6 +2056,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
unify(fcx, expr.span, expected, fty);
check_fn(fcx.ccx, f, id, some(fcx));
if f.proto == ast::proto_block {
write::ty_only_fixup(fcx, id, expected);
}
}
ast::expr_block(b) {
// If this is an unchecked block, turn off purity-checking
@ -2301,7 +2319,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
// FIXME: These next three functions are largely ripped off from
// similar ones in collect::. Is there a better way to do this?
fn ty_of_arg(ccx: @crate_ctxt, a: ast::arg) -> ty::arg {
ret {mode: a.mode, ty: ast_ty_to_ty_crate(ccx, a.ty)};
let ty = ast_ty_to_ty_crate(ccx, a.ty);
ret {mode: default_arg_mode_for_ty(ccx.tcx, a.mode, ty), ty: ty};
}
fn ty_of_method(ccx: @crate_ctxt, m: @ast::method) -> ty::method {