about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2011-08-15 11:40:26 +0200
committerMarijn Haverbeke <marijnh@gmail.com>2011-08-15 11:40:38 +0200
commit9538b0036358acc1e1d9ca8b00128bc0fa9940b2 (patch)
treeb73c9feabcdcb66f6cc318e7ce03727c9db4af6f
parent25b85df3704de79467dafa30ed185e8d94cd46c7 (diff)
Tuple types back, not constructable yet
-rw-r--r--src/comp/metadata/tydecode.rs7
-rw-r--r--src/comp/metadata/tyencode.rs5
-rw-r--r--src/comp/middle/alias.rs8
-rw-r--r--src/comp/middle/gc.rs6
-rw-r--r--src/comp/middle/shape.rs6
-rw-r--r--src/comp/middle/trans.rs55
-rw-r--r--src/comp/middle/trans_comm.rs4
-rw-r--r--src/comp/middle/tstate/pre_post_conditions.rs3
-rw-r--r--src/comp/middle/tstate/states.rs6
-rw-r--r--src/comp/middle/ty.rs138
-rw-r--r--src/comp/middle/typeck.rs17
-rw-r--r--src/comp/syntax/ast.rs2
-rw-r--r--src/comp/syntax/fold.rs7
-rw-r--r--src/comp/syntax/parse/parser.rs1
-rw-r--r--src/comp/syntax/print/pprust.rs18
-rw-r--r--src/comp/syntax/visit.rs6
-rw-r--r--src/comp/util/ppaux.rs5
17 files changed, 278 insertions, 16 deletions
diff --git a/src/comp/metadata/tydecode.rs b/src/comp/metadata/tydecode.rs
index 961d7f1f400..523aada6e62 100644
--- a/src/comp/metadata/tydecode.rs
+++ b/src/comp/metadata/tydecode.rs
@@ -236,6 +236,13 @@ fn parse_ty(st: @pstate, sd: str_def) -> ty::t {
         st.pos = st.pos + 1u;
         ret ty::mk_rec(st.tcx, fields);
       }
+      'T' {
+        assert (next(st) as char == '[');
+        let params = ~[];
+        while peek(st) as char != ']' { params += ~[parse_mt(st, sd)]; }
+        st.pos = st.pos + 1u;
+        ret ty::mk_tup(st.tcx, params);
+      }
       'F' {
         let func = parse_ty_fn(st, sd);
         ret ty::mk_fn(st.tcx, ast::proto_fn, func.args, func.ty, func.cf,
diff --git a/src/comp/metadata/tyencode.rs b/src/comp/metadata/tyencode.rs
index 4a58a613fc3..71f9e2ca113 100644
--- a/src/comp/metadata/tyencode.rs
+++ b/src/comp/metadata/tyencode.rs
@@ -120,6 +120,11 @@ fn enc_sty(w: &io::writer, cx: &@ctxt, st: &ty::sty) {
         for t: ty::t  in tys { enc_ty(w, cx, t); }
         w.write_char(']');
       }
+      ty::ty_tup(mts) {
+        w.write_str("T[");
+        for mt in mts { enc_mt(w, cx, mt); }
+        w.write_char(']');
+      }
       ty::ty_box(mt) { w.write_char('@'); enc_mt(w, cx, mt); }
       ty::ty_uniq(t) { w.write_char('~'); enc_ty(w, cx, t); }
       ty::ty_ptr(mt) { w.write_char('*'); enc_mt(w, cx, mt); }
diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs
index 613f8510fe0..8f19665ea56 100644
--- a/src/comp/middle/alias.rs
+++ b/src/comp/middle/alias.rs
@@ -678,6 +678,14 @@ fn ty_can_unsafely_include(cx: &ctx, needle: ty::t, haystack: ty::t,
             }
             ret false;
           }
+          ty::ty_tup(mts) {
+            for mt in mts {
+                if helper(tcx, needle, mt.ty, get_mut(mut, mt)) {
+                    ret true;
+                }
+            }
+            ret false;
+          }
 
           // These may contain anything.
           ty::ty_fn(_, _, _, _, _) {
diff --git a/src/comp/middle/gc.rs b/src/comp/middle/gc.rs
index 056adf94cc5..c2b0819d5ff 100644
--- a/src/comp/middle/gc.rs
+++ b/src/comp/middle/gc.rs
@@ -46,6 +46,12 @@ fn type_is_gc_relevant(cx: &ty::ctxt, ty: &ty::t) -> bool {
             }
             ret false;
         }
+        ty::ty_tup(elts) {
+            for elt in elts {
+                if type_is_gc_relevant(cx, elt.ty) { ret true; }
+            }
+            ret false;
+        }
 
         ty::ty_tag(did, tps) {
             let variants = ty::tag_variants(cx, did);
diff --git a/src/comp/middle/shape.rs b/src/comp/middle/shape.rs
index 9cbb546535b..7c01a8e2751 100644
--- a/src/comp/middle/shape.rs
+++ b/src/comp/middle/shape.rs
@@ -371,6 +371,12 @@ fn shape_of(ccx : &@crate_ctxt, t : ty::t) -> [u8] {
         for f : field in fields { sub += shape_of(ccx, f.mt.ty); }
         add_substr(s, sub);
       }
+      ty::ty_tup(elts) {
+        s += ~[shape_struct];
+        let sub = ~[];
+        for elt in elts { sub += shape_of(ccx, elt.ty); }
+        add_substr(s, sub);
+      }
 
       ty::ty_fn(_,_,_,_,_) { s += ~[shape_fn]; }
       ty::ty_native_fn(_,_,_) { s += ~[shape_u32]; }
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 13bac3218bb..0cc9176a66f 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -253,6 +253,13 @@ fn type_of_inner(cx: &@crate_ctxt, sp: &span, t: &ty::t) -> TypeRef {
       }
       ty::ty_param(_, _) { llty = T_typaram(cx.tn); }
       ty::ty_type. { llty = T_ptr(cx.tydesc_type); }
+      ty::ty_tup(elts) {
+        let tys = ~[];
+        for elt in elts {
+            tys += ~[type_of_inner(cx, sp, elt.ty)];
+        }
+        llty = T_struct(tys);
+      }
     }
     assert (llty as int != 0);
     cx.lltypes.insert(t, llty);
@@ -577,6 +584,11 @@ fn dynamic_size_of(cx: &@block_ctxt, t: ty::t) -> result {
         for f: ty::field  in flds { tys += ~[f.mt.ty]; }
         ret align_elements(cx, tys);
       }
+      ty::ty_tup(elts) {
+        let tys = ~[];
+        for mt in elts { tys += ~[mt.ty]; }
+        ret align_elements(cx, tys);
+      }
       ty::ty_tag(tid, tps) {
         let bcx = cx;
         // Compute max(variant sizes).
@@ -645,6 +657,16 @@ fn dynamic_align_of(cx: &@block_ctxt, t: &ty::t) -> result {
         let llalign = umax(bcx, llalign_of(T_int()), llunitalign);
         ret rslt(bcx, llalign);
       }
+      ty::ty_tup(elts) {
+        let a = C_int(1);
+        let bcx = cx;
+        for e in elts {
+            let align = align_of(bcx, e.ty);
+            bcx = align.bcx;
+            a = umax(bcx, a, align.val);
+        }
+        ret rslt(bcx, a);
+      }
     }
 }
 
@@ -1811,7 +1833,7 @@ fn iter_structural_ty_full(cx: &@block_ctxt, av: ValueRef, t: &ty::t,
     alt ty::struct(bcx_tcx(cx), t) {
       ty::ty_rec(fields) {
         let i: int = 0;
-        for fld: ty::field  in fields {
+        for fld: ty::field in fields {
             r = GEP_tup_like(r.bcx, t, av, ~[0, i]);
             let llfld_a = r.val;
             r = f(r.bcx, load_if_immediate(r.bcx, llfld_a, fld.mt.ty),
@@ -1819,6 +1841,16 @@ fn iter_structural_ty_full(cx: &@block_ctxt, av: ValueRef, t: &ty::t,
             i += 1;
         }
       }
+      ty::ty_tup(args) {
+        let i = 0;
+        for arg in args {
+            r = GEP_tup_like(r.bcx, t, av, ~[0, i]);
+            let llfld_a = r.val;
+            r = f(r.bcx, load_if_immediate(r.bcx, llfld_a, arg.ty),
+                  arg.ty);
+            i += 1;
+        }
+      }
       ty::ty_res(_, inner, tps) {
         let tcx = bcx_tcx(cx);
         let inner1 = ty::substitute_type_params(tcx, tps, inner);
@@ -4737,6 +4769,26 @@ fn trans_call(cx: &@block_ctxt, f: &@ast::expr,
     ret rslt(bcx, retval);
 }
 
+fn trans_tup(cx: &@block_ctxt, elts: &[ast::elt], id: ast::node_id)
+    -> result {
+    let bcx = cx;
+    let t = node_id_type(bcx.fcx.lcx.ccx, id);
+    let tup_res = alloc_ty(bcx, t);
+    let tup_val = tup_res.val;
+    bcx = tup_res.bcx;
+    add_clean_temp(cx, tup_val, t);
+    let i: int = 0;
+    for e in elts {
+        let e_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, e.expr);
+        let src = trans_lval(bcx, e.expr);
+        bcx = src.res.bcx;
+        let dst_res = GEP_tup_like(bcx, t, tup_val, ~[0, i]);
+        bcx = move_val_if_temp(dst_res.bcx, INIT, dst_res.val, src, e_ty).bcx;
+        i += 1;
+    }
+    ret rslt(bcx, tup_val);
+}
+
 fn trans_vec(cx: &@block_ctxt, args: &[@ast::expr], id: ast::node_id) ->
    result {
     let t = node_id_type(bcx_ccx(cx), id);
@@ -5085,6 +5137,7 @@ fn trans_expr_out(cx: &@block_ctxt, e: &@ast::expr, output: out_method) ->
         ret trans_ivec(cx, args, e.id);
       }
       ast::expr_rec(args, base) { ret trans_rec(cx, args, base, e.id); }
+      ast::expr_tup(args) { ret trans_tup(cx, args, e.id); }
       ast::expr_mac(_) { ret bcx_ccx(cx).sess.bug("unexpanded macro"); }
       ast::expr_fail(expr) { ret trans_fail_expr(cx, some(e.span), expr); }
       ast::expr_log(lvl, a) { ret trans_log(lvl, cx, a); }
diff --git a/src/comp/middle/trans_comm.rs b/src/comp/middle/trans_comm.rs
index 272da125128..343fb055e08 100644
--- a/src/comp/middle/trans_comm.rs
+++ b/src/comp/middle/trans_comm.rs
@@ -174,9 +174,9 @@ fn mk_spawn_wrapper(cx: &@block_ctxt, func: &@ast::expr, args_ty: &ty::t) ->
     // unpack the arguments
 
     alt ty::struct(fcx.lcx.ccx.tcx, args_ty) {
-      ty::ty_rec(fields) {
+      ty::ty_tup(elts) {
         let i = 0;
-        for f: ty::field  in fields {
+        for elt in elts {
             let src = fbcx.build.GEP(arg, ~[C_int(0), C_int(i)]);
             i += 1;
             let child_arg = fbcx.build.Load(src);
diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs
index 338d514011d..4f651da9f8f 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -404,6 +404,9 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
         alt maybe_base { none. {/* no-op */ } some(b) { es += ~[b]; } }
         find_pre_post_exprs(fcx, es, e.id);
       }
+      expr_tup(elts) {
+        find_pre_post_exprs(fcx, elt_exprs(elts), e.id);
+      }
       expr_move(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_move); }
       expr_swap(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_swap); }
       expr_assign(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_assign); }
diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs
index e1ceb062524..d3b5701dd52 100644
--- a/src/comp/middle/tstate/states.rs
+++ b/src/comp/middle/tstate/states.rs
@@ -388,6 +388,12 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
         }
         ret changed;
       }
+      expr_tup(elts) {
+        ret find_pre_post_state_exprs(fcx, pres, e.id,
+                                      ivec::init_elt(init_assign,
+                                                     ivec::len(elts)),
+                                      elt_exprs(elts), return);
+      }
       expr_move(lhs, rhs) {
         ret find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, oper_move);
       }
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 37dcce69a50..b0d0ab7b7e9 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -47,6 +47,7 @@ export expr_ty_params_and_ty;
 export fold_ty;
 export field;
 export field_idx;
+export field_num;
 export fm_general;
 export get_element_type;
 export hash_ty;
@@ -69,6 +70,7 @@ export mk_float;
 export mk_fn;
 export mk_imm_box;
 export mk_mut_ptr;
+export mk_imm_tup;
 export mk_imm_vec;
 export mk_int;
 export mk_istr;
@@ -83,10 +85,10 @@ export mk_param;
 export mk_port;
 export mk_ptr;
 export mk_rec;
-export mk_imm_tup;
 export mk_str;
 export mk_tag;
 export mk_task;
+export mk_tup;
 export mk_type;
 export mk_uint;
 export mk_uniq;
@@ -146,6 +148,7 @@ export ty_rec;
 export ty_str;
 export ty_tag;
 export ty_task;
+export ty_tup;
 export ty_type;
 export ty_uint;
 export ty_uniq;
@@ -279,6 +282,7 @@ tag sty {
     ty_native_fn(ast::native_abi, [arg], t);
     ty_obj([method]);
     ty_res(def_id, t, [t]);
+    ty_tup([mt]);
     ty_var(int); // type variable
 
     ty_param(uint, ast::kind); // fn/tag type param
@@ -301,6 +305,8 @@ tag type_err {
     terr_controlflow_mismatch;
     terr_box_mutability;
     terr_vec_mutability;
+    terr_tuple_size(uint, uint);
+    terr_tuple_mutability;
     terr_record_size(uint, uint);
     terr_record_mutability;
     terr_record_fields(ast::ident, ast::ident);
@@ -485,6 +491,11 @@ fn mk_raw_ty(cx: &ctxt, st: &sty, in_cname: &option::t[str]) -> @raw_t {
             derive_flags_mt(cx, has_params, has_vars, f.mt);
         }
       }
+      ty_tup(mts) {
+        for m in mts {
+            derive_flags_mt(cx, has_params, has_vars, m);
+        }
+      }
       ty_fn(_, args, tt, _, _) {
         derive_flags_sig(cx, has_params, has_vars, args, tt);
       }
@@ -590,20 +601,19 @@ fn mk_task(cx: &ctxt) -> t { ret gen_ty(cx, ty_task); }
 
 fn mk_rec(cx: &ctxt, fs: &[field]) -> t { ret gen_ty(cx, ty_rec(fs)); }
 
-fn mk_imm_tup(cx: &ctxt, tys: &[t]) -> t {
-    let fields = ~[];
-    let i = 0u;
-    for typ: t  in tys {
-        fields += ~[{ident: #fmt("_%u", i), mt: {ty: typ, mut: ast::imm}}];
-        i += 1u;
-    }
-    ret gen_ty(cx, ty_rec(fields));
-}
-
 fn mk_constr(cx: &ctxt, t: &t, cs: &[@type_constr]) -> t {
     ret gen_ty(cx, ty_constr(t, cs));
 }
 
+fn mk_tup(cx: &ctxt, tms: &[mt]) -> t { ret gen_ty(cx, ty_tup(tms)); }
+
+fn mk_imm_tup(cx: &ctxt, tys: &[t]) -> t {
+    // TODO: map
+    let mts = ~[];
+    for typ in tys { mts += ~[{ty: typ, mut: ast::imm}]; }
+    ret mk_tup(cx, mts);
+}
+
 fn mk_fn(cx: &ctxt, proto: &ast::proto, args: &[arg], ty: &t,
          cf: &controlflow, constrs: &[@constr]) -> t {
     ret gen_ty(cx, ty_fn(proto, args, ty, cf, constrs));
@@ -677,6 +687,9 @@ fn walk_ty(cx: &ctxt, walker: ty_walk, ty: t) {
       ty_rec(fields) {
         for fl: field  in fields { walk_ty(cx, walker, fl.mt.ty); }
       }
+      ty_tup(mts) {
+        for tm in mts { walk_ty(cx, walker, tm.ty); }
+      }
       ty_fn(proto, args, ret_ty, _, _) {
         for a: arg  in args { walk_ty(cx, walker, a.ty); }
         walk_ty(cx, walker, ret_ty);
@@ -762,6 +775,14 @@ fn fold_ty(cx: &ctxt, fld: fold_mode, ty_0: t) -> t {
         }
         ty = copy_cname(cx, mk_rec(cx, new_fields), ty);
       }
+      ty_tup(mts) {
+        let new_mts = ~[];
+        for tm in mts {
+            let new_subty = fold_ty(cx, fld, tm.ty);
+            new_mts += ~[{ty: new_subty, mut: tm.mut}];
+        }
+        ty = copy_cname(cx, mk_tup(cx, new_mts), ty);
+      }
       ty_fn(proto, args, ret_ty, cf, constrs) {
         let new_args: [arg] = ~[];
         for a: arg  in args {
@@ -861,6 +882,7 @@ fn type_is_task(cx: &ctxt, ty: &t) -> bool {
 fn type_is_structural(cx: &ctxt, ty: &t) -> bool {
     alt struct(cx, ty) {
       ty_rec(_) { ret true; }
+      ty_tup(_) { ret true; }
       ty_tag(_, _) { ret true; }
       ty_fn(_, _, _, _, _) { ret true; }
       ty_obj(_) { ret true; }
@@ -925,6 +947,7 @@ fn type_is_tup_like(cx: &ctxt, ty: &t) -> bool {
     alt struct(cx, ty) {
       ty_box(_) { ret true; }
       ty_rec(_) { ret true; }
+      ty_tup(_) { ret true; }
       ty_tag(_, _) { ret true; }
       _ { ret false; }
     }
@@ -933,6 +956,7 @@ fn type_is_tup_like(cx: &ctxt, ty: &t) -> bool {
 fn get_element_type(cx: &ctxt, ty: &t, i: uint) -> t {
     alt struct(cx, ty) {
       ty_rec(flds) { ret flds.(i).mt.ty; }
+      ty_tup(mts) { ret mts.(i).ty; }
       _ {
         cx.sess.bug("get_element_type called on type " + ty_to_str(cx, ty) +
                         " - expected a \
@@ -1006,6 +1030,11 @@ fn type_has_pointers(cx: &ctxt, ty: &t) -> bool {
             }
         }
       }
+      ty_tup(elts) {
+        for m in elts {
+            if type_has_pointers(cx, m.ty) { result = true; }
+        }
+      }
       ty_tag(did, tps) {
         let variants = tag_variants(cx, did);
         for variant: variant_info  in variants {
@@ -1196,6 +1225,14 @@ fn type_has_dynamic_size(cx: &ctxt, ty: &t) -> bool {
         }
         ret false;
       }
+      ty_tup(mts) {
+        let i = 0u;
+        while i < ivec::len(mts) {
+            if type_has_dynamic_size(cx, mts.(i).ty) { ret true; }
+            i += 1u;
+        }
+        ret false;
+      }
       ty_fn(_, _, _, _, _) { ret false; }
       ty_native_fn(_, _, _) { ret false; }
       ty_obj(_) { ret false; }
@@ -1317,12 +1354,16 @@ fn type_owns_heap_mem(cx: &ctxt, ty: &t) -> bool {
             if type_owns_heap_mem(cx, f.mt.ty) { result = true; }
         }
       }
+      ty_tup(elts) {
+        for m in elts {
+            if type_owns_heap_mem(cx, m.ty) { result = true; }
+        }
+      }
       ty_res(_, inner, tps) {
         result =
             type_owns_heap_mem(cx, substitute_type_params(cx, tps, inner));
       }
 
-
       ty_ptr(_) {
         result = false;
       }
@@ -1368,6 +1409,11 @@ fn type_is_pod(cx : &ctxt, ty : &t) -> bool {
                 if !type_is_pod(cx, f.mt.ty) { result = false; }
             }
         }
+        ty_tup(elts) {
+            for elt in elts {
+                if !type_is_pod(cx, elt.ty) { result = false; }
+            }
+        }
         ty_res(_, inner, tps) {
             result = type_is_pod(cx,
                 substitute_type_params(cx, tps, inner));
@@ -1517,6 +1563,11 @@ fn hash_type_structure(st: &sty) -> uint {
         for f: field  in fields { h += h << 5u + hash_ty(f.mt.ty); }
         ret h;
       }
+      ty_tup(mts) {
+        let h = 25u;
+        for tm in mts { h += h << 5u + hash_ty(tm.ty); }
+        ret h;
+      }
 
       // ???
       ty_fn(_, args, rty, _, _) {
@@ -1698,6 +1749,22 @@ fn equal_type_structures(a: &sty, b: &sty) -> bool {
           _ { ret false; }
         }
       }
+      ty_tup(mts_a) {
+        alt (b) {
+          ty_tup(mts_b) {
+            let len = ivec::len(mts_a);
+            if len != ivec::len(mts_b) { ret false; }
+            let i = 0u;
+            while i < len {
+                if !equal_mt(mts_a.(i), mts_b.(i)) { ret false; }
+                i += 1u;
+            }
+            ret true;
+          }
+          _ { ret false; }
+        }
+      }
+
       ty_fn(p_a, args_a, rty_a, cf_a, constrs_a) {
         alt b {
           ty_fn(p_b, args_b, rty_b, cf_b, constrs_b) {
@@ -2659,6 +2726,44 @@ mod unify {
               _ { ret ures_err(terr_mismatch); }
             }
           }
+          ty::ty_tup(expected_elems) {
+            alt struct(cx.tcx, actual) {
+              ty::ty_tup(actual_elems) {
+                let expected_len = ivec::len(expected_elems);
+                let actual_len = ivec::len(actual_elems);
+                if (expected_len != actual_len) {
+                    let err = terr_tuple_size(expected_len, actual_len);
+                    ret ures_err(err);
+                }
+                // TODO: implement an iterator that can iterate over
+                // two arrays simultaneously.
+
+                let result_elems = ~[];
+                let i = 0u;
+                while i < expected_len {
+                    let expected_elem = expected_elems.(i);
+                    let actual_elem = actual_elems.(i);
+                    let mut;
+                    alt unify_mut(expected_elem.mut, actual_elem.mut) {
+                      none. { ret ures_err(terr_tuple_mutability); }
+                      some(m) { mut = m; }
+                    }
+                    let result = unify_step(cx, expected_elem.ty,
+                                            actual_elem.ty);
+                    alt result {
+                      ures_ok(rty) {
+                        let mt = {ty: rty, mut: mut};
+                        result_elems += ~[mt];
+                      }
+                      _ { ret result; }
+                    }
+                    i += 1u;
+                }
+                ret ures_ok(mk_tup(cx.tcx, result_elems));
+              }
+              _ { ret ures_err(terr_mismatch); }
+            }
+          }
           ty::ty_fn(ep, expected_inputs, expected_output, expected_cf,
                     expected_constrs) {
             alt struct(cx.tcx, actual) {
@@ -2802,6 +2907,14 @@ fn type_err_to_str(err: &ty::type_err) -> str {
       }
       terr_box_mutability. { ret "boxed values differ in mutability"; }
       terr_vec_mutability. { ret "vectors differ in mutability"; }
+      terr_tuple_size(e_sz, a_sz) {
+        ret "expected a tuple with " + uint::to_str(e_sz, 10u) +
+            " elements but found one with " + uint::to_str(a_sz, 10u)
+            + " elements";
+      }
+      terr_tuple_mutability. {
+        ret "tuple elements differ in mutability";
+      }
       terr_record_size(e_sz, a_sz) {
         ret "expected a record with " + uint::to_str(e_sz, 10u) +
                 " fields but found one with " + uint::to_str(a_sz, 10u) +
@@ -3044,6 +3157,7 @@ fn is_binopable(cx: &ctxt, ty: t, op: ast::binop) -> bool {
           ty_vec(_) { tycat_vec }
           ty_ivec(_) { tycat_vec }
           ty_rec(_) { tycat_struct }
+          ty_tup(_) { tycat_struct }
           ty_tag(_, _) { tycat_struct }
           ty_bot.    { tycat_bot }
           _ { tycat_other }
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index 7484fd92996..64b900f8053 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -339,6 +339,10 @@ fn ast_ty_to_ty(tcx: &ty::ctxt, getter: &ty_getter, ast_ty: &@ast::ty) ->
       ast::ty_chan(t) {
         typ = ty::mk_chan(tcx, ast_ty_to_ty(tcx, getter, t));
       }
+      ast::ty_tup(fields) {
+        let flds = ivec::map(bind ast_mt_to_mt(tcx, getter, _), fields);
+        typ = ty::mk_tup(tcx, flds);
+      }
       ast::ty_rec(fields) {
         let flds: [field] = ~[];
         for f: ast::ty_field  in fields {
@@ -2160,7 +2164,7 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr,
         write::ty_only_fixup(fcx, id, t_1);
       }
       ast::expr_vec(args, mut, kind) {
-        let t: ty::t = next_ty_var(fcx);;
+        let t: ty::t = next_ty_var(fcx);
         for e: @ast::expr in args {
             bot |= check_expr_with(fcx, e, t);
         }
@@ -2173,6 +2177,17 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr,
         }
         write::ty_only_fixup(fcx, id, typ);
       }
+      ast::expr_tup(elts) {
+        let elts_mt = ~[];
+        ivec::reserve(elts_mt, ivec::len(elts));
+        for e in elts {
+            check_expr(fcx, e.expr);
+            let ety = expr_ty(fcx.ccx.tcx, e.expr);
+            elts_mt += ~[{ty: ety, mut: e.mut}];
+        }
+        let typ = ty::mk_tup(fcx.ccx.tcx, elts_mt);
+        write::ty_only_fixup(fcx, id, typ);
+      }
       ast::expr_rec(fields, base) {
         alt base { none. {/* no-op */ } some(b_0) { check_expr(fcx, b_0); } }
         let fields_t: [spanned[field]] = ~[];
diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs
index 4c82771f69d..4052c5cc1d8 100644
--- a/src/comp/syntax/ast.rs
+++ b/src/comp/syntax/ast.rs
@@ -292,6 +292,7 @@ tag expr_ {
     expr_vec([@expr], mutability, seq_kind);
     expr_rec([field], option::t[@expr]);
     expr_call(@expr, [@expr]);
+    expr_tup([elt]);
     expr_self_method(ident);
     expr_bind(@expr, [option::t[@expr]]);
     expr_spawn(spawn_dom, option::t[str], @expr, [@expr]);
@@ -447,6 +448,7 @@ tag ty_ {
     ty_rec([ty_field]);
     ty_fn(proto, [ty_arg], @ty, controlflow, [@constr]);
     ty_obj([ty_method]);
+    ty_tup([mt]);
     ty_path(path, node_id);
     ty_type;
     ty_constr(@ty, [@ty_constr]);
diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs
index aa0f45be997..4819e893bdb 100644
--- a/src/comp/syntax/fold.rs
+++ b/src/comp/syntax/fold.rs
@@ -338,6 +338,13 @@ fn noop_fold_expr(e: &expr_, fld: ast_fold) -> expr_ {
             expr_rec(ivec::map(fold_field, fields),
                      option::map(fld.fold_expr, maybe_expr))
           }
+          expr_tup(elts) {
+            let elts_ = ~[];
+            for elt in elts {
+                elts_ += ~[{mut: elt.mut, expr: fld.fold_expr(elt.expr)}];
+            }
+            expr_tup(elts_)
+          }
           expr_call(f, args) {
             expr_call(fld.fold_expr(f), fld.map_exprs(fld.fold_expr, args))
           }
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index 39843072c2a..58c7b618ec3 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -1569,6 +1569,7 @@ fn stmt_ends_with_semi(stmt: &ast::stmt) -> bool {
         ret alt e.node {
               ast::expr_vec(_, _, _) { true }
               ast::expr_rec(_, _) { true }
+              ast::expr_tup(_) { true }
               ast::expr_call(_, _) { true }
               ast::expr_self_method(_) { false }
               ast::expr_bind(_, _) { true }
diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs
index 45fd48bcd86..deae225a221 100644
--- a/src/comp/syntax/print/pprust.rs
+++ b/src/comp/syntax/print/pprust.rs
@@ -322,6 +322,11 @@ fn print_type(s: &ps, ty: &ast::ty) {
         commasep_cmnt(s, consistent, fields, print_field, get_span);
         word(s.s, "}");
       }
+      ast::ty_tup(elts) {
+          popen(s);
+          commasep(s, inconsistent, elts, print_mt);
+          pclose(s);
+      }
       ast::ty_fn(proto, inputs, output, cf, constrs) {
         print_ty_fn(s, proto, none[str], inputs, output, cf, constrs);
       }
@@ -713,6 +718,7 @@ fn print_expr(s: &ps, expr: &@ast::expr) {
         alt kind {
           ast::sk_rc. { word(s.s, "["); }
           ast::sk_unique. { word(s.s, "~["); }
+
         }
         if mut == ast::mut {
             word(s.s, "mutable");
@@ -746,6 +752,18 @@ fn print_expr(s: &ps, expr: &@ast::expr) {
         }
         word(s.s, "}");
       }
+      ast::expr_tup(exprs) {
+        fn printElt(s: &ps, elt: &ast::elt) {
+            ibox(s, indent_unit);
+            if elt.mut == ast::mut { word_nbsp(s, "mutable"); }
+            print_expr(s, elt.expr);
+            end(s);
+        }
+        fn get_span(elt: &ast::elt) -> codemap::span { ret elt.expr.span; }
+        popen(s);
+        commasep_cmnt(s, inconsistent, exprs, printElt, get_span);
+        pclose(s);
+      }
       ast::expr_call(func, args) {
         print_expr_parens_if_unary(s, func);
         popen(s);
diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs
index f7e7c3b352d..4b1f709a9fc 100644
--- a/src/comp/syntax/visit.rs
+++ b/src/comp/syntax/visit.rs
@@ -133,6 +133,9 @@ fn visit_ty[E](t: &@ty, e: &E, v: &vt[E]) {
       ty_rec(flds) {
         for f: ty_field  in flds { v.visit_ty(f.node.mt.ty, e, v); }
       }
+      ty_tup(mts) {
+        for mt in mts { v.visit_ty(mt.ty, e, v); }
+      }
       ty_fn(_, args, out, _, constrs) {
         for a: ty_arg  in args { v.visit_ty(a.node.ty, e, v); }
         for c: @constr  in constrs {
@@ -244,6 +247,9 @@ fn visit_expr[E](ex: &@expr, e: &E, v: &vt[E]) {
         for f: field  in flds { v.visit_expr(f.node.expr, e, v); }
         visit_expr_opt(base, e, v);
       }
+      expr_tup(elts) {
+        for el in elts { v.visit_expr(el.expr, e, v); }
+      }
       expr_call(callee, args) {
         v.visit_expr(callee, e, v);
         visit_exprs(args, e, v);
diff --git a/src/comp/util/ppaux.rs b/src/comp/util/ppaux.rs
index 2873bfa7ccd..eadcb1422d0 100644
--- a/src/comp/util/ppaux.rs
+++ b/src/comp/util/ppaux.rs
@@ -103,6 +103,11 @@ fn ty_to_str(cx: &ctxt, typ: &t) -> str {
         for fld: field  in elems { strs += ~[field_to_str(cx, fld)]; }
         s += "{" + str::connect(strs, ",") + "}";
       }
+      ty_tup(elems) {
+        let strs = ~[];
+        for tm in elems { strs += ~[mt_to_str(cx, tm)]; }
+        s += "(" + str::connect(strs, ",") + ")";
+      }
       ty_tag(id, tps) {
         // The user should never see this if the cname is set properly!