about summary refs log tree commit diff
path: root/src/comp
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-02-02 16:50:17 -0800
committerNiko Matsakis <niko@alum.mit.edu>2012-02-05 09:12:44 -0800
commit77b06d24cd76bf808138f1f7df4dcff40260ff38 (patch)
tree1830c6000f2c4a02be320e088389979cefb1414f /src/comp
parent5163606d060ccb2c6462d34f590e2a1f30ce4a1f (diff)
downloadrust-77b06d24cd76bf808138f1f7df4dcff40260ff38.tar.gz
rust-77b06d24cd76bf808138f1f7df4dcff40260ff38.zip
infer modes rather than overwriting with expected ty
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/front/test.rs2
-rw-r--r--src/comp/metadata/tydecode.rs2
-rw-r--r--src/comp/metadata/tyencode.rs6
-rw-r--r--src/comp/middle/alias.rs25
-rw-r--r--src/comp/middle/block_use.rs2
-rw-r--r--src/comp/middle/kind.rs5
-rw-r--r--src/comp/middle/last_use.rs16
-rw-r--r--src/comp/middle/mut.rs12
-rw-r--r--src/comp/middle/trans/base.rs33
-rw-r--r--src/comp/middle/trans/closure.rs11
-rw-r--r--src/comp/middle/trans/common.rs3
-rw-r--r--src/comp/middle/trans/impl.rs3
-rw-r--r--src/comp/middle/tstate/auxiliary.rs8
-rw-r--r--src/comp/middle/tstate/pre_post_conditions.rs9
-rw-r--r--src/comp/middle/ty.rs116
-rw-r--r--src/comp/middle/typeck.rs138
-rw-r--r--src/comp/syntax/ast.rs12
-rw-r--r--src/comp/syntax/parse/parser.rs25
-rw-r--r--src/comp/syntax/print/pprust.rs21
-rw-r--r--src/comp/util/ppaux.rs26
20 files changed, 309 insertions, 166 deletions
diff --git a/src/comp/front/test.rs b/src/comp/front/test.rs
index 0e84930658a..5d74165c4b6 100644
--- a/src/comp/front/test.rs
+++ b/src/comp/front/test.rs
@@ -382,7 +382,7 @@ fn mk_main(cx: test_ctxt) -> @ast::item {
     let args_ty: ast::ty = nospan(ast::ty_vec(args_mt));
 
     let args_arg: ast::arg =
-        {mode: ast::by_val,
+        {mode: ast::expl(ast::by_val),
          ty: @args_ty,
          ident: "args",
          id: cx.sess.next_node_id()};
diff --git a/src/comp/metadata/tydecode.rs b/src/comp/metadata/tydecode.rs
index 92e6da125ef..dc7496dddb3 100644
--- a/src/comp/metadata/tydecode.rs
+++ b/src/comp/metadata/tydecode.rs
@@ -353,7 +353,7 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::fn_ty {
           '#' { ast::by_val }
         };
         st.pos += 1u;
-        inputs += [{mode: mode, ty: parse_ty(st, conv)}];
+        inputs += [{mode: ast::expl(mode), ty: parse_ty(st, conv)}];
     }
     st.pos += 1u; // eat the ']'
     let cs = parse_constrs(st, conv);
diff --git a/src/comp/metadata/tyencode.rs b/src/comp/metadata/tyencode.rs
index 84f77842a58..a9baa4f7470 100644
--- a/src/comp/metadata/tyencode.rs
+++ b/src/comp/metadata/tyencode.rs
@@ -203,16 +203,12 @@ fn enc_proto(w: io::writer, proto: proto) {
 fn enc_ty_fn(w: io::writer, cx: @ctxt, ft: ty::fn_ty) {
     w.write_char('[');
     for arg: ty::arg in ft.inputs {
-        alt arg.mode {
+        alt ty::resolved_mode(cx.tcx, arg.mode) {
           by_mut_ref { w.write_char('&'); }
           by_move { w.write_char('-'); }
           by_copy { w.write_char('+'); }
           by_ref { w.write_char('='); }
           by_val { w.write_char('#'); }
-          // tediously, this has to be there until there's a way
-          // to constraint post-typeck types not to contain a mode_infer
-          mode_infer { cx.tcx.sess.bug("enc_ty_fn: shouldn't see \
-            mode_infer"); }
         }
         enc_ty(w, cx, arg.ty);
     }
diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs
index 7aa675c9e8d..388fcb20c47 100644
--- a/src/comp/middle/alias.rs
+++ b/src/comp/middle/alias.rs
@@ -82,9 +82,11 @@ fn visit_fn(cx: @ctx, _fk: visit::fn_kind, decl: ast::fn_decl,
     let fty = ty::node_id_to_type(cx.tcx, id);
     let args = ty::ty_fn_args(cx.tcx, fty);
     for arg in args {
-        if arg.mode == ast::by_val &&
-           ty::type_has_dynamic_size(cx.tcx, arg.ty) {
+        alt ty::resolved_mode(cx.tcx, arg.mode) {
+          ast::by_val if ty::type_has_dynamic_size(cx.tcx, arg.ty) {
             err(*cx, sp, "can not pass a dynamically-sized type by value");
+          }
+          _ { /* fallthrough */ }
         }
     }
 
@@ -226,7 +228,8 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
     for arg_t: ty::arg in arg_ts {
         let arg = args[i];
         let root = expr_root(cx, arg, false);
-        if arg_t.mode == ast::by_mut_ref {
+        alt ty::resolved_mode(cx.tcx, arg_t.mode) {
+          ast::by_mut_ref {
             alt path_def(cx, arg) {
               some(def) {
                 let dnum = ast_util::def_id_of_def(def).node;
@@ -234,18 +237,21 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
               }
               _ { }
             }
+          }
+          ast::by_ref | ast::by_val | ast::by_move | ast::by_copy { }
         }
         let root_var = path_def_id(cx, root.ex);
+        let arg_copied = alt ty::resolved_mode(cx.tcx, arg_t.mode) {
+          ast::by_move | ast::by_copy { copied }
+          ast::by_mut_ref { not_allowed }
+          ast::by_ref | ast::by_val { not_copied }
+        };
         bindings += [@{node_id: arg.id,
                        span: arg.span,
                        root_var: root_var,
                        local_id: 0u,
                        unsafe_tys: unsafe_set(root.mut),
-                       mutable copied: alt arg_t.mode {
-                         ast::by_move | ast::by_copy { copied }
-                         ast::by_mut_ref { not_allowed }
-                         _ { not_copied }
-                       }}];
+                       mutable copied: arg_copied}];
         i += 1u;
     }
     let f_may_close =
@@ -279,7 +285,8 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
         for unsafe_ty in b.unsafe_tys {
             let i = 0u;
             for arg_t: ty::arg in arg_ts {
-                let mut_alias = arg_t.mode == ast::by_mut_ref;
+                let mut_alias =
+                    (ast::by_mut_ref == ty::arg_mode(cx.tcx, arg_t));
                 if i != j &&
                        ty_can_unsafely_include(cx, unsafe_ty, arg_t.ty,
                                                mut_alias) &&
diff --git a/src/comp/middle/block_use.rs b/src/comp/middle/block_use.rs
index 4357f754a58..9d31e089e10 100644
--- a/src/comp/middle/block_use.rs
+++ b/src/comp/middle/block_use.rs
@@ -28,7 +28,7 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
         v.visit_expr(f, cx, v);
         let i = 0u;
         for arg_t in ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)) {
-            cx.allow_block = arg_t.mode == by_ref;
+            cx.allow_block = (ty::arg_mode(cx.tcx, arg_t) == by_ref);
             v.visit_expr(args[i], cx, v);
             i += 1u;
         }
diff --git a/src/comp/middle/kind.rs b/src/comp/middle/kind.rs
index e06ce29add8..8e02389f2d2 100644
--- a/src/comp/middle/kind.rs
+++ b/src/comp/middle/kind.rs
@@ -165,7 +165,10 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
       expr_call(f, args, _) {
         let i = 0u;
         for arg_t in ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)) {
-            alt arg_t.mode { by_copy { maybe_copy(cx, args[i]); } _ {} }
+            alt ty::arg_mode(cx.tcx, arg_t) {
+              by_copy { maybe_copy(cx, args[i]); }
+              by_ref | by_val | by_mut_ref | by_move { }
+            }
             i += 1u;
         }
       }
diff --git a/src/comp/middle/last_use.rs b/src/comp/middle/last_use.rs
index 27968048a57..9c7fd7c0bc1 100644
--- a/src/comp/middle/last_use.rs
+++ b/src/comp/middle/last_use.rs
@@ -157,7 +157,7 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
                 fns += [arg];
               }
               _ {
-                alt arg_ts[i].mode {
+                alt ty::arg_mode(cx.tcx, arg_ts[i]) {
                   by_mut_ref { clear_if_path(cx, arg, v, false); }
                   _ { v.visit_expr(arg, cx, v); }
                 }
@@ -286,11 +286,21 @@ fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
 fn clear_def_if_path(cx: ctx, d: def, to: bool)
     -> option<node_id> {
     alt d {
-      def_local(def_id, let_copy) | def_arg(def_id, by_copy) |
-      def_arg(def_id, by_move) {
+      def_local(def_id, let_copy) {
         clear_in_current(cx, def_id.node, to);
         some(def_id.node)
       }
+      def_arg(def_id, m) {
+        alt ty::resolved_mode(cx.tcx, m) {
+          by_copy | by_move {
+            clear_in_current(cx, def_id.node, to);
+            some(def_id.node)
+          }
+          by_ref | by_val | by_mut_ref {
+            none
+          }
+        }
+      }
       _ {
         none
       }
diff --git a/src/comp/middle/mut.rs b/src/comp/middle/mut.rs
index 42f658ff8d6..8ea39d33de1 100644
--- a/src/comp/middle/mut.rs
+++ b/src/comp/middle/mut.rs
@@ -228,10 +228,10 @@ fn check_call(cx: @ctx, f: @expr, args: [@expr]) {
     let arg_ts = ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f));
     let i = 0u;
     for arg_t: ty::arg in arg_ts {
-        alt arg_t.mode {
+        alt ty::resolved_mode(cx.tcx, 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 | by_val | by_copy { }
         }
         i += 1u;
     }
@@ -267,8 +267,12 @@ fn is_immutable_def(cx: @ctx, def: def) -> option<str> {
       def_use(_) {
         some("static item")
       }
-      def_arg(_, by_ref) | def_arg(_, by_val) |
-      def_arg(_, mode_infer) { some("argument") }
+      def_arg(_, m) {
+        alt ty::resolved_mode(cx.tcx, m) {
+          by_ref | by_val { some("argument") }
+          by_mut_ref | by_move | by_copy { none }
+        }
+      }
       def_self(_) { some("self argument") }
       def_upvar(_, inner, node_id) {
         let ty = ty::node_id_to_type(cx.tcx, node_id);
diff --git a/src/comp/middle/trans/base.rs b/src/comp/middle/trans/base.rs
index 33ef3230a23..cc3572e28f1 100644
--- a/src/comp/middle/trans/base.rs
+++ b/src/comp/middle/trans/base.rs
@@ -63,16 +63,18 @@ fn type_of(cx: @crate_ctxt, t: ty::t) : type_has_static_size(cx, t)
 
 fn type_of_explicit_args(cx: @crate_ctxt, inputs: [ty::arg]) ->
    [TypeRef] {
-    let atys = [];
-    for arg in inputs {
+    let tcx = ccx_tcx(cx);
+    vec::map(inputs) {|arg|
         let arg_ty = arg.ty;
         // FIXME: would be nice to have a constraint on arg
         // that would obviate the need for this check
         check non_ty_var(cx, arg_ty);
         let llty = type_of_inner(cx, arg_ty);
-        atys += [if arg.mode == ast::by_val { llty } else { T_ptr(llty) }];
+        alt ty::resolved_mode(tcx, arg.mode) {
+          ast::by_val { llty }
+          _ { T_ptr(llty) }
+        }
     }
-    ret atys;
 }
 
 
@@ -2981,15 +2983,16 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty: TypeRef,
     let lv = trans_temp_lval(cx, e);
     let bcx = lv.bcx;
     let val = lv.val;
+    let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode);
     if is_bot {
         // For values of type _|_, we generate an
         // "undef" value, as such a value should never
         // be inspected. It's important for the value
         // to have type lldestty (the callee's expected type).
         val = llvm::LLVMGetUndef(lldestty);
-    } else if arg.mode == ast::by_ref || arg.mode == ast::by_val {
+    } else if arg_mode == ast::by_ref || arg_mode == ast::by_val {
         let copied = false, imm = ty::type_is_immediate(ccx.tcx, e_ty);
-        if arg.mode == ast::by_ref && lv.kind != owned && imm {
+        if arg_mode == ast::by_ref && lv.kind != owned && imm {
             val = do_spill_noroot(bcx, val);
             copied = true;
         }
@@ -3002,10 +3005,10 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty: TypeRef,
             } else { bcx = take_ty(bcx, val, e_ty); }
             add_clean(bcx, val, e_ty);
         }
-        if arg.mode == ast::by_val && (lv.kind == owned || !imm) {
+        if arg_mode == ast::by_val && (lv.kind == owned || !imm) {
             val = Load(bcx, val);
         }
-        } else if arg.mode == ast::by_copy {
+        } else if arg_mode == ast::by_copy {
         let {bcx: cx, val: alloc} = alloc_ty(bcx, e_ty);
         let last_use = ccx.last_uses.contains_key(e.id);
         bcx = cx;
@@ -3031,7 +3034,7 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty: TypeRef,
     }
 
     // Collect arg for later if it happens to be one we've moving out.
-    if arg.mode == ast::by_move {
+    if arg_mode == ast::by_move {
         if lv.kind == owned {
             // Use actual ty, not declared ty -- anything else doesn't make
             // sense if declared ty is a ty param
@@ -4414,9 +4417,10 @@ fn create_llargs_for_fn_args(cx: @fn_ctxt, ty_self: self_arg,
 
 fn copy_args_to_allocas(fcx: @fn_ctxt, bcx: @block_ctxt, args: [ast::arg],
                         arg_tys: [ty::arg]) -> @block_ctxt {
+    let tcx = bcx_tcx(bcx);
     let arg_n: uint = 0u, bcx = bcx;
-    fn epic_fail_(bcx: @block_ctxt) -> ! {
-        bcx_tcx(bcx).sess.bug("Someone forgot\
+    let epic_fail = fn@() -> ! {
+        tcx.sess.bug("Someone forgot\
                 to document an invariant in copy_args_to_allocas!");
     }
     let epic_fail = bind epic_fail_(bcx);
@@ -4424,7 +4428,7 @@ fn copy_args_to_allocas(fcx: @fn_ctxt, bcx: @block_ctxt, args: [ast::arg],
         let id = args[arg_n].id;
         let argval = alt fcx.llargs.get(id) { local_mem(v) { v }
                                               _ { epic_fail() } };
-        alt arg.mode {
+        alt ty::resolved_mode(tcx, arg.mode) {
           ast::by_mut_ref { }
           ast::by_move | ast::by_copy { add_clean(bcx, argval, arg.ty); }
           ast::by_val {
@@ -4438,7 +4442,6 @@ fn copy_args_to_allocas(fcx: @fn_ctxt, bcx: @block_ctxt, args: [ast::arg],
             }
           }
           ast::by_ref {}
-          _ { epic_fail(); }
         }
         if fcx_ccx(fcx).sess.opts.extra_debuginfo {
             debuginfo::create_arg(bcx, args[arg_n], args[arg_n].ty.span);
@@ -4585,7 +4588,7 @@ fn trans_enum_variant(ccx: @crate_ctxt,
     // Translate variant arguments to function arguments.
     let fn_args = [], i = 0u;
     for varg in variant.node.args {
-        fn_args += [{mode: ast::by_copy,
+        fn_args += [{mode: ast::expl(ast::by_copy),
                      ty: varg.ty,
                      ident: "arg" + uint::to_str(i, 10u),
                      id: varg.id}];
@@ -5039,7 +5042,7 @@ fn create_main_wrapper(ccx: @crate_ctxt, sp: span, main_llfn: ValueRef,
                    takes_argv: bool) -> ValueRef {
         let unit_ty = ty::mk_str(ccx.tcx);
         let vecarg_ty: ty::arg =
-            {mode: ast::by_val,
+            {mode: ast::expl(ast::by_val),
              ty: ty::mk_vec(ccx.tcx, {ty: unit_ty, mut: ast::imm})};
         // FIXME: mk_nil should have a postcondition
         let nt = ty::mk_nil(ccx.tcx);
diff --git a/src/comp/middle/trans/closure.rs b/src/comp/middle/trans/closure.rs
index 43d8c675b22..12997f0e539 100644
--- a/src/comp/middle/trans/closure.rs
+++ b/src/comp/middle/trans/closure.rs
@@ -895,13 +895,20 @@ fn trans_bind_thunk(ccx: @crate_ctxt,
                              [0, abi::closure_body_bindings, b]);
             bcx = bound_arg.bcx;
             let val = bound_arg.val;
-            if out_arg.mode == ast::by_val { val = Load(bcx, val); }
-            if out_arg.mode == ast::by_copy {
+
+            alt ty::resolved_mode(tcx, out_arg.mode) {
+              ast::by_val {
+                val = Load(bcx, val);
+              }
+              ast::by_copy {
                 let {bcx: cx, val: alloc} = alloc_ty(bcx, out_arg.ty);
                 bcx = memmove_ty(cx, alloc, val, out_arg.ty);
                 bcx = take_ty(bcx, alloc, out_arg.ty);
                 val = alloc;
+              }
+              ast::by_ref | ast::by_mut_ref | ast::by_move { }
             }
+
             // If the type is parameterized, then we need to cast the
             // type we actually have to the parameterized out type.
             if ty::type_contains_params(ccx.tcx, out_arg.ty) {
diff --git a/src/comp/middle/trans/common.rs b/src/comp/middle/trans/common.rs
index 2fb83e0d77e..4f8a6402da3 100644
--- a/src/comp/middle/trans/common.rs
+++ b/src/comp/middle/trans/common.rs
@@ -300,7 +300,8 @@ fn get_res_dtor(ccx: @crate_ctxt, 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, [{mode: ast::by_ref, ty: inner_t}],
+    let fn_mode = ast::expl(ast::by_ref);
+    let f_t = type_of_fn(ccx, [{mode: fn_mode, ty: inner_t}],
                          nil_res, *param_bounds);
     ret base::get_extern_const(ccx.externs, ccx.llmod,
                                 csearch::get_symbol(ccx.sess.cstore,
diff --git a/src/comp/middle/trans/impl.rs b/src/comp/middle/trans/impl.rs
index a0bc1a582fa..18ee215b2dc 100644
--- a/src/comp/middle/trans/impl.rs
+++ b/src/comp/middle/trans/impl.rs
@@ -63,7 +63,8 @@ fn trans_impl(ccx: @crate_ctxt, path: path, name: ast::ident,
 fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result {
     let tz = [], tr = [];
     let basety = expr_ty(bcx, base);
-    let {bcx, val} = trans_arg_expr(bcx, {mode: ast::by_ref, ty: basety},
+    let m_by_ref = ast::expl(ast::by_ref);
+    let {bcx, val} = trans_arg_expr(bcx, {mode: m_by_ref, ty: basety},
                                     T_ptr(type_of_or_i8(bcx, basety)), tz,
                                     tr, base);
     rslt(bcx, PointerCast(bcx, val, T_opaque_cbox_ptr(bcx_ccx(bcx))))
diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs
index 3ca8a0b89fb..222006f3fa8 100644
--- a/src/comp/middle/tstate/auxiliary.rs
+++ b/src/comp/middle/tstate/auxiliary.rs
@@ -1089,10 +1089,12 @@ fn callee_modes(fcx: fn_ctxt, callee: node_id) -> [mode] {
 }
 
 fn callee_arg_init_ops(fcx: fn_ctxt, callee: node_id) -> [init_op] {
-    fn mode_to_op(m: mode) -> init_op {
-        alt m { by_move { init_move } _ { init_assign } }
+    vec::map(callee_modes(fcx, callee)) {|m|
+        alt ty::resolved_mode(fcx.ccx.tcx, m) {
+          by_move { init_move }
+          by_copy | by_ref | by_val | by_mut_ref { init_assign }
+        }
     }
-    vec::map(callee_modes(fcx, callee), mode_to_op)
 }
 
 fn anon_bindings(ops: [init_op], es: [@expr]) -> [binding] {
diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs
index 02e5a33545c..0ca3a6ee294 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -276,12 +276,11 @@ fn handle_var_def(fcx: fn_ctxt, rslt: pre_and_post, def: def, name: ident) {
 
 fn forget_args_moved_in(fcx: fn_ctxt, parent: @expr, modes: [mode],
                         operands: [@expr]) {
-    let i = 0u;
-    for mode: mode in modes {
-        if mode == by_move {
-            forget_in_postcond(fcx, parent.id, operands[i].id);
+    vec::iteri(modes) {|i,mode|
+        alt ty::resolved_mode(fcx.ccx.tcx, mode) {
+          by_move { forget_in_postcond(fcx, parent.id, operands[i].id); }
+          by_ref | by_val | by_mut_ref | by_copy { }
         }
-        i += 1u;
     }
 }
 
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 9c507d296c6..02f687cb615 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -19,7 +19,6 @@ import util::common::*;
 import syntax::util::interner;
 import util::ppaux::ty_to_str;
 import util::ppaux::ty_constr_to_str;
-import util::ppaux::mode_str;
 import syntax::print::pprust::*;
 
 export node_id_to_type;
@@ -168,6 +167,10 @@ export type_structurally_contains;
 export type_structurally_contains_uniques;
 export type_autoderef;
 export type_param;
+export resolved_mode;
+export arg_mode;
+export unify_mode;
+export set_default_mode;
 export unify;
 export variant_info;
 export walk_ty;
@@ -178,12 +181,12 @@ export ck_box;
 export ck_uniq;
 export param_bound, param_bounds, bound_copy, bound_send, bound_iface;
 export param_bounds_to_kind;
+export default_arg_mode_for_ty;
 
 // Data types
 
-// TODO: really should be a separate type, or a refinement,
-// so that we don't have to handle the mode_infer case after
-// typeck. but that's too hard right now.
+// Note: after typeck, you should use resolved_mode() to convert this mode
+// into an rmode, which will take into account the results of mode inference.
 type arg = {mode: ast::mode, ty: t};
 
 type field = {ident: ast::ident, mt: mt};
@@ -217,7 +220,8 @@ type ctxt =
       ast_ty_to_ty_cache: hashmap<@ast::ty, option<t>>,
       enum_var_cache: hashmap<def_id, @[variant_info]>,
       iface_method_cache: hashmap<def_id, @[method]>,
-      ty_param_bounds: hashmap<ast::node_id, param_bounds>};
+      ty_param_bounds: hashmap<ast::node_id, param_bounds>,
+      inferred_modes: hashmap<ast::node_id, ast::mode>};
 
 type ty_ctxt = ctxt;
 
@@ -424,7 +428,8 @@ fn mk_ctxt(s: session::session, dm: resolve::def_map, amap: ast_map::map,
               map::mk_hashmap(ast_util::hash_ty, ast_util::eq_ty),
           enum_var_cache: new_def_hash(),
           iface_method_cache: new_def_hash(),
-          ty_param_bounds: map::new_int_hash()};
+          ty_param_bounds: map::new_int_hash(),
+          inferred_modes: map::new_int_hash()};
     populate_type_store(cx);
     ret cx;
 }
@@ -646,6 +651,12 @@ pure fn ty_name(cx: ctxt, typ: t) -> option<@str> {
     }
 }
 
+fn default_arg_mode_for_ty(tcx: ty::ctxt, ty: ty::t) -> ast::rmode {
+    assert !ty::type_contains_vars(tcx, ty);
+    if ty::type_is_immediate(tcx, ty) { ast::by_val }
+    else { ast::by_ref }
+}
+
 fn walk_ty(cx: ctxt, ty: t, f: fn(t)) {
     alt struct(cx, ty) {
       ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
@@ -684,13 +695,14 @@ enum fold_mode {
 
 fn fold_ty(cx: ctxt, fld: fold_mode, ty_0: t) -> t {
     let ty = ty_0;
-    // Fast paths.
 
+    // Fast paths.
     alt fld {
       fm_var(_) { if !type_contains_vars(cx, ty) { ret ty; } }
       fm_param(_) { if !type_contains_params(cx, ty) { ret ty; } }
       fm_general(_) {/* no fast path */ }
     }
+
     alt interner::get(*cx.ts, ty).struct {
       ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
       ty_str | ty_type | ty_send_type | ty_opaque_closure_ptr(_) {}
@@ -1619,6 +1631,77 @@ fn occurs_check_fails(tcx: ctxt, sp: option<span>, vid: int, rt: t) ->
     } else { ret false; }
 }
 
+// Maintains a little union-set tree for inferred modes.  `canon()` returns
+// the current head value for `m0`.
+fn canon<T:copy>(tbl: hashmap<ast::node_id, ast::inferable<T>>,
+                 m0: ast::inferable<T>) -> ast::inferable<T> {
+    alt m0 {
+      ast::infer(id) {
+        alt tbl.find(id) {
+          none { m0 }
+          some(m1) {
+            let cm1 = canon(tbl, m1);
+            // path compression:
+            if cm1 != m1 { tbl.insert(id, cm1); }
+            cm1
+          }
+        }
+      }
+      _ { m0 }
+    }
+}
+
+// Maintains a little union-set tree for inferred modes.  `resolve_mode()`
+// returns the current head value for `m0`.
+fn canon_mode(cx: ctxt, m0: ast::mode) -> ast::mode {
+    canon(cx.inferred_modes, m0)
+}
+
+// Returns the head value for mode, failing if `m` was a infer(_) that
+// was never inferred.  This should be safe for use after typeck.
+fn resolved_mode(cx: ctxt, m: ast::mode) -> ast::rmode {
+    alt canon_mode(cx, m) {
+      ast::infer(_) {
+        cx.sess.bug(#fmt["mode %? was never resolved", m]);
+      }
+      ast::expl(m0) { m0 }
+    }
+}
+
+fn arg_mode(cx: ctxt, a: arg) -> ast::rmode { ty::resolved_mode(cx, a.mode) }
+
+// Unifies `m1` and `m2`.  Returns unified value or failure code.
+fn unify_mode(cx: ctxt, m1: ast::mode, m2: ast::mode)
+    -> result::t<ast::mode, type_err> {
+    alt (canon_mode(cx, m1), canon_mode(cx, m2)) {
+      (m1, m2) if (m1 == m2) {
+        result::ok(m1)
+      }
+      (ast::infer(id1), ast::infer(id2)) {
+        cx.inferred_modes.insert(id2, m1);
+        result::ok(m1)
+      }
+      (ast::infer(id), m) | (m, ast::infer(id)) {
+        cx.inferred_modes.insert(id, m);
+        result::ok(m1)
+      }
+      (m1, m2) {
+        result::err(terr_mode_mismatch(m1, m2))
+      }
+    }
+}
+
+// If `m` was never unified, unifies it with `m_def`.  Returns the final value
+// for `m`.
+fn set_default_mode(cx: ctxt, m: ast::mode, m_def: ast::rmode) {
+    alt canon_mode(cx, m) {
+      ast::infer(id) {
+        cx.inferred_modes.insert(id, ast::expl(m_def));
+      }
+      ast::expl(_) { }
+    }
+}
+
 // Type unification via Robinson's algorithm (Robinson 1965). Implemented as
 // described in Hoder and Voronkov:
 //
@@ -1869,15 +1952,14 @@ mod unify {
         for expected_input in e_args {
             let actual_input = a_args[i];
             i += 1u;
+
             // Unify the result modes.
-            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 either::left(ures_err(terr_mode_mismatch(
-                    expected_input.mode, actual_input.mode)));
-            } else { expected_input.mode };
+            let result_mode =
+                alt unify_mode(cx.tcx, expected_input.mode,
+                               actual_input.mode) {
+                  result::err(err) { ret either::left(ures_err(err)); }
+                  result::ok(m) { m }
+                };
 
             alt unify_step(cx, expected_input.ty, actual_input.ty,
                            variance) {
@@ -2446,8 +2528,8 @@ fn type_err_to_str(err: ty::type_err) -> str {
       }
       terr_arg_count { ret "incorrect number of function parameters"; }
       terr_mode_mismatch(e_mode, a_mode) {
-        ret "expected argument mode " + mode_str(e_mode) + " but found " +
-                mode_str(a_mode);
+        ret "expected argument mode " + mode_to_str(e_mode) + " but found " +
+                mode_to_str(a_mode);
       }
       terr_constr_len(e_len, a_len) {
         ret "Expected a type with " + uint::str(e_len) +
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index 90dfd5ccd95..148222d95f0 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -64,7 +64,6 @@ type fn_ctxt =
      var_bindings: @ty::unify::var_bindings,
      locals: hashmap<ast::node_id, int>,
      next_var_id: @mutable int,
-     mutable fixups: [ast::node_id],
      ccx: @crate_ctxt};
 
 
@@ -215,27 +214,11 @@ fn type_is_c_like_enum(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
     ret ty::type_is_c_like_enum(fcx.ccx.tcx, typ_s);
 }
 
-// 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 }
-    }
-}
-
 enum mode { m_collect, m_check, m_check_tyvar(@fn_ctxt), }
 
+// 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 ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t {
     fn getter(tcx: ty::ctxt, mode: mode, id: ast::def_id)
         -> ty::ty_param_bounds_and_ty {
@@ -443,13 +426,38 @@ fn ty_of_native_item(tcx: ty::ctxt, mode: mode, it: @ast::native_item)
     }
 }
 fn ty_of_arg(tcx: ty::ctxt, mode: mode, a: ast::arg) -> ty::arg {
+    fn arg_mode(tcx: ty::ctxt, m: ast::mode, ty: ty::t) -> ast::mode {
+        alt m {
+          ast::infer(_) {
+            alt ty::struct(tcx, ty) {
+              // If the type is not specified, then this must be a fn expr.
+              // Leave the mode as infer(_), it will get inferred based
+              // on constraints elsewhere.
+              ty::ty_var(_) { m }
+
+              // If the type is known, then use the default for that type.
+              // Here we unify m and the default.  This should update the
+              // tables in tcx but should never fail, because nothing else
+              // will have been unified with m yet:
+              _ {
+                let m1 = ast::expl(ty::default_arg_mode_for_ty(tcx, ty));
+                result::get(ty::unify_mode(tcx, m, m1))
+              }
+            }
+          }
+          ast::expl(_) { m }
+        }
+    }
+
     let ty = ast_ty_to_ty(tcx, mode, a.ty);
-    {mode: default_arg_mode_for_ty(tcx, a.mode, ty), ty: ty}
-}
-fn ty_of_fn_decl(tcx: ty::ctxt, mode: mode,
-                 proto: ast::proto, decl: ast::fn_decl) -> ty::fn_ty {
-    let input_tys = [];
-    for a: ast::arg in decl.inputs { input_tys += [ty_of_arg(tcx, mode, a)]; }
+    let mode = arg_mode(tcx, a.mode, ty);
+    {mode: mode, ty: ty}
+}
+fn ty_of_fn_decl(tcx: ty::ctxt,
+                 mode: mode,
+                 proto: ast::proto,
+                 decl: ast::fn_decl) -> ty::fn_ty {
+    let input_tys = vec::map(decl.inputs) {|a| ty_of_arg(tcx, mode, a) };
     let output_ty = ast_ty_to_ty(tcx, mode, decl.output);
 
     let out_constrs = [];
@@ -472,7 +480,9 @@ fn ty_of_native_fn_decl(tcx: ty::ctxt, mode: mode, decl: ast::fn_decl,
                         ty_params: [ast::ty_param], def_id: ast::def_id)
     -> ty::ty_param_bounds_and_ty {
     let input_tys = [], bounds = ty_param_bounds(tcx, mode, ty_params);
-    for a: ast::arg in decl.inputs { input_tys += [ty_of_arg(tcx, mode, a)]; }
+    for a: ast::arg in decl.inputs {
+        input_tys += [ty_of_arg(tcx, mode, a)];
+    }
     let output_ty = ast_ty_to_ty(tcx, mode, decl.output);
 
     let t_fn = ty::mk_fn(tcx, {proto: ast::proto_bare,
@@ -585,7 +595,9 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method,
     } else {
         let auto_modes = vec::map2(impl_m.fty.inputs, if_m.fty.inputs, {|i, f|
             alt ty::struct(tcx, f.ty) {
-              ty::ty_param(0u, _) { {mode: ast::by_ref with i} }
+              ty::ty_param(0u, _) {
+                {mode: ast::expl(ast::by_ref) with i}
+              }
               _ { i }
             }
         });
@@ -641,7 +653,7 @@ mod collect {
                 let args: [arg] = [];
                 for va: ast::variant_arg in variant.node.args {
                     let arg_ty = ast_ty_to_ty(cx.tcx, m_collect, va.ty);
-                    args += [{mode: ast::by_copy, ty: arg_ty}];
+                    args += [{mode: ast::expl(ast::by_copy), ty: arg_ty}];
                 }
                 // FIXME: this will be different for constrained types
                 ty::mk_fn(cx.tcx,
@@ -716,7 +728,7 @@ mod collect {
                                    params);
             let t_ctor = ty::mk_fn(cx.tcx, {
                 proto: ast::proto_box,
-                inputs: [{mode: ast::by_copy with t_arg}],
+                inputs: [{mode: ast::expl(ast::by_copy) with t_arg}],
                 output: t_res,
                 ret_style: ast::return_val, constraints: []
             });
@@ -925,9 +937,6 @@ fn variant_arg_types(ccx: @crate_ctxt, _sp: span, vid: ast::def_id,
 // Type resolution: the phase that finds all the types in the AST with
 // unresolved type variables and replaces "ty_var" types with their
 // substitutions.
-//
-// TODO: inefficient since not all types have vars in them. It would be better
-// to maintain a list of fixups.
 mod writeback {
 
     export resolve_type_vars_in_block;
@@ -946,24 +955,32 @@ mod writeback {
           }
         }
     }
-    fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id) {
+    fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id)
+        -> option<ty::t> {
         let fcx = wbcx.fcx, tcx = fcx.ccx.tcx;
         alt resolve_type_vars_in_type(fcx, sp, ty::node_id_to_type(tcx, id)) {
-          some(t) { write_ty(tcx, id, t); }
-          none { wbcx.success = false; ret }
-        }
-        alt tcx.node_type_substs.find(id) {
-          some(substs) {
-            let new_substs = [];
-            for subst: ty::t in substs {
-                alt resolve_type_vars_in_type(fcx, sp, subst) {
-                  some(t) { new_substs += [t]; }
-                  none { wbcx.success = false; ret; }
+          none {
+            wbcx.success = false;
+            ret none;
+          }
+
+          some(t) {
+            write_ty(tcx, id, t);
+            alt tcx.node_type_substs.find(id) {
+              some(substs) {
+                let new_substs = [];
+                for subst: ty::t in substs {
+                    alt resolve_type_vars_in_type(fcx, sp, subst) {
+                      some(t) { new_substs += [t]; }
+                      none { wbcx.success = false; ret none; }
+                    }
                 }
+                write_substs(tcx, id, new_substs);
+              }
+              none {}
             }
-            write_substs(tcx, id, new_substs);
+            ret some(t);
           }
-          none {}
         }
     }
 
@@ -984,8 +1001,19 @@ mod writeback {
         alt e.node {
           ast::expr_fn(_, decl, _, _) |
           ast::expr_fn_block(decl, _) {
-            for input in decl.inputs {
-                resolve_type_vars_for_node(wbcx, e.span, input.id);
+            vec::iter(decl.inputs) {|input|
+                let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id);
+
+                // Just in case we never constrained the mode to anything,
+                // constrain it to the default for the type in question.
+                alt (r_ty, input.mode) {
+                  (some(t), ast::infer(_)) {
+                    let tcx = wbcx.fcx.ccx.tcx;
+                    let m_def = ty::default_arg_mode_for_ty(tcx, t);
+                    ty::set_default_mode(tcx, input.mode, m_def);
+                  }
+                  _ {}
+                }
             }
           }
           _ { }
@@ -1541,8 +1569,8 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt,
                               unify: unifier,
                               expected: ty::t) {
     let tcx = fcx.ccx.tcx;
-    let fty = ty::mk_fn(tcx, ty_of_fn_decl(tcx, m_check_tyvar(fcx),
-                                           proto, decl));
+    let fty = ty::mk_fn(tcx,
+                        ty_of_fn_decl(tcx, m_check_tyvar(fcx), proto, decl));
 
     #debug("check_expr_fn_with_unifier %s fty=%s",
            expr_to_str(expr),
@@ -1600,7 +1628,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
                          }]);
             // HACK: build an arguments list with dummy arguments to
             // check against
-            let dummy = {mode: ast::by_ref, ty: ty::mk_bot(fcx.ccx.tcx)};
+            let dummy = {mode: ast::expl(ast::by_ref),
+                         ty: ty::mk_bot(fcx.ccx.tcx)};
             arg_tys = vec::init_elt(supplied_arg_count, dummy);
         }
 
@@ -2027,7 +2056,6 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
                ty_to_str(tcx, expected));
         check_expr_fn_with_unifier(fcx, expr, proto, decl, body,
                                    unify, expected);
-        write_ty(tcx, id, expected);
       }
       ast::expr_block(b) {
         // If this is an unchecked block, turn off purity-checking
@@ -2428,7 +2456,6 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
     // FIXME: this is kinda a kludge; we manufacture a fake function context
     // and statement context for checking the initializer expression.
     let rty = node_id_to_type(ccx.tcx, id);
-    let fixups: [ast::node_id] = [];
     let fcx: @fn_ctxt =
         @{ret_ty: rty,
           purity: ast::pure_fn,
@@ -2436,7 +2463,6 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
           var_bindings: ty::unify::mk_var_bindings(),
           locals: new_int_hash::<int>(),
           next_var_id: @mutable 0,
-          mutable fixups: fixups,
           ccx: ccx};
     check_expr(fcx, e);
     let cty = expr_ty(fcx.ccx.tcx, e);
@@ -2449,7 +2475,6 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
     // FIXME: this is kinda a kludge; we manufacture a fake function context
     // and statement context for checking the initializer expression.
     let rty = node_id_to_type(ccx.tcx, id);
-    let fixups: [ast::node_id] = [];
     let fcx: @fn_ctxt =
         @{ret_ty: rty,
           purity: ast::pure_fn,
@@ -2457,7 +2482,6 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
           var_bindings: ty::unify::mk_var_bindings(),
           locals: new_int_hash::<int>(),
           next_var_id: @mutable 0,
-          mutable fixups: fixups,
           ccx: ccx};
     let disr_vals: [int] = [];
     let disr_val = 0;
@@ -2630,7 +2654,6 @@ fn check_fn(ccx: @crate_ctxt,
     };
 
     let gather_result = gather_locals(ccx, decl, body, id, old_fcx);
-    let fixups: [ast::node_id] = [];
     let fcx: @fn_ctxt =
         @{ret_ty: ty::ty_fn_ret(ccx.tcx, ty::node_id_to_type(ccx.tcx, id)),
           purity: purity,
@@ -2638,7 +2661,6 @@ fn check_fn(ccx: @crate_ctxt,
           var_bindings: gather_result.var_bindings,
           locals: gather_result.locals,
           next_var_id: gather_result.next_var_id,
-          mutable fixups: fixups,
           ccx: ccx};
 
     check_constraints(fcx, decl.constraints, decl.inputs);
diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs
index b8e44257f52..8db4573c2b4 100644
--- a/src/comp/syntax/ast.rs
+++ b/src/comp/syntax/ast.rs
@@ -160,7 +160,17 @@ enum unop {
     deref, not, neg,
 }
 
-enum mode { by_ref, by_val, by_mut_ref, by_move, by_copy, mode_infer, }
+// Generally, after typeck you can get the inferred value
+// using ty::resolved_T(...).
+enum inferable<T> {
+    expl(T), infer(node_id)
+}
+
+// "resolved" mode: the real modes.
+enum rmode { by_ref, by_val, by_mut_ref, by_move, by_copy }
+
+// inferable mode.
+type mode = inferable<rmode>;
 
 type stmt = spanned<stmt_>;
 
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index a4ed54f2675..0b321b17b92 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -521,14 +521,19 @@ fn parse_ty(p: parser, colons_before_params: bool) -> @ast::ty {
 }
 
 fn parse_arg_mode(p: parser) -> ast::mode {
-    if eat(p, token::BINOP(token::AND)) { ast::by_mut_ref }
-    else if eat(p, token::BINOP(token::MINUS)) { ast::by_move }
-    else if eat(p, token::ANDAND) { ast::by_ref }
-    else if eat(p, token::BINOP(token::PLUS)) {
-        if eat(p, token::BINOP(token::PLUS)) { ast::by_val }
-        else { ast::by_copy }
-    }
-    else { ast::mode_infer }
+    if eat(p, token::BINOP(token::AND)) {
+        ast::expl(ast::by_mut_ref)
+    } else if eat(p, token::BINOP(token::MINUS)) {
+        ast::expl(ast::by_move)
+    } else if eat(p, token::ANDAND) {
+        ast::expl(ast::by_ref)
+    } else if eat(p, token::BINOP(token::PLUS)) {
+        if eat(p, token::BINOP(token::PLUS)) {
+            ast::expl(ast::by_val)
+        } else {
+            ast::expl(ast::by_copy)
+        }
+    } else { ast::infer(p.get_id()) }
 }
 
 fn parse_arg(p: parser) -> ast::arg {
@@ -1984,8 +1989,8 @@ fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item {
     let dtor = parse_block_no_value(p);
     let decl =
         {inputs:
-             [{mode: ast::by_ref, ty: t, ident: arg_ident,
-               id: p.get_id()}],
+             [{mode: ast::expl(ast::by_ref), ty: t,
+               ident: arg_ident, id: p.get_id()}],
          output: @spanned(lo, lo, ast::ty_nil),
          purity: ast::impure_fn,
          cf: ast::return_val,
diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs
index f4a04c145d7..864702d2ba1 100644
--- a/src/comp/syntax/print/pprust.rs
+++ b/src/comp/syntax/print/pprust.rs
@@ -136,7 +136,7 @@ fn res_to_str(decl: ast::fn_decl, name: ast::ident,
 fn test_res_to_str() {
     let decl: ast::fn_decl = {
         inputs: [{
-            mode: ast::by_val,
+            mode: ast::expl(ast::by_val),
             ty: @ast_util::respan(ast_util::dummy_sp(), ast::ty_bool),
             ident: "b",
             id: 0
@@ -1280,17 +1280,22 @@ fn print_fn_block_args(s: ps, decl: ast::fn_decl) {
     maybe_print_comment(s, decl.output.span.lo);
 }
 
-fn print_arg_mode(s: ps, m: ast::mode) {
+fn mode_to_str(m: ast::mode) -> str {
     alt m {
-      ast::by_mut_ref { word(s.s, "&"); }
-      ast::by_move { word(s.s, "-"); }
-      ast::by_ref { word(s.s, "&&"); }
-      ast::by_val { word(s.s, "++"); }
-      ast::by_copy { word(s.s, "+"); }
-      ast::mode_infer {}
+      ast::expl(ast::by_mut_ref) { "&" }
+      ast::expl(ast::by_move) { "-" }
+      ast::expl(ast::by_ref) { "&&" }
+      ast::expl(ast::by_val) { "++" }
+      ast::expl(ast::by_copy) { "+" }
+      ast::infer(_) { "" }
     }
 }
 
+fn print_arg_mode(s: ps, m: ast::mode) {
+    let ms = mode_to_str(m);
+    if ms != "" { word(s.s, ms); }
+}
+
 fn print_bounds(s: ps, bounds: @[ast::ty_param_bound]) {
     if vec::len(*bounds) > 0u {
         word(s.s, ":");
diff --git a/src/comp/util/ppaux.rs b/src/comp/util/ppaux.rs
index 4772bbc4fc3..512b02ca734 100644
--- a/src/comp/util/ppaux.rs
+++ b/src/comp/util/ppaux.rs
@@ -4,32 +4,18 @@ import middle::ty;
 import middle::ty::*;
 import metadata::encoder;
 import syntax::print::pprust;
-import syntax::print::pprust::{path_to_str, constr_args_to_str, proto_to_str};
+import syntax::print::pprust::{path_to_str, constr_args_to_str, proto_to_str,
+                               mode_to_str};
 import syntax::{ast, ast_util};
 import middle::ast_map;
 
-fn mode_str(m: ast::mode) -> str {
-    alt m {
-      ast::by_ref { "&&" }
-      ast::by_val { "++" }
-      ast::by_mut_ref { "&" }
-      ast::by_move { "-" }
-      ast::by_copy { "+" }
-      _ { "" }
-    }
-}
-
 fn ty_to_str(cx: ctxt, typ: t) -> str {
     fn fn_input_to_str(cx: ctxt, input: {mode: ast::mode, ty: t}) ->
        str {
-        let modestr = alt input.mode {
-          ast::by_ref {
-            if ty::type_is_immediate(cx, input.ty) { "&&" } else { "" }
-          }
-          ast::by_val {
-            if ty::type_is_immediate(cx, input.ty) { "" } else { "++" }
-          }
-          _ { mode_str(input.mode) }
+        let arg_mode = ty::arg_mode(cx, input);
+        let modestr = {
+            if arg_mode == ty::default_arg_mode_for_ty(cx, input.ty) { "" }
+            else { mode_to_str(input.mode) }
         };
         modestr + ty_to_str(cx, input.ty)
     }