about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorStefan Plantikow <stefan.plantikow@googlemail.com>2012-01-05 00:11:25 +0100
committerStefan Plantikow <stefan.plantikow@googlemail.com>2012-01-05 02:07:12 +0100
commit6284190ef9918e05cb9147a2a81100ddcb06fea8 (patch)
treee5e8075a4de03db9c4efee2b6d994343a674b3b5 /src
parent16405386f0a843167e234d8d54855a537b0f261d (diff)
parent3971b520bcdd556ff78120c77ffd13785e1c3695 (diff)
Merge branch 'master' into kmath
Conflicts:
	src/libcore/cmath.rs
Diffstat (limited to 'src')
-rw-r--r--src/comp/back/link.rs15
-rw-r--r--src/comp/back/rpath.rs10
-rw-r--r--src/comp/back/x86.rs6
-rw-r--r--src/comp/back/x86_64.rs8
-rw-r--r--src/comp/driver/driver.rs10
-rw-r--r--src/comp/driver/session.rs2
-rw-r--r--src/comp/lib/llvm.rs3
-rw-r--r--src/comp/metadata/creader.rs3
-rw-r--r--src/comp/metadata/csearch.rs1
-rw-r--r--src/comp/metadata/decoder.rs15
-rw-r--r--src/comp/metadata/encoder.rs2
-rw-r--r--src/comp/metadata/tydecode.rs17
-rw-r--r--src/comp/metadata/tyencode.rs13
-rw-r--r--src/comp/middle/kind.rs21
-rw-r--r--src/comp/middle/last_use.rs27
-rw-r--r--src/comp/middle/resolve.rs103
-rw-r--r--src/comp/middle/shape.rs2
-rw-r--r--src/comp/middle/trans.rs380
-rw-r--r--src/comp/middle/trans_closure.rs137
-rw-r--r--src/comp/middle/trans_common.rs32
-rw-r--r--src/comp/middle/trans_impl.rs172
-rw-r--r--src/comp/middle/trans_objects.rs10
-rw-r--r--src/comp/middle/tstate/auxiliary.rs8
-rw-r--r--src/comp/middle/ty.rs119
-rw-r--r--src/comp/middle/typeck.rs544
-rw-r--r--src/comp/rustc.rc1
-rw-r--r--src/comp/syntax/parse/eval.rs4
-rw-r--r--src/comp/syntax/parse/parser.rs207
-rw-r--r--src/comp/syntax/print/pprust.rs32
-rw-r--r--src/comp/syntax/visit.rs23
-rw-r--r--src/comp/util/ppaux.rs5
-rw-r--r--src/compiletest/procsrv.rs1
-rw-r--r--src/compiletest/runtest.rs1
-rw-r--r--src/compiletest/util.rs2
-rw-r--r--src/etc/snapshot.py10
-rw-r--r--src/libcore/char.rs32
-rw-r--r--src/libcore/cmath.rs38
-rw-r--r--src/libcore/either.rs18
-rw-r--r--src/libcore/f32.rs59
-rw-r--r--src/libcore/f64.rs58
-rw-r--r--src/libcore/option.rs6
-rw-r--r--src/libcore/result.rs4
-rw-r--r--src/libcore/str.rs47
-rw-r--r--src/libcore/vec.rs11
-rw-r--r--src/libstd/deque.rs2
-rw-r--r--src/libstd/freebsd_os.rs155
-rw-r--r--src/libstd/fs.rs4
-rw-r--r--src/libstd/generic_os.rs4
-rw-r--r--src/libstd/list.rs35
-rw-r--r--src/libstd/rope.rs2
-rw-r--r--src/libstd/run_program.rs3
-rw-r--r--src/libstd/std.rc7
-rw-r--r--src/libstd/util.rs2
-rw-r--r--src/libstd/uv.rs5
m---------src/llvm0
-rw-r--r--src/rt/arch/i386/ccall.S10
-rw-r--r--src/rt/arch/i386/morestack.S14
-rw-r--r--src/rt/arch/i386/record_sp.S2
-rw-r--r--src/rt/arch/x86_64/ccall.S8
-rw-r--r--src/rt/arch/x86_64/context.h4
-rw-r--r--src/rt/arch/x86_64/morestack.S10
-rw-r--r--src/rt/arch/x86_64/record_sp.S8
-rw-r--r--src/rt/rust_abi.cpp2
-rw-r--r--src/rt/rust_task.cpp10
-rw-r--r--src/rt/rust_upcall.cpp2
-rw-r--r--src/test/compile-fail/binop-add-ptr.rs2
-rw-r--r--src/test/compile-fail/block-must-not-have-result-do.rs2
-rw-r--r--src/test/compile-fail/block-must-not-have-result-for.rs2
-rw-r--r--src/test/compile-fail/block-must-not-have-result-res.rs2
-rw-r--r--src/test/compile-fail/block-must-not-have-result-while.rs2
-rw-r--r--src/test/compile-fail/forgot-ret.rs2
-rw-r--r--src/test/compile-fail/if-without-else-result.rs2
-rw-r--r--src/test/compile-fail/missing-return2.rs2
-rw-r--r--src/test/compile-fail/native-type-mismatch.rs2
-rw-r--r--src/test/pretty/disamb-stmt-expr.rs8
-rw-r--r--src/test/run-pass/block-arg-as-stmt.rs25
-rw-r--r--src/test/run-pass/block-arg-can-be-followed-by-binop.rs8
-rw-r--r--src/test/run-pass/block-arg-can-be-followed-by-block-arg.rs6
-rw-r--r--src/test/run-pass/block-arg-can-be-followed-by-call.rs6
-rw-r--r--src/test/run-pass/block-arg-in-parentheses.rs23
-rw-r--r--src/test/run-pass/block-arg-in-ternary.rs6
-rw-r--r--src/test/run-pass/block-arg.rs52
-rw-r--r--src/test/run-pass/dupe-first-attr.rc5
-rw-r--r--src/test/run-pass/early-ret-binop-add.rs2
-rw-r--r--src/test/run-pass/early-ret-binop.rs2
-rw-r--r--src/test/run-pass/expr-alt-generic-box2.rs2
-rw-r--r--src/test/run-pass/expr-alt-generic-unique1.rs2
-rw-r--r--src/test/run-pass/expr-alt-generic-unique2.rs2
-rw-r--r--src/test/run-pass/expr-alt-generic.rs2
-rw-r--r--src/test/run-pass/expr-fn.rs2
-rw-r--r--src/test/run-pass/iface-generic.rs35
-rw-r--r--src/test/run-pass/iface-to-str.rs28
-rw-r--r--src/test/run-pass/issue-970.rs6
-rw-r--r--src/test/run-pass/last-use-is-capture.rs8
-rw-r--r--src/test/run-pass/non-boolean-pure-fns.rs5
-rw-r--r--src/test/run-pass/unchecked-predicates.rs12
-rw-r--r--src/test/run-pass/x86stdcall.rs1
-rw-r--r--src/test/run-pass/x86stdcall2.rs1
-rw-r--r--src/test/stdtest/char.rs16
-rw-r--r--src/test/stdtest/list.rs43
-rw-r--r--src/test/stdtest/os.rs7
-rw-r--r--src/test/stdtest/str.rs6
-rw-r--r--src/test/stdtest/uv.rs3
103 files changed, 2095 insertions, 755 deletions
diff --git a/src/comp/back/link.rs b/src/comp/back/link.rs
index 98917342c2d..8950618c2e6 100644
--- a/src/comp/back/link.rs
+++ b/src/comp/back/link.rs
@@ -34,7 +34,7 @@ fn llvm_err(sess: session::session, msg: str) unsafe {
     let buf = llvm::LLVMRustGetLastError();
     if buf == ptr::null() {
         sess.fatal(msg);
-    } else { sess.fatal(msg + ": " + str::str_from_cstr(buf)); }
+    } else { sess.fatal(msg + ": " + str::from_cstr(buf)); }
 }
 
 fn load_intrinsics_bc(sess: session::session) -> option::t<ModuleRef> {
@@ -566,7 +566,8 @@ fn link_binary(sess: session::session,
         let rmlib =
             bind fn (config: @session::config, filename: str) -> str {
                      if config.os == session::os_macos ||
-                            config.os == session::os_linux &&
+                            (config.os == session::os_linux ||
+                             config.os == session::os_freebsd) &&
                                 str::find(filename, "lib") == 0 {
                          ret str::slice(filename, 3u,
                                         str::byte_len(filename));
@@ -580,6 +581,7 @@ fn link_binary(sess: session::session,
         ret alt config.os {
               session::os_macos. { rmext(rmlib(filename)) }
               session::os_linux. { rmext(rmlib(filename)) }
+              session::os_freebsd. { rmext(rmlib(filename)) }
               _ { rmext(filename) }
             };
     }
@@ -657,6 +659,15 @@ fn link_binary(sess: session::session,
         gcc_args += ["-lrt", "-ldl"];
     }
 
+    if sess.get_targ_cfg().os == session::os_freebsd {
+        gcc_args += ["-lrt", "-L/usr/local/lib", "-lexecinfo",
+                     "-L/usr/local/lib/gcc46",
+                     "-L/usr/local/lib/gcc44", "-lstdc++",
+                     "-Wl,-z,origin",
+                     "-Wl,-rpath,/usr/local/lib/gcc46",
+                     "-Wl,-rpath,/usr/local/lib/gcc44"];
+    }
+
     // OS X 10.6 introduced 'compact unwind info', which is produced by the
     // linker from the dwarf unwind info. Unfortunately, it does not seem to
     // understand how to unwind our __morestack frame, so we have to turn it
diff --git a/src/comp/back/rpath.rs b/src/comp/back/rpath.rs
index d7eaa083bfb..fccacdcba80 100644
--- a/src/comp/back/rpath.rs
+++ b/src/comp/back/rpath.rs
@@ -105,6 +105,7 @@ fn get_rpath_relative_to_output(os: session::os,
     // Mac doesn't appear to support $ORIGIN
     let prefix = alt os {
         session::os_linux. { "$ORIGIN" + fs::path_sep() }
+        session::os_freebsd. { "$ORIGIN" + fs::path_sep() }
         session::os_macos. { "@executable_path" + fs::path_sep() }
     };
 
@@ -191,6 +192,7 @@ fn minimize_rpaths(rpaths: [str]) -> [str] {
 
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
 #[cfg(test)]
 mod test {
     #[test]
@@ -316,6 +318,14 @@ mod test {
     }
 
     #[test]
+    #[cfg(target_os = "freebsd")]
+    fn test_rpath_relative() {
+        let res = get_rpath_relative_to_output(session::os_freebsd,
+            "/usr", "bin/rustc", "lib/libstd.so");
+        assert res == "$ORIGIN/../lib";
+    }
+
+    #[test]
     #[cfg(target_os = "macos")]
     fn test_rpath_relative() {
         let res = get_rpath_relative_to_output(session::os_macos,
diff --git a/src/comp/back/x86.rs b/src/comp/back/x86.rs
index 1e5b61739d4..6e668b4c967 100644
--- a/src/comp/back/x86.rs
+++ b/src/comp/back/x86.rs
@@ -8,6 +8,7 @@ fn get_target_strs(target_os: session::os) -> target_strs::t {
           session::os_macos. { "__DATA,__note.rustc" }
           session::os_win32. { ".note.rustc" }
           session::os_linux. { ".note.rustc" }
+          session::os_freebsd. { ".note.rustc" }
         },
 
         data_layout: alt target_os {
@@ -24,12 +25,17 @@ fn get_target_strs(target_os: session::os) -> target_strs::t {
           session::os_linux. {
             "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32"
           }
+
+          session::os_freebsd. {
+            "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32"
+          }
         },
 
         target_triple: alt target_os {
           session::os_macos. { "i686-apple-darwin" }
           session::os_win32. { "i686-pc-mingw32" }
           session::os_linux. { "i686-unknown-linux-gnu" }
+          session::os_freebsd. { "i686-unknown-freebsd" }
         },
 
         gcc_args: ["-m32"]
diff --git a/src/comp/back/x86_64.rs b/src/comp/back/x86_64.rs
index 98d33d0a8b7..b02910be489 100644
--- a/src/comp/back/x86_64.rs
+++ b/src/comp/back/x86_64.rs
@@ -8,6 +8,7 @@ fn get_target_strs(target_os: session::os) -> target_strs::t {
           session::os_macos. { "__DATA,__note.rustc" }
           session::os_win32. { ".note.rustc" }
           session::os_linux. { ".note.rustc" }
+          session::os_freebsd. { ".note.rustc" }
         },
 
         data_layout: alt target_os {
@@ -29,12 +30,19 @@ fn get_target_strs(target_os: session::os) -> target_strs::t {
                 "f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+
                 "s0:64:64-f80:128:128-n8:16:32:64-S128"
           }
+
+          session::os_freebsd. {
+            "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+
+                "f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+
+                "s0:64:64-f80:128:128-n8:16:32:64-S128"
+          }
         },
 
         target_triple: alt target_os {
           session::os_macos. { "x86_64-apple-darwin" }
           session::os_win32. { "x86_64-pc-mingw32" }
           session::os_linux. { "x86_64-unknown-linux-gnu" }
+          session::os_freebsd. { "x86_64-unknown-freebsd" }
         },
 
         gcc_args: ["-m64"]
diff --git a/src/comp/driver/driver.rs b/src/comp/driver/driver.rs
index d67b7bd7cf3..b8a6ba28729 100644
--- a/src/comp/driver/driver.rs
+++ b/src/comp/driver/driver.rs
@@ -26,6 +26,7 @@ fn default_configuration(sess: session::session, argv0: str, input: str) ->
           session::os_win32. { "msvcrt.dll" }
           session::os_macos. { "libc.dylib" }
           session::os_linux. { "libc.so.6" }
+          session::os_freebsd. { "libc.so.7" }
           _ { "libc.so" }
         };
 
@@ -172,8 +173,9 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str,
     time(time_passes, "const checking",
          bind middle::check_const::check_crate(sess, crate));
     let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars);
-    let method_map = time(time_passes, "typechecking",
-                          bind typeck::check_crate(ty_cx, impl_map, crate));
+    let (method_map, dict_map) =
+        time(time_passes, "typechecking",
+             bind typeck::check_crate(ty_cx, impl_map, crate));
     time(time_passes, "block-use checking",
          bind middle::block_use::check_crate(ty_cx, crate));
     time(time_passes, "function usage",
@@ -201,7 +203,7 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str,
              bind trans::trans_crate(sess, crate, ty_cx,
                                      outputs.obj_filename, exp_map, ast_map,
                                      mut_map, copy_map, last_uses,
-                                     method_map));
+                                     method_map, dict_map));
     time(time_passes, "LLVM passes",
          bind link::write::run_passes(sess, llmod, outputs.obj_filename));
 
@@ -294,6 +296,8 @@ fn get_os(triple: str) -> session::os {
             session::os_macos
         } else if str::find(triple, "linux") >= 0 {
             session::os_linux
+        } else if str::find(triple, "freebsd") >= 0 {
+            session::os_freebsd
         } else { early_error("Unknown operating system!") };
 }
 
diff --git a/src/comp/driver/session.rs b/src/comp/driver/session.rs
index 1bedbf87f7e..55f25a14f1d 100644
--- a/src/comp/driver/session.rs
+++ b/src/comp/driver/session.rs
@@ -9,7 +9,7 @@ import syntax::parse::parser::parse_sess;
 import util::filesearch;
 import back::target_strs;
 
-tag os { os_win32; os_macos; os_linux; }
+tag os { os_win32; os_macos; os_linux; os_freebsd; }
 
 tag arch { arch_x86; arch_x86_64; arch_arm; }
 
diff --git a/src/comp/lib/llvm.rs b/src/comp/lib/llvm.rs
index 25617066515..927e9879657 100644
--- a/src/comp/lib/llvm.rs
+++ b/src/comp/lib/llvm.rs
@@ -993,7 +993,8 @@ fn type_to_str_inner(names: type_names, outer0: [TypeRef], ty: TypeRef) ->
       }
       10 {
         let el_ty = llvm::LLVMGetElementType(ty);
-        ret "[" + type_to_str_inner(names, outer, el_ty) + "]";
+        ret "[" + type_to_str_inner(names, outer, el_ty) + " x " +
+            uint::str(llvm::LLVMGetArrayLength(ty)) + "]";
       }
       11 {
         let i: uint = 0u;
diff --git a/src/comp/metadata/creader.rs b/src/comp/metadata/creader.rs
index 618de096773..21ead604d31 100644
--- a/src/comp/metadata/creader.rs
+++ b/src/comp/metadata/creader.rs
@@ -129,6 +129,7 @@ fn default_native_lib_naming(sess: session::session, static: bool) ->
       session::os_win32. { ret {prefix: "", suffix: ".dll"}; }
       session::os_macos. { ret {prefix: "lib", suffix: ".dylib"}; }
       session::os_linux. { ret {prefix: "lib", suffix: ".so"}; }
+      session::os_freebsd. { ret {prefix: "lib", suffix: ".so"}; }
     }
 }
 
@@ -215,7 +216,7 @@ fn get_metadata_section(sess: session::session,
     let si = mk_section_iter(of.llof);
     while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
         let name_buf = llvm::LLVMGetSectionName(si.llsi);
-        let name = unsafe { str::str_from_cstr(name_buf) };
+        let name = unsafe { str::from_cstr(name_buf) };
         if str::eq(name, sess.get_targ_cfg().target_strs.meta_sect_name) {
             let cbuf = llvm::LLVMGetSectionContents(si.llsi);
             let csz = llvm::LLVMGetSectionSize(si.llsi);
diff --git a/src/comp/metadata/csearch.rs b/src/comp/metadata/csearch.rs
index 2603903cf65..3da705f41ef 100644
--- a/src/comp/metadata/csearch.rs
+++ b/src/comp/metadata/csearch.rs
@@ -72,6 +72,7 @@ fn get_impls_for_mod(cstore: cstore::cstore, def: ast::def_id,
         let nm = decoder::lookup_item_name(cdata, did.node);
         if alt name { some(n) { n == nm } none. { true } } {
             result += [@{did: did,
+                         iface_did: none::<ast::def_id>, // FIXME[impl]
                          ident: nm,
                          methods: decoder::lookup_impl_methods(
                              cdata, did.node, did.crate)}];
diff --git a/src/comp/metadata/decoder.rs b/src/comp/metadata/decoder.rs
index adb5e581ae5..49536b6e1c9 100644
--- a/src/comp/metadata/decoder.rs
+++ b/src/comp/metadata/decoder.rs
@@ -117,23 +117,20 @@ fn item_type(item: ebml::doc, this_cnum: ast::crate_num, tcx: ty::ctxt,
 
 fn item_ty_param_bounds(item: ebml::doc, this_cnum: ast::crate_num,
                         tcx: ty::ctxt, extres: external_resolver)
-    -> [@[ty::param_bound]] {
+    -> @[ty::param_bounds] {
     let bounds = [];
     let def_parser = bind parse_external_def_id(this_cnum, extres, _);
     ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) {|p|
         bounds += [tydecode::parse_bounds_data(@ebml::doc_data(p), this_cnum,
                                                def_parser, tcx)];
     }
-    bounds
+    @bounds
 }
 
 fn item_ty_param_count(item: ebml::doc) -> uint {
     let n = 0u;
-    ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) {|p|
-        for byte in ebml::doc_data(p) {
-            if byte as char == '.' { n += 1u; }
-        }
-    }
+    ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds,
+                      {|_p| n += 1u; });
     n
 }
 
@@ -212,8 +209,8 @@ fn get_type(data: @[u8], def: ast::def_id, tcx: ty::ctxt,
     let t = item_type(item, this_cnum, tcx, extres);
     let tp_bounds = if family_has_type_params(item_family(item)) {
         item_ty_param_bounds(item, this_cnum, tcx, extres)
-    } else { [] };
-    ret @{bounds: tp_bounds, ty: t};
+    } else { @[] };
+    ret {bounds: tp_bounds, ty: t};
 }
 
 fn get_type_param_count(data: @[u8], id: ast::node_id) -> uint {
diff --git a/src/comp/metadata/encoder.rs b/src/comp/metadata/encoder.rs
index 60994d3b17c..4776b8af34d 100644
--- a/src/comp/metadata/encoder.rs
+++ b/src/comp/metadata/encoder.rs
@@ -190,7 +190,7 @@ fn encode_type_param_bounds(ebml_w: ebml::writer, ecx: @encode_ctxt,
                         abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)};
     for param in params {
         ebml::start_tag(ebml_w, tag_items_data_item_ty_param_bounds);
-        let bs = ecx.ccx.tcx.ty_param_bounds.get(local_def(param.id));
+        let bs = ecx.ccx.tcx.ty_param_bounds.get(param.id);
         tyencode::enc_bounds(io::new_writer(ebml_w.writer), ty_str_ctxt, bs);
         ebml::end_tag(ebml_w);
     }
diff --git a/src/comp/metadata/tydecode.rs b/src/comp/metadata/tydecode.rs
index 4f4c7f41c5e..1a083bf8fb6 100644
--- a/src/comp/metadata/tydecode.rs
+++ b/src/comp/metadata/tydecode.rs
@@ -202,9 +202,17 @@ fn parse_ty(st: @pstate, sd: str_def) -> ty::t {
         st.pos = st.pos + 1u;
         ret ty::mk_tag(st.tcx, def, params);
       }
+      'x' {
+        assert (next(st) as char == '[');
+        let def = parse_def(st, sd);
+        let params: [ty::t] = [];
+        while peek(st) as char != ']' { params += [parse_ty(st, sd)]; }
+        st.pos = st.pos + 1u;
+        ret ty::mk_iface(st.tcx, def, params);
+      }
       'p' {
-        let bounds = parse_bounds(st, sd);
-        ret ty::mk_param(st.tcx, parse_int(st) as uint, bounds);
+        let did = parse_def(st, sd);
+        ret ty::mk_param(st.tcx, parse_int(st) as uint, did);
       }
       '@' { ret ty::mk_box(st.tcx, parse_mt(st, sd)); }
       '~' { ret ty::mk_uniq(st.tcx, parse_mt(st, sd)); }
@@ -259,7 +267,7 @@ fn parse_ty(st: @pstate, sd: str_def) -> ty::t {
             while peek(st) as char != '[' {
                 name += str::unsafe_from_byte(next(st));
             }
-            methods += [{ident: name, tps: [],
+            methods += [{ident: name, tps: @[],
                          fty: {proto: proto with parse_ty_fn(st, sd)}}];
         }
         st.pos += 1u;
@@ -401,8 +409,7 @@ fn parse_bounds_data(data: @[u8], crate_num: int, sd: str_def, tcx: ty::ctxt)
 
 fn parse_bounds(st: @pstate, sd: str_def) -> @[ty::param_bound] {
     let bounds = [];
-    while peek(st) as char == '.' {
-        next(st);
+    while peek(st) != 0u8 {
         bounds += [alt next(st) as char {
           'S' { ty::bound_send }
           'C' { ty::bound_copy }
diff --git a/src/comp/metadata/tyencode.rs b/src/comp/metadata/tyencode.rs
index 88885153e77..73d44472723 100644
--- a/src/comp/metadata/tyencode.rs
+++ b/src/comp/metadata/tyencode.rs
@@ -126,6 +126,13 @@ 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_iface(def, tys) {
+        w.write_str("x[");
+        w.write_str(cx.ds(def));
+        w.write_char('|');
+        for t: ty::t in tys { enc_ty(w, cx, t); }
+        w.write_char(']');
+      }
       ty::ty_tup(ts) {
         w.write_str("T[");
         for t in ts { enc_ty(w, cx, t); }
@@ -176,9 +183,10 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
         w.write_str(cx.ds(def));
         w.write_char('|');
       }
-      ty::ty_param(id, bounds) {
+      ty::ty_param(id, did) {
         w.write_char('p');
-        enc_bounds(w, cx, bounds);
+        w.write_str(cx.ds(did));
+        w.write_char('|');
         w.write_str(uint::str(id));
       }
       ty::ty_type. { w.write_char('Y'); }
@@ -265,7 +273,6 @@ fn enc_ty_constr(w: io::writer, cx: @ctxt, c: @ty::type_constr) {
 
 fn enc_bounds(w: io::writer, cx: @ctxt, bs: @[ty::param_bound]) {
     for bound in *bs {
-        w.write_char('.');
         alt bound {
           ty::bound_send. { w.write_char('S'); }
           ty::bound_copy. { w.write_char('C'); }
diff --git a/src/comp/middle/kind.rs b/src/comp/middle/kind.rs
index f71112002c4..00d5534bfec 100644
--- a/src/comp/middle/kind.rs
+++ b/src/comp/middle/kind.rs
@@ -44,6 +44,7 @@ fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map,
     let visit = visit::mk_vt(@{
         visit_expr: check_expr,
         visit_stmt: check_stmt,
+        visit_block: check_block,
         visit_fn: check_fn
         with *visit::default_visitor()
     });
@@ -117,12 +118,18 @@ fn check_fn_cap_clause(cx: ctx,
     }
 }
 
-fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
+fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
+    alt b.node.expr {
+      some(ex) { maybe_copy(cx, ex); }
+      _ {}
+    }
+    visit::visit_block(b, cx, v);
+}
 
+fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
     alt e.node {
       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_unary(box(_), ex) | expr_unary(uniq(_), ex) |
       expr_ret(some(ex)) { maybe_copy(cx, ex); }
       expr_copy(expr) { check_copy_ex(cx, expr, false); }
       // Vector add copies.
@@ -163,14 +170,14 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
         alt substs.substs {
           some(ts) {
             let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
-            let kinds = vec::map(ty::lookup_item_type(cx.tcx, did).bounds,
-                                 {|bs| ty::param_bounds_to_kind(bs)});
+            let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
             let i = 0u;
             for ty in ts {
                 let kind = ty::type_kind(cx.tcx, ty);
-                if !ty::kind_lteq(kinds[i], kind) {
+                let p_kind = ty::param_bounds_to_kind(bounds[i]);
+                if !ty::kind_lteq(p_kind, kind) {
                     cx.tcx.sess.span_err(e.span, "instantiating a " +
-                                         kind_to_str(kinds[i]) +
+                                         kind_to_str(p_kind) +
                                          " type parameter with a "
                                          + kind_to_str(kind) + " type");
                 }
diff --git a/src/comp/middle/last_use.rs b/src/comp/middle/last_use.rs
index 113e9a9c299..78301c81202 100644
--- a/src/comp/middle/last_use.rs
+++ b/src/comp/middle/last_use.rs
@@ -1,7 +1,7 @@
 import syntax::{visit, ast_util};
 import syntax::ast::*;
 import syntax::codemap::span;
-import std::list::{list, nil, cons, tail};
+import std::list::{is_not_empty, list, nil, cons, tail};
 import core::{vec, option};
 import std::list;
 
@@ -63,6 +63,13 @@ fn find_last_uses(c: @crate, def_map: resolve::def_map,
     ret mini_table;
 }
 
+fn is_block(cx: ctx, id: node_id) -> bool {
+    alt ty::struct(cx.tcx, ty::node_id_to_monotype(cx.tcx, id)) {
+      ty::ty_fn({proto: proto_block., _}) { true }
+      _ { false }
+    }
+}
+
 fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
     alt ex.node {
       expr_ret(oexpr) {
@@ -135,9 +142,8 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
         let arg_ts = ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f));
         for arg in args {
             alt arg.node {
-              //NDM--register captured as uses
-              expr_fn(_, _, _, captured) { fns += [arg]; }
-              expr_fn_block(_, _) { fns += [arg]; }
+              expr_fn(proto_block., _, _, _) { fns += [arg]; }
+              expr_fn_block(_, _) when is_block(cx, arg.id) { fns += [arg]; }
               _ {
                 alt arg_ts[i].mode {
                   by_mut_ref. { clear_if_path(cx, arg, v, false); }
@@ -163,6 +169,15 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
             visit::visit_fn(fk, decl, body, sp, id, cx, v);
         });
     } else {
+        alt cx.tcx.freevars.find(id) {
+          some(vars) {
+            for v in *vars {
+                clear_in_current(cx, ast_util::def_id_of_def(v.def).node,
+                                 false);
+            }
+          }
+          _ {}
+        }
         let old = nil;
         cx.blocks <-> old;
         visit::visit_fn(fk, decl, body, sp, id, cx, v);
@@ -177,7 +192,9 @@ fn visit_block(tp: block_type, cx: ctx, visit: block()) {
     visit();
     local.second = true;
     visit();
-    cx.blocks = tail(cx.blocks);
+    let cx_blocks = cx.blocks;
+    check is_not_empty(cx_blocks);
+    cx.blocks = tail(cx_blocks);
     cx.current = join_branches(local.exits);
 }
 
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index ca9887534cb..29d29a32fba 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -37,7 +37,7 @@ tag scope {
     scope_loop(@ast::local); // there's only 1 decl per loop.
     scope_block(ast::blk, @mutable uint, @mutable uint);
     scope_arm(ast::arm);
-    scope_self(ast::node_id);
+    scope_method(ast::node_id, [ast::ty_param]);
 }
 
 type scopes = list<scope>;
@@ -142,6 +142,7 @@ type env =
                     mutable data: [ast::node_id]},
      mutable reported: [{ident: str, sc: scope}],
      mutable ignored_imports: [node_id],
+     mutable current_tp: option::t<uint>,
      sess: session};
 
 
@@ -168,6 +169,7 @@ fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) ->
           used_imports: {mutable track: false, mutable data:  []},
           mutable reported: [],
           mutable ignored_imports: [],
+          mutable current_tp: none,
           sess: sess};
     map_crate(e, crate);
     resolve_imports(*e);
@@ -266,6 +268,7 @@ fn map_crate(e: @env, c: @ast::crate) {
             let imp = follow_import(*e, sc, *path, vi.span);
             if option::is_some(imp) {
                 let glob = {def: option::get(imp), item: vi};
+                check list::is_not_empty(sc);
                 alt list::head(sc) {
                   scope_item(i) {
                     e.mod_map.get(i.id).glob_imports += [glob];
@@ -335,6 +338,7 @@ fn resolve_names(e: @env, c: @ast::crate) {
           visit_pat: bind walk_pat(e, _, _, _),
           visit_expr: bind walk_expr(e, _, _, _),
           visit_ty: bind walk_ty(e, _, _, _),
+          visit_ty_params: bind walk_tps(e, _, _, _),
           visit_constr: bind walk_constr(e, _, _, _, _, _),
           visit_fn: bind visit_fn_with_scope(e, _, _, _, _, _, _, _)
           with *visit::default_visitor()};
@@ -368,6 +372,20 @@ fn resolve_names(e: @env, c: @ast::crate) {
           _ { }
         }
     }
+    fn walk_tps(e: @env, tps: [ast::ty_param], sc: scopes, v: vt<scopes>) {
+        let outer_current_tp = e.current_tp, current = 0u;
+        for tp in tps {
+            e.current_tp = some(current);
+            for bound in *tp.bounds {
+                alt bound {
+                  bound_iface(t) { v.visit_ty(t, sc, v); }
+                  _ {}
+                }
+            }
+            current += 1u;
+        }
+        e.current_tp = outer_current_tp;
+    }
     fn walk_constr(e: @env, p: @ast::path, sp: span, id: node_id, sc: scopes,
                    _v: vt<scopes>) {
         maybe_insert(e, id, lookup_path_strict(*e, sc, sp, p.node, ns_value));
@@ -403,9 +421,17 @@ fn visit_item_with_scope(i: @ast::item, sc: scopes, v: vt<scopes>) {
         alt ifce { some(ty) { v.visit_ty(ty, sc, v); } _ {} }
         v.visit_ty(sty, sc, v);
         for m in methods {
-            v.visit_fn(visit::fk_method(m.ident, tps + m.tps),
-                       m.decl, m.body, m.span,
-                       m.id, sc, v);
+            let msc = cons(scope_method(i.id, tps + m.tps), @sc);
+            v.visit_fn(visit::fk_method(m.ident, []),
+                       m.decl, m.body, m.span, m.id, msc, v);
+        }
+      }
+      ast::item_iface(tps, methods) {
+        visit::visit_ty_params(tps, sc, v);
+        for m in methods {
+            let msc = cons(scope_method(i.id, tps + m.tps), @sc);
+            for a in m.decl.inputs { v.visit_ty(a.ty, msc, v); }
+            v.visit_ty(m.decl.output, msc, v);
         }
       }
       _ { visit::visit_item(i, sc, v); }
@@ -436,8 +462,8 @@ fn visit_fn_with_scope(e: @env, fk: visit::fn_kind, decl: ast::fn_decl,
     // for f's constrs in the table.
     for c: @ast::constr in decl.constraints { resolve_constr(e, c, sc, v); }
     let scope = alt fk {
-      visit::fk_item_fn(_, tps) | visit::fk_method(_, tps) |
-      visit::fk_res(_, tps) {
+      visit::fk_item_fn(_, tps) | visit::fk_res(_, tps) |
+      visit::fk_method(_, tps) {
         scope_bare_fn(decl, id, tps)
       }
       visit::fk_anon(_) | visit::fk_fn_block. {
@@ -461,6 +487,7 @@ fn visit_block_with_scope(b: ast::blk, sc: scopes, v: vt<scopes>) {
 }
 
 fn visit_decl_with_scope(d: @decl, sc: scopes, v: vt<scopes>) {
+    check list::is_not_empty(sc);
     let loc_pos = alt list::head(sc) {
       scope_block(_, _, pos) { pos }
       _ { @mutable 0u }
@@ -489,7 +516,7 @@ fn visit_expr_with_scope(x: @ast::expr, sc: scopes, v: vt<scopes>) {
         v.visit_block(blk, new_sc, v);
       }
       ast::expr_anon_obj(_) {
-        visit::visit_expr(x, cons(scope_self(x.id), @sc), v);
+        visit::visit_expr(x, cons(scope_method(x.id, []), @sc), v);
       }
       _ { visit::visit_expr(x, sc, v); }
     }
@@ -796,17 +823,14 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
           scope_item(it) {
             alt it.node {
               ast::item_obj(ob, ty_params, _) {
-                ret lookup_in_obj(name, ob, ty_params, ns, it.id);
+                ret lookup_in_obj(e, name, ob, ty_params, ns, it.id);
               }
-              ast::item_impl(ty_params, _, _, _) {
-                if (name == "self" && ns == ns_value) {
-                    ret some(ast::def_self(local_def(it.id)));
-                }
-                if ns == ns_type { ret lookup_in_ty_params(name, ty_params); }
+              ast::item_impl(tps, _, _, _) {
+                if ns == ns_type { ret lookup_in_ty_params(e, name, tps); }
               }
               ast::item_iface(tps, _) | ast::item_tag(_, tps) |
               ast::item_ty(_, tps) {
-                if ns == ns_type { ret lookup_in_ty_params(name, tps); }
+                if ns == ns_type { ret lookup_in_ty_params(e, name, tps); }
               }
               ast::item_mod(_) {
                 ret lookup_in_local_mod(e, it.id, sp, name, ns, inside);
@@ -817,21 +841,23 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
               _ { }
             }
           }
-          scope_self(id) {
+          scope_method(id, tps) {
             if (name == "self" && ns == ns_value) {
                 ret some(ast::def_self(local_def(id)));
+            } else if ns == ns_type {
+                ret lookup_in_ty_params(e, name, tps);
             }
           }
           scope_native_item(it) {
             alt it.node {
               ast::native_item_fn(decl, ty_params) {
-                ret lookup_in_fn(name, decl, ty_params, ns);
+                ret lookup_in_fn(e, name, decl, ty_params, ns);
               }
             }
           }
           scope_bare_fn(decl, _, ty_params) |
           scope_fn_expr(decl, _, ty_params) {
-            ret lookup_in_fn(name, decl, ty_params, ns);
+            ret lookup_in_fn(e, name, decl, ty_params, ns);
           }
           scope_loop(local) {
             if ns == ns_value {
@@ -906,13 +932,13 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
     e.sess.bug("reached unreachable code in lookup_in_scope"); // sigh
 }
 
-fn lookup_in_ty_params(name: ident, ty_params: [ast::ty_param]) ->
-   option::t<def> {
+fn lookup_in_ty_params(e: env, name: ident, ty_params: [ast::ty_param])
+    -> option::t<def> {
     let n = 0u;
     for tp: ast::ty_param in ty_params {
-        if str::eq(tp.ident, name) {
-            ret some(ast::def_ty_param(local_def(tp.id), n));
-        }
+        if str::eq(tp.ident, name) && alt e.current_tp {
+            some(cur) { n < cur } none. { true }
+        } { ret some(ast::def_ty_param(local_def(tp.id), n)); }
         n += 1u;
     }
     ret none::<def>;
@@ -927,7 +953,8 @@ fn lookup_in_pat(name: ident, pat: @ast::pat) -> option::t<def_id> {
     ret found;
 }
 
-fn lookup_in_fn(name: ident, decl: ast::fn_decl, ty_params: [ast::ty_param],
+fn lookup_in_fn(e: env, name: ident, decl: ast::fn_decl,
+                ty_params: [ast::ty_param],
                 ns: namespace) -> option::t<def> {
     alt ns {
       ns_value. {
@@ -938,12 +965,13 @@ fn lookup_in_fn(name: ident, decl: ast::fn_decl, ty_params: [ast::ty_param],
         }
         ret none::<def>;
       }
-      ns_type. { ret lookup_in_ty_params(name, ty_params); }
+      ns_type. { ret lookup_in_ty_params(e, name, ty_params); }
       _ { ret none::<def>; }
     }
 }
 
-fn lookup_in_obj(name: ident, ob: ast::_obj, ty_params: [ast::ty_param],
+fn lookup_in_obj(e: env, name: ident, ob: ast::_obj,
+                 ty_params: [ast::ty_param],
                  ns: namespace, id: node_id) -> option::t<def> {
     alt ns {
       ns_value. {
@@ -955,7 +983,7 @@ fn lookup_in_obj(name: ident, ob: ast::_obj, ty_params: [ast::ty_param],
         }
         ret none::<def>;
       }
-      ns_type. { ret lookup_in_ty_params(name, ty_params); }
+      ns_type. { ret lookup_in_ty_params(e, name, ty_params); }
       _ { ret none::<def>; }
     }
 }
@@ -1701,7 +1729,8 @@ fn check_exports(e: @env) {
 // Impl resolution
 
 type method_info = {did: def_id, n_tps: uint, ident: ast::ident};
-type _impl = {did: def_id, ident: ast::ident, methods: [@method_info]};
+type _impl = {did: def_id, iface_did: option::t<def_id>,
+              ident: ast::ident, methods: [@method_info]};
 type iscopes = list<@[@_impl]>;
 
 fn resolve_impls(e: @env, c: @ast::crate) {
@@ -1757,14 +1786,20 @@ fn find_impls_in_view_item(e: env, vi: @ast::view_item,
     }
 }
 
-fn find_impls_in_item(i: @ast::item, &impls: [@_impl],
+fn find_impls_in_item(e: env, i: @ast::item, &impls: [@_impl],
                       name: option::t<ident>,
                       ck_exports: option::t<ast::_mod>) {
     alt i.node {
-      ast::item_impl(_, _, _, mthds) {
+      ast::item_impl(_, ifce, _, mthds) {
         if alt name { some(n) { n == i.ident } _ { true } } &&
            alt ck_exports { some(m) { is_exported(i.ident, m) } _ { true } } {
             impls += [@{did: local_def(i.id),
+                        iface_did: alt ifce {
+                            some(@{node: ast::ty_path(_, id), _}) {
+                                some(def_id_of_def(e.def_map.get(id)))
+                            }
+                            _ { none }
+                        },
                         ident: i.ident,
                         methods: vec::map(mthds, {|m|
                             @{did: local_def(m.id),
@@ -1788,7 +1823,7 @@ fn find_impls_in_mod(e: env, m: def, &impls: [@_impl],
             cached = if defid.crate == ast::local_crate {
                 let tmp = [];
                 for i in option::get(e.mod_map.get(defid.node).m).items {
-                    find_impls_in_item(i, tmp, name, none);
+                    find_impls_in_item(e, i, tmp, name, none);
                 }
                 @tmp
             } else {
@@ -1816,7 +1851,7 @@ fn visit_block_with_impl_scope(e: @env, b: ast::blk, sc: iscopes,
     for st in b.node.stmts {
         alt st.node {
           ast::stmt_decl(@{node: ast::decl_item(i), _}, _) {
-            find_impls_in_item(i, impls, none, none);
+            find_impls_in_item(*e, i, impls, none, none);
           }
           _ {}
         }
@@ -1829,13 +1864,15 @@ fn visit_mod_with_impl_scope(e: @env, m: ast::_mod, s: span, sc: iscopes,
                              v: vt<iscopes>) {
     let impls = [];
     for vi in m.view_items { find_impls_in_view_item(*e, vi, impls, sc); }
-    for i in m.items { find_impls_in_item(i, impls, none, none); }
+    for i in m.items { find_impls_in_item(*e, i, impls, none, none); }
     visit::visit_mod(m, s, vec::len(impls) > 0u ? cons(@impls, @sc) : sc, v);
 }
 
 fn resolve_impl_in_expr(e: @env, x: @ast::expr, sc: iscopes, v: vt<iscopes>) {
     alt x.node {
-      ast::expr_field(_, _, _) { e.impl_map.insert(x.id, sc); }
+      ast::expr_field(_, _, _) | ast::expr_path(_) {
+        e.impl_map.insert(x.id, sc);
+      }
       _ {}
     }
     visit::visit_expr(x, sc, v);
diff --git a/src/comp/middle/shape.rs b/src/comp/middle/shape.rs
index 94529b8be60..f78e0ffef85 100644
--- a/src/comp/middle/shape.rs
+++ b/src/comp/middle/shape.rs
@@ -450,7 +450,7 @@ fn gen_tag_shapes(ccx: @crate_ctxt) -> ValueRef {
         let did = ccx.shape_cx.tag_order[i];
         let variants = ty::tag_variants(ccx.tcx, did);
         let item_tyt = ty::lookup_item_type(ccx.tcx, did);
-        let ty_param_count = vec::len(item_tyt.bounds);
+        let ty_param_count = vec::len(*item_tyt.bounds);
 
         for v: ty::variant_info in *variants {
             offsets += [vec::len(data) as u16];
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 26a9a29b685..c279c0a9b49 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -13,7 +13,6 @@
 //     but many TypeRefs correspond to one ty::t; for instance, tup(int, int,
 //     int) and rec(x=int, y=int, z=int) will have the same TypeRef.
 
-import core::{either, str, int, uint, option, vec};
 import std::{map, time};
 import std::map::hashmap;
 import std::map::{new_int_hash, new_str_hash};
@@ -81,13 +80,12 @@ fn type_of_explicit_args(cx: @crate_ctxt, sp: span, inputs: [ty::arg]) ->
 //  - create_llargs_for_fn_args.
 //  - new_fn_ctxt
 //  - trans_args
-fn type_of_fn(cx: @crate_ctxt, sp: span,
-              is_method: bool, inputs: [ty::arg],
-              output: ty::t, ty_param_count: uint)
-   : non_ty_var(cx, output) -> TypeRef {
+fn type_of_fn(cx: @crate_ctxt, sp: span, is_method: bool, inputs: [ty::arg],
+              output: ty::t, params: [ty::param_bounds]) -> TypeRef {
     let atys: [TypeRef] = [];
 
     // Arg 0: Output pointer.
+    check non_ty_var(cx, output);
     let out_ty = T_ptr(type_of_inner(cx, sp, output));
     atys += [out_ty];
 
@@ -100,8 +98,15 @@ fn type_of_fn(cx: @crate_ctxt, sp: span,
 
     // Args >2: ty params, if not acquired via capture...
     if !is_method {
-        let i = 0u;
-        while i < ty_param_count { atys += [T_ptr(cx.tydesc_type)]; i += 1u; }
+        for bounds in params {
+            atys += [T_ptr(cx.tydesc_type)];
+            for bound in *bounds {
+                alt bound {
+                  ty::bound_iface(_) { atys += [T_ptr(T_dict())]; }
+                  _ {}
+                }
+            }
+        }
     }
     // ... then explicit args.
     atys += type_of_explicit_args(cx, sp, inputs);
@@ -110,15 +115,13 @@ fn type_of_fn(cx: @crate_ctxt, sp: span,
 
 // Given a function type and a count of ty params, construct an llvm type
 fn type_of_fn_from_ty(cx: @crate_ctxt, sp: span, fty: ty::t,
-                      ty_param_count: uint)
-    : returns_non_ty_var(cx, fty) -> TypeRef {
+                      param_bounds: [ty::param_bounds]) -> TypeRef {
     // 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, ty::ty_fn_args(cx.tcx, fty),
-                   ret_ty, ty_param_count);
+                   ret_ty, param_bounds);
 }
 
 fn type_of_inner(cx: @crate_ctxt, sp: span, t: ty::t)
@@ -169,12 +172,10 @@ fn type_of_inner(cx: @crate_ctxt, sp: span, t: ty::t)
         T_struct(tys)
       }
       ty::ty_fn(_) {
-        // FIXME: could be a constraint on ty_fn
-        check returns_non_ty_var(cx, t);
-        T_fn_pair(cx, type_of_fn_from_ty(cx, sp, t, 0u))
+        T_fn_pair(cx, type_of_fn_from_ty(cx, sp, t, []))
       }
       ty::ty_native_fn(args, out) {
-        let nft = native_fn_wrapper_type(cx, sp, 0u, t);
+        let nft = native_fn_wrapper_type(cx, sp, [], t);
         T_fn_pair(cx, nft)
       }
       ty::ty_obj(meths) { cx.rust_object_type }
@@ -204,6 +205,11 @@ fn type_of_inner(cx: @crate_ctxt, sp: span, t: ty::t)
       ty::ty_opaque_closure. {
         T_opaque_closure(cx)
       }
+      ty::ty_constr(subt,_) {
+        // FIXME: could be a constraint on ty_fn
+          check non_ty_var(cx, subt);
+          type_of_inner(cx, sp, subt)
+      }
       _ {
         fail "type_of_inner not implemented for this kind of type";
       }
@@ -233,8 +239,7 @@ fn type_of_ty_param_bounds_and_ty(lcx: @local_ctxt, sp: span,
     let t = tpt.ty;
     alt ty::struct(cx.tcx, t) {
       ty::ty_fn(_) | ty::ty_native_fn(_, _) {
-        check returns_non_ty_var(cx, t);
-        ret type_of_fn_from_ty(cx, sp, t, vec::len(tpt.bounds));
+        ret type_of_fn_from_ty(cx, sp, t, *tpt.bounds);
       }
       _ {
         // fall through
@@ -906,7 +911,10 @@ fn linearize_ty_params(cx: @block_ctxt, t: ty::t) ->
           ty::ty_param(pid, _) {
             let seen: bool = false;
             for d: uint in r.defs { if d == pid { seen = true; } }
-            if !seen { r.vals += [r.cx.fcx.lltydescs[pid]]; r.defs += [pid]; }
+            if !seen {
+                r.vals += [r.cx.fcx.lltyparams[pid].desc];
+                r.defs += [pid];
+            }
           }
           _ { }
         }
@@ -1042,8 +1050,9 @@ fn get_tydesc(cx: @block_ctxt, t: ty::t, escapes: bool,
     // Is the supplied type a type param? If so, return the passed-in tydesc.
     alt ty::type_param(bcx_tcx(cx), t) {
       some(id) {
-        if id < vec::len(cx.fcx.lltydescs) {
-            ret {kind: tk_param, result: rslt(cx, cx.fcx.lltydescs[id])};
+        if id < vec::len(cx.fcx.lltyparams) {
+            ret {kind: tk_param,
+                 result: rslt(cx, cx.fcx.lltyparams[id].desc)};
         } else {
             bcx_tcx(cx).sess.span_bug(cx.sp,
                                       "Unbound typaram in get_tydesc: " +
@@ -1206,10 +1215,7 @@ fn make_generic_glue_inner(cx: @local_ctxt, sp: span, t: ty::t,
         p += 1u;
     }
 
-    // FIXME: Implement some kind of freeze operation in the standard library.
-    let lltydescs_frozen = [];
-    for lltydesc: ValueRef in lltydescs { lltydescs_frozen += [lltydesc]; }
-    fcx.lltydescs = lltydescs_frozen;
+    fcx.lltyparams = vec::map_mut(lltydescs, {|d| {desc: d, dicts: none}});
 
     let bcx = new_top_block_ctxt(fcx);
     let lltop = bcx.llbb;
@@ -2559,10 +2565,13 @@ fn trans_do_while(cx: @block_ctxt, body: ast::blk, cond: @ast::expr) ->
     ret next_cx;
 }
 
-type generic_info =
-    {item_type: ty::t,
-     static_tis: [option::t<@tydesc_info>],
-     tydescs: [ValueRef]};
+type generic_info = {
+    item_type: ty::t,
+    static_tis: [option::t<@tydesc_info>],
+    tydescs: [ValueRef],
+    param_bounds: @[ty::param_bounds],
+    origins: option::t<typeck::dict_res>
+};
 
 tag lval_kind {
     temporary; //< Temporary value passed by value if of immediate type
@@ -2571,7 +2580,12 @@ tag lval_kind {
 }
 type local_var_result = {val: ValueRef, kind: lval_kind};
 type lval_result = {bcx: @block_ctxt, val: ValueRef, kind: lval_kind};
-tag callee_env { obj_env(ValueRef); null_env; is_closure; }
+tag callee_env {
+    null_env;
+    is_closure;
+    obj_env(ValueRef);
+    dict_env(ValueRef, ValueRef);
+}
 type lval_maybe_callee = {bcx: @block_ctxt,
                           val: ValueRef,
                           kind: lval_kind,
@@ -2608,18 +2622,19 @@ fn trans_external_path(cx: @block_ctxt, did: ast::def_id,
 
 fn lval_static_fn(bcx: @block_ctxt, fn_id: ast::def_id, id: ast::node_id)
     -> lval_maybe_callee {
-    let tpt = ty::lookup_item_type(bcx_tcx(bcx), fn_id);
+    let ccx = bcx_ccx(bcx);
+    let tpt = ty::lookup_item_type(ccx.tcx, fn_id);
     let val = if fn_id.crate == ast::local_crate {
         // Internal reference.
-        assert (bcx_ccx(bcx).item_ids.contains_key(fn_id.node));
-        bcx_ccx(bcx).item_ids.get(fn_id.node)
+        assert (ccx.item_ids.contains_key(fn_id.node));
+        ccx.item_ids.get(fn_id.node)
     } else {
         // External reference.
         trans_external_path(bcx, fn_id, tpt)
     };
-    let tys = ty::node_id_to_type_params(bcx_tcx(bcx), id);
+    let tys = ty::node_id_to_type_params(ccx.tcx, id);
     let gen = none, bcx = bcx;
-    if vec::len::<ty::t>(tys) != 0u {
+    if vec::len(tys) != 0u {
         let tydescs = [], tis = [];
         for t in tys {
             // TODO: Doesn't always escape.
@@ -2629,7 +2644,11 @@ fn lval_static_fn(bcx: @block_ctxt, fn_id: ast::def_id, id: ast::node_id)
             bcx = td.bcx;
             tydescs += [td.val];
         }
-        gen = some({item_type: tpt.ty, static_tis: tis, tydescs: tydescs});
+        gen = some({item_type: tpt.ty,
+                    static_tis: tis,
+                    tydescs: tydescs,
+                    param_bounds: tpt.bounds,
+                    origins: ccx.dict_map.find(id)});
     }
     ret {bcx: bcx, val: val, kind: owned, env: null_env, generic: gen};
 }
@@ -2731,7 +2750,7 @@ fn trans_var(cx: @block_ctxt, sp: span, def: ast::def, id: ast::node_id)
             ret lval_no_env(cx, ccx.consts.get(did.node), owned);
         } else {
             let tp = ty::node_id_to_monotype(ccx.tcx, id);
-            let val = trans_external_path(cx, did, @{bounds: [], ty: tp});
+            let val = trans_external_path(cx, did, {bounds: @[], ty: tp});
             ret lval_no_env(cx, load_if_immediate(cx, val, tp), owned_imm);
         }
       }
@@ -2764,10 +2783,8 @@ fn trans_object_field_inner(bcx: @block_ctxt, o: ValueRef,
     let fn_ty: ty::t = ty::mk_fn(tcx, mths[ix].fty);
     let ret_ty = ty::ty_fn_ret(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,
-                              ty::ty_fn_args(tcx, fn_ty), ret_ty, 0u);
+                              ty::ty_fn_args(tcx, fn_ty), ret_ty, []);
     v = Load(bcx, PointerCast(bcx, v, T_ptr(T_ptr(ll_fn_ty))));
     ret {bcx: bcx, mthptr: v, objptr: o};
 }
@@ -2840,17 +2857,6 @@ fn expr_is_lval(bcx: @block_ctxt, e: @ast::expr) -> bool {
     ty::expr_is_lval(ccx.method_map, ccx.tcx, e)
 }
 
-// This is for impl methods, not obj methods.
-fn trans_method_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr,
-                       did: ast::def_id) -> lval_maybe_callee {
-    let tz = [], tr = [];
-    let basety = ty::expr_ty(bcx_tcx(bcx), base);
-    let {bcx, val} = trans_arg_expr(bcx, {mode: ast::by_ref, ty: basety},
-                                    type_of_or_i8(bcx, basety), tz, tr, base);
-    let val = PointerCast(bcx, val, T_opaque_boxed_closure_ptr(bcx_ccx(bcx)));
-    {env: obj_env(val) with lval_static_fn(bcx, did, e.id)}
-}
-
 fn trans_callee(bcx: @block_ctxt, e: @ast::expr) -> lval_maybe_callee {
     alt e.node {
       ast::expr_path(p) { ret trans_path(bcx, p, e.id); }
@@ -2858,8 +2864,12 @@ fn trans_callee(bcx: @block_ctxt, e: @ast::expr) -> lval_maybe_callee {
         // Lval means this is a record field, so not a method
         if !expr_is_lval(bcx, e) {
             alt bcx_ccx(bcx).method_map.find(e.id) {
-              some(did) { // An impl method
-                ret trans_method_callee(bcx, e, base, did);
+              some(typeck::method_static(did)) { // An impl method
+                ret trans_impl::trans_static_callee(bcx, e, base, did);
+              }
+              some(typeck::method_param(iid, off, p, b)) {
+                ret trans_impl::trans_dict_callee(
+                    bcx, e, base, iid, off, p, b);
               }
               none. { // An object method
                 let of = trans_object_field(bcx, base, ident);
@@ -2930,7 +2940,7 @@ fn maybe_add_env(bcx: @block_ctxt, c: lval_maybe_callee)
     -> (lval_kind, ValueRef) {
     alt c.env {
       is_closure. { (c.kind, c.val) }
-      obj_env(_) {
+      obj_env(_) | dict_env(_, _) {
         fail "Taking the value of a method does not work yet (issue #435)";
       }
       null_env. {
@@ -3143,7 +3153,23 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef,
     alt gen {
       some(g) {
         lazily_emit_all_generic_info_tydesc_glues(cx, g);
-        lltydescs = g.tydescs;
+        let i = 0u, n_orig = 0u;
+        for param in *g.param_bounds {
+            lltydescs += [g.tydescs[i]];
+            for bound in *param {
+                alt bound {
+                  ty::bound_iface(_) {
+                    let res = trans_impl::get_dict(
+                        bcx, option::get(g.origins)[n_orig]);
+                    lltydescs += [res.val];
+                    bcx = res.bcx;
+                    n_orig += 1u;
+                  }
+                  _ {}
+                }
+            }
+            i += 1u;
+        }
         args = ty::ty_fn_args(tcx, g.item_type);
         retty = ty::ty_fn_ret(tcx, g.item_type);
       }
@@ -3214,12 +3240,13 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
     let bcx = f_res.bcx;
 
     let faddr = f_res.val;
-    let llenv;
+    let llenv, dict_param = none;
     alt f_res.env {
       null_env. {
         llenv = llvm::LLVMGetUndef(T_opaque_boxed_closure_ptr(bcx_ccx(cx)));
       }
       obj_env(e) { llenv = e; }
+      dict_env(dict, e) { llenv = e; dict_param = some(dict); }
       is_closure. {
         // It's a closure. Have to fetch the elements
         if f_res.kind == owned {
@@ -3238,6 +3265,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
         trans_args(bcx, llenv, f_res.generic, args, fn_expr_ty, dest);
     bcx = args_res.bcx;
     let llargs = args_res.args;
+    option::may(dict_param) {|dict| llargs = [dict] + llargs}
     let llretslot = args_res.retslot;
 
     /* If the block is terminated,
@@ -3300,8 +3328,7 @@ fn invoke_(bcx: @block_ctxt, llfn: ValueRef, llargs: [ValueRef],
     // cleanups to run
     if bcx.unreachable { ret bcx; }
     let normal_bcx = new_sub_block_ctxt(bcx, "normal return");
-    invoker(bcx, llfn, llargs,
-            normal_bcx.llbb,
+    invoker(bcx, llfn, llargs, normal_bcx.llbb,
             get_landing_pad(bcx, to_zero, to_revoke));
     ret normal_bcx;
 }
@@ -4345,7 +4372,7 @@ fn new_fn_ctxt_w_id(cx: @local_ctxt, sp: span, llfndecl: ValueRef,
           llobjfields: new_int_hash::<ValueRef>(),
           lllocals: new_int_hash::<local_val>(),
           llupvars: new_int_hash::<ValueRef>(),
-          mutable lltydescs: [],
+          mutable lltyparams: [],
           derived_tydescs: ty::new_ty_hash(),
           id: id,
           ret_style: rstyle,
@@ -4387,10 +4414,22 @@ fn create_llargs_for_fn_args(cx: @fn_ctxt, ty_self: self_arg,
       obj_self(_) {}
       _ {
         for tp in ty_params {
-            let llarg = llvm::LLVMGetParam(cx.llfn, arg_n);
-            assert (llarg as int != 0);
-            cx.lltydescs += [llarg];
+            let lltydesc = llvm::LLVMGetParam(cx.llfn, arg_n), dicts = none;
             arg_n += 1u;
+            for bound in *fcx_tcx(cx).ty_param_bounds.get(tp.id) {
+                alt bound {
+                  ty::bound_iface(_) {
+                    let dict = llvm::LLVMGetParam(cx.llfn, arg_n);
+                    arg_n += 1u;
+                    dicts = some(alt dicts {
+                      none. { [dict] }
+                      some(ds) { ds + [dict] }
+                    });
+                  }
+                  _ {}
+                }
+            }
+            cx.lltyparams += [{desc: lltydesc, dicts: dicts}];
         }
       }
     }
@@ -4479,7 +4518,7 @@ fn populate_fn_ctxt_from_llself(fcx: @fn_ctxt, llself: val_self_pair) {
         let lltyparam: ValueRef =
             GEPi(bcx, obj_typarams, [0, i]);
         lltyparam = Load(bcx, lltyparam);
-        fcx.lltydescs += [lltyparam];
+        fcx.lltyparams += [{desc: lltyparam, dicts: none}];
         i += 1;
     }
     i = 0;
@@ -4658,7 +4697,8 @@ fn trans_tag_variant(cx: @local_ctxt, tag_id: ast::node_id,
     let ty_param_substs: [ty::t] = [];
     i = 0u;
     for tp: ast::ty_param in ty_params {
-        ty_param_substs += [ty::mk_param(ccx.tcx, i, @[])];
+        ty_param_substs += [ty::mk_param(ccx.tcx, i,
+                                         ast_util::local_def(tp.id))];
         i += 1u;
     }
     let arg_tys = arg_tys_of_fn(ccx, variant.node.id);
@@ -4700,20 +4740,6 @@ fn trans_tag_variant(cx: @local_ctxt, tag_id: ast::node_id,
     finish_fn(fcx, lltop);
 }
 
-fn trans_impl(cx: @local_ctxt, name: ast::ident, methods: [@ast::method],
-              id: ast::node_id, tps: [ast::ty_param]) {
-    let sub_cx = extend_path(cx, name);
-    for m in methods {
-        alt cx.ccx.item_ids.find(m.id) {
-          some(llfn) {
-            trans_fn(extend_path(sub_cx, m.ident), m.span, m.decl, m.body,
-                     llfn, impl_self(ty::node_id_to_monotype(cx.ccx.tcx, id)),
-                     tps + m.tps, m.id);
-          }
-        }
-    }
-}
-
 
 // FIXME: this should do some structural hash-consing to avoid
 // duplicate constants. I think. Maybe LLVM has a magical mode
@@ -5013,8 +5039,8 @@ fn trans_item(cx: @local_ctxt, item: ast::item) {
                  with *extend_path(cx, item.ident)};
         trans_obj(sub_cx, item.span, ob, ctor_id, tps);
       }
-      ast::item_impl(tps, _, _, ms) {
-        trans_impl(cx, item.ident, ms, item.id, tps);
+      ast::item_impl(tps, ifce, _, ms) {
+        trans_impl::trans_impl(cx, item.ident, ms, item.id, tps, ifce);
       }
       ast::item_res(decl, tps, body, dtor_id, ctor_id) {
         trans_res_ctor(cx, item.span, decl, ctor_id, tps);
@@ -5080,13 +5106,17 @@ fn register_fn(ccx: @crate_ctxt, sp: span, path: [str], flav: str,
     register_fn_full(ccx, sp, path, flav, ty_params, node_id, t);
 }
 
+fn param_bounds(ccx: @crate_ctxt, tp: ast::ty_param) -> ty::param_bounds {
+    ccx.tcx.ty_param_bounds.get(tp.id)
+}
+
 fn register_fn_full(ccx: @crate_ctxt, sp: span, path: [str], _flav: str,
-                    ty_params: [ast::ty_param], node_id: ast::node_id,
+                    tps: [ast::ty_param], node_id: ast::node_id,
                     node_type: ty::t)
     : returns_non_ty_var(ccx, node_type) {
     let path = path;
-    let llfty =
-        type_of_fn_from_ty(ccx, sp, node_type, vec::len(ty_params));
+    let llfty = type_of_fn_from_ty(ccx, sp, node_type,
+                                   vec::map(tps, {|p| param_bounds(ccx, p)}));
     let ps: str = mangle_exported_name(ccx, path, node_type);
     let llfn: ValueRef = decl_cdecl_fn(ccx.llmod, ps, llfty);
     ccx.item_ids.insert(node_id, llfn);
@@ -5122,9 +5152,7 @@ fn create_main_wrapper(ccx: @crate_ctxt, sp: span, main_llfn: ValueRef,
              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);
-        check non_ty_var(ccx, nt);
-
-        let llfty = type_of_fn(ccx, sp, false, [vecarg_ty], nt, 0u);
+        let llfty = type_of_fn(ccx, sp, false, [vecarg_ty], nt, []);
         let llfdecl = decl_fn(ccx.llmod, "_rust_main",
                               lib::llvm::LLVMCCallConv, llfty);
 
@@ -5152,6 +5180,8 @@ fn create_main_wrapper(ccx: @crate_ctxt, sp: span, main_llfn: ValueRef,
         fn main_name() -> str { ret "main"; }
         #[cfg(target_os = "linux")]
         fn main_name() -> str { ret "main"; }
+        #[cfg(target_os = "freebsd")]
+        fn main_name() -> str { ret "main"; }
         let llfty = T_fn([ccx.int_type, ccx.int_type], ccx.int_type);
         let llfn = decl_cdecl_fn(ccx.llmod, main_name(), llfty);
         let llbb = str::as_buf("top", {|buf|
@@ -5215,12 +5245,12 @@ fn native_fn_ty_param_count(cx: @crate_ctxt, id: ast::node_id) -> uint {
     ret count;
 }
 
-fn native_fn_wrapper_type(cx: @crate_ctxt, sp: span, ty_param_count: uint,
+fn native_fn_wrapper_type(cx: @crate_ctxt, sp: span,
+                          param_bounds: [ty::param_bounds],
                           x: ty::t) -> TypeRef {
     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, args, out, ty_param_count);
+        ret type_of_fn(cx, sp, false, args, out, param_bounds);
       }
     }
 }
@@ -5245,56 +5275,54 @@ fn collect_native_item(ccx: @crate_ctxt,
                        _v: vt<[str]>) {
     alt i.node {
       ast::native_item_fn(_, tps) {
-        if !ccx.obj_methods.contains_key(i.id) {
-            let sp = i.span;
-            let id = i.id;
-            let node_type = node_id_type(ccx, id);
-            let fn_abi =
-                alt attr::get_meta_item_value_str_by_name(i.attrs, "abi") {
-              option::none. {
+        let sp = i.span;
+        let id = i.id;
+        let node_type = node_id_type(ccx, id);
+        let fn_abi =
+            alt attr::get_meta_item_value_str_by_name(i.attrs, "abi") {
+            option::none. {
                 // if abi isn't specified for this function, inherit from
-                // its enclosing native module
-                option::get(*abi)
+                  // its enclosing native module
+                  option::get(*abi)
               }
-              _ {
-                alt attr::native_abi(i.attrs) {
-                  either::right(abi_) { abi_ }
-                  either::left(msg) { ccx.sess.span_fatal(i.span, msg) }
+                _ {
+                    alt attr::native_abi(i.attrs) {
+                      either::right(abi_) { abi_ }
+                      either::left(msg) { ccx.sess.span_fatal(i.span, msg) }
+                    }
                 }
-              }
             };
-            alt fn_abi {
-              ast::native_abi_rust_intrinsic. {
-                // For intrinsics: link the function directly to the intrinsic
-                // function itself.
-                let num_ty_param = vec::len(tps);
-                check returns_non_ty_var(ccx, node_type);
-                let fn_type = type_of_fn_from_ty(ccx, sp, node_type,
-                                                 num_ty_param);
-                let ri_name = "rust_intrinsic_" + link_name(i);
-                let llnativefn = get_extern_fn(
-                    ccx.externs, ccx.llmod, ri_name,
-                    lib::llvm::LLVMCCallConv, fn_type);
-                ccx.item_ids.insert(id, llnativefn);
-                ccx.item_symbols.insert(id, ri_name);
-              }
+        alt fn_abi {
+          ast::native_abi_rust_intrinsic. {
+            // For intrinsics: link the function directly to the intrinsic
+            // function itself.
+            let fn_type = type_of_fn_from_ty(
+                ccx, sp, node_type,
+                vec::map(tps, {|p| param_bounds(ccx, p)}));
+            let ri_name = "rust_intrinsic_" + link_name(i);
+            let llnativefn = get_extern_fn(
+                ccx.externs, ccx.llmod, ri_name,
+                lib::llvm::LLVMCCallConv, fn_type);
+            ccx.item_ids.insert(id, llnativefn);
+            ccx.item_symbols.insert(id, ri_name);
+          }
 
-              ast::native_abi_cdecl. | ast::native_abi_stdcall. {
-                // For true external functions: create a rust wrapper
-                // and link to that.  The rust wrapper will handle
-                // switching to the C stack.
-                let new_pt = pt + [i.ident];
-                register_fn(ccx, i.span, new_pt, "native fn", tps, i.id);
-              }
-            }
+          ast::native_abi_cdecl. | ast::native_abi_stdcall. {
+            // For true external functions: create a rust wrapper
+            // and link to that.  The rust wrapper will handle
+            // switching to the C stack.
+            let new_pt = pt + [i.ident];
+            register_fn(ccx, i.span, new_pt, "native fn", tps, i.id);
+          }
         }
       }
       _ { }
     }
 }
 
-fn collect_item_1(ccx: @crate_ctxt, abi: @mutable option::t<ast::native_abi>,
-                  i: @ast::item, &&pt: [str], v: vt<[str]>) {
+fn collect_item(ccx: @crate_ctxt, abi: @mutable option::t<ast::native_abi>,
+                i: @ast::item, &&pt: [str], v: vt<[str]>) {
+    let new_pt = pt + [i.ident];
     alt i.node {
       ast::item_const(_, _) {
         let typ = node_id_type(ccx, i.id);
@@ -5319,26 +5347,11 @@ fn collect_item_1(ccx: @crate_ctxt, abi: @mutable option::t<ast::native_abi>,
           }
         }
       }
-      _ { }
-    }
-    visit::visit_item(i, pt + [i.ident], v);
-}
-
-fn collect_item_2(ccx: @crate_ctxt, i: @ast::item, &&pt: [str],
-                  v: vt<[str]>) {
-    let new_pt = pt + [i.ident];
-    visit::visit_item(i, new_pt, v);
-    alt i.node {
       ast::item_fn(_, tps, _) {
-        if !ccx.obj_methods.contains_key(i.id) {
-            register_fn(ccx, i.span, new_pt, "fn", tps, i.id);
-        }
+        register_fn(ccx, i.span, new_pt, "fn", tps, i.id);
       }
       ast::item_obj(ob, tps, ctor_id) {
         register_fn(ccx, i.span, new_pt, "obj_ctor", tps, ctor_id);
-        for m: @ast::method in ob.methods {
-            ccx.obj_methods.insert(m.id, ());
-        }
       }
       ast::item_impl(tps, _, _, methods) {
         let name = ccx.names.next(i.ident);
@@ -5358,47 +5371,28 @@ fn collect_item_2(ccx: @crate_ctxt, i: @ast::item, &&pt: [str],
         check returns_non_ty_var(ccx, t);
         register_fn_full(ccx, i.span, new_pt, "res_dtor", tps, i.id, t);
       }
-      _ { }
-    }
-}
-
-fn collect_items(ccx: @crate_ctxt, crate: @ast::crate) {
-    let abi = @mutable none::<ast::native_abi>;
-    let visitor0 = visit::default_visitor();
-    let visitor1 =
-        @{visit_native_item: bind collect_native_item(ccx, abi, _, _, _),
-          visit_item: bind collect_item_1(ccx, abi, _, _, _) with *visitor0};
-    let visitor2 =
-        @{visit_item: bind collect_item_2(ccx, _, _, _) with *visitor0};
-    visit::visit_crate(*crate, [], visit::mk_vt(visitor1));
-    visit::visit_crate(*crate, [], visit::mk_vt(visitor2));
-}
-
-fn collect_tag_ctor(ccx: @crate_ctxt, i: @ast::item, &&pt: [str],
-                    v: vt<[str]>) {
-    let new_pt = pt + [i.ident];
-    visit::visit_item(i, new_pt, v);
-    alt i.node {
       ast::item_tag(variants, tps) {
-        for variant: ast::variant in variants {
+        for variant in variants {
             if vec::len(variant.node.args) != 0u {
                 register_fn(ccx, i.span, new_pt + [variant.node.name],
                             "tag", tps, variant.node.id);
             }
         }
       }
-      _ {/* fall through */ }
+      _ { }
     }
+    visit::visit_item(i, new_pt, v);
 }
 
-fn collect_tag_ctors(ccx: @crate_ctxt, crate: @ast::crate) {
-    let visitor =
-        @{visit_item: bind collect_tag_ctor(ccx, _, _, _)
-             with *visit::default_visitor()};
-    visit::visit_crate(*crate, [], visit::mk_vt(visitor));
+fn collect_items(ccx: @crate_ctxt, crate: @ast::crate) {
+    let abi = @mutable none::<ast::native_abi>;
+    visit::visit_crate(*crate, [], visit::mk_vt(@{
+        visit_native_item: bind collect_native_item(ccx, abi, _, _, _),
+        visit_item: bind collect_item(ccx, abi, _, _, _)
+        with *visit::default_visitor()
+    }));
 }
 
-
 // The constant translation pass.
 fn trans_constant(ccx: @crate_ctxt, it: @ast::item, &&pt: [str],
                   v: vt<[str]>) {
@@ -5407,15 +5401,12 @@ fn trans_constant(ccx: @crate_ctxt, it: @ast::item, &&pt: [str],
     alt it.node {
       ast::item_tag(variants, _) {
         let i = 0u;
-        let n_variants = vec::len::<ast::variant>(variants);
-        while i < n_variants {
-            let variant = variants[i];
-            let p = new_pt + [it.ident, variant.node.name, "discrim"];
+        for variant in variants {
+            let p = new_pt + [variant.node.name, "discrim"];
             let s = mangle_exported_name(ccx, p, ty::mk_int(ccx.tcx));
-            let discrim_gvar =
-                str::as_buf(s, {|buf|
-                    llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf)
-                });
+            let discrim_gvar = str::as_buf(s, {|buf|
+                llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf)
+            });
             llvm::LLVMSetInitializer(discrim_gvar, C_int(ccx, i as int));
             llvm::LLVMSetGlobalConstant(discrim_gvar, True);
             ccx.discrims.insert(
@@ -5424,6 +5415,28 @@ fn trans_constant(ccx: @crate_ctxt, it: @ast::item, &&pt: [str],
             i += 1u;
         }
       }
+      ast::item_impl(tps, some(@{node: ast::ty_path(_, id), _}), _, ms) {
+        let i_did = ast_util::def_id_of_def(ccx.tcx.def_map.get(id));
+        let ty = ty::lookup_item_type(ccx.tcx, i_did).ty;
+        // FIXME[impl] use the same name as used in collect_items, for
+        // slightly more consistent symbol names?
+        let new_pt = pt + [ccx.names.next(it.ident)];
+        let extra_tps = vec::map(tps, {|p| param_bounds(ccx, p)});
+        let tbl = C_struct(vec::map(*ty::iface_methods(ccx.tcx, i_did), {|im|
+            alt vec::find(ms, {|m| m.ident == im.ident}) {
+              some(m) {
+                trans_impl::trans_wrapper(ccx, new_pt, extra_tps, m)
+              }
+            }
+        }));
+        let s = mangle_exported_name(ccx, new_pt + ["!vtable"], ty);
+        let vt_gvar = str::as_buf(s, {|buf|
+            llvm::LLVMAddGlobal(ccx.llmod, val_ty(tbl), buf)
+        });
+        llvm::LLVMSetInitializer(vt_gvar, tbl);
+        llvm::LLVMSetGlobalConstant(vt_gvar, True);
+        ccx.item_ids.insert(it.id, vt_gvar);
+      }
       _ { }
     }
 }
@@ -5601,7 +5614,8 @@ fn write_abi_version(ccx: @crate_ctxt) {
 fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt,
                output: str, emap: resolve::exp_map, amap: ast_map::map,
                mut_map: mut::mut_map, copy_map: alias::copy_map,
-               last_uses: last_use::last_uses, method_map: typeck::method_map)
+               last_uses: last_use::last_uses, method_map: typeck::method_map,
+               dict_map: typeck::dict_map)
     -> (ModuleRef, link::link_meta) {
     let sha = std::sha1::mk_sha1();
     let link_meta = link::build_link_meta(sess, *crate, output, sha);
@@ -5666,7 +5680,6 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt,
           discrims: ast_util::new_def_id_hash::<ValueRef>(),
           discrim_symbols: new_int_hash::<str>(),
           consts: new_int_hash::<ValueRef>(),
-          obj_methods: new_int_hash::<()>(),
           tydescs: ty::new_ty_hash(),
           module_data: new_str_hash::<ValueRef>(),
           lltypes: ty::new_ty_hash(),
@@ -5679,6 +5692,7 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt,
           copy_map: copy_map,
           last_uses: last_uses,
           method_map: method_map,
+          dict_map: dict_map,
           stats:
               {mutable n_static_tydescs: 0u,
                mutable n_derived_tydescs: 0u,
@@ -5702,7 +5716,6 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt,
           dbg_cx: dbg_cx};
     let cx = new_local_ctxt(ccx);
     collect_items(ccx, crate);
-    collect_tag_ctors(ccx, crate);
     trans_constants(ccx, crate);
     trans_mod(cx, crate.node.module);
     fill_crate_map(ccx, crate_map);
@@ -5720,7 +5733,6 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt,
         #error("n_null_glues: %u", ccx.stats.n_null_glues);
         #error("n_real_glues: %u", ccx.stats.n_real_glues);
 
-
         for timing: {ident: str, time: int} in *ccx.stats.fn_times {
             #error("time: %s took %d ms", timing.ident, timing.time);
         }
diff --git a/src/comp/middle/trans_closure.rs b/src/comp/middle/trans_closure.rs
index 7205dd27c1c..7759072f028 100644
--- a/src/comp/middle/trans_closure.rs
+++ b/src/comp/middle/trans_closure.rs
@@ -82,17 +82,21 @@ tag environment_value {
 // Given a closure ty, emits a corresponding tuple ty
 fn mk_closure_ty(tcx: ty::ctxt,
                  ck: ty::closure_kind,
-                 n_bound_tds: uint,
+                 ty_params: [fn_ty_param],
                  bound_data_ty: ty::t)
     -> ty::t {
     let tydesc_ty = alt ck {
       ty::closure_block. | ty::closure_shared. { ty::mk_type(tcx) }
       ty::closure_send. { ty::mk_send_type(tcx) }
     };
-    ret ty::mk_tup(tcx, [
-        tydesc_ty,
-        ty::mk_tup(tcx, vec::init_elt(tydesc_ty, n_bound_tds)),
-        bound_data_ty]);
+    let param_ptrs = [];
+    for tp in ty_params {
+        param_ptrs += [tydesc_ty];
+        option::may(tp.dicts) {|dicts|
+            for dict in dicts { param_ptrs += [tydesc_ty]; }
+        }
+    }
+    ty::mk_tup(tcx, [tydesc_ty, ty::mk_tup(tcx, param_ptrs), bound_data_ty])
 }
 
 fn shared_opaque_closure_box_ty(tcx: ty::ctxt) -> ty::t {
@@ -117,7 +121,7 @@ type closure_result = {
 // heap allocated closure that copies the upvars into environment.
 // Otherwise, it is stack allocated and copies pointers to the upvars.
 fn store_environment(
-    bcx: @block_ctxt, lltydescs: [ValueRef],
+    bcx: @block_ctxt, lltyparams: [fn_ty_param],
     bound_values: [environment_value],
     ck: ty::closure_kind)
     -> closure_result {
@@ -162,7 +166,7 @@ fn store_environment(
     }
     let bound_data_ty = ty::mk_tup(tcx, bound_tys);
     let closure_ty =
-        mk_closure_ty(tcx, ck, vec::len(lltydescs), bound_data_ty);
+        mk_closure_ty(tcx, ck, lltyparams, bound_data_ty);
 
     let temp_cleanups = [];
 
@@ -210,7 +214,7 @@ fn store_environment(
         // in the shape code.  Therefore, I am using
         // tps_normal, which is what we used before.
         //
-        // let tps = tps_fn(vec::len(lltydescs));
+        // let tps = tps_fn(vec::len(lltyparams));
 
         let tps = tps_normal;
         let {result:closure_td, _} =
@@ -232,10 +236,19 @@ fn store_environment(
     let {bcx:bcx, val:ty_params_slot} =
         GEP_tup_like_1(bcx, closure_ty, closure,
                        [0, abi::closure_elt_ty_params]);
-    vec::iteri(lltydescs) { |i, td|
-        let ty_param_slot = GEPi(bcx, ty_params_slot, [0, i as int]);
-        let cloned_td = maybe_clone_tydesc(bcx, ck, td);
-        Store(bcx, cloned_td, ty_param_slot);
+    let off = 0;
+
+    for tp in lltyparams {
+        let cloned_td = maybe_clone_tydesc(bcx, ck, tp.desc);
+        Store(bcx, cloned_td, GEPi(bcx, ty_params_slot, [0, off]));
+        off += 1;
+        option::may(tp.dicts, {|dicts|
+            for dict in dicts {
+                let cast = PointerCast(bcx, dict, val_ty(cloned_td));
+                Store(bcx, cast, GEPi(bcx, ty_params_slot, [0, off]));
+                off += 1;
+            }
+        });
     }
 
     // Copy expr values into boxed bindings.
@@ -316,7 +329,7 @@ fn build_closure(bcx0: @block_ctxt,
           }
         }
     }
-    ret store_environment(bcx, copy bcx.fcx.lltydescs, env_vals, ck);
+    ret store_environment(bcx, copy bcx.fcx.lltyparams, env_vals, ck);
 }
 
 // Given an enclosing block context, a new function context, a closure type,
@@ -338,13 +351,23 @@ fn load_environment(enclosing_cx: @block_ctxt,
     // Populate the type parameters from the environment. We need to
     // do this first because the tydescs are needed to index into
     // the bindings if they are dynamically sized.
-    let tydesc_count = vec::len(enclosing_cx.fcx.lltydescs);
     let lltydescs = GEPi(bcx, llclosure,
                          [0, abi::box_rc_field_body,
                           abi::closure_elt_ty_params]);
-    uint::range(0u, tydesc_count) { |i|
-        let lltydescptr = GEPi(bcx, lltydescs, [0, i as int]);
-        fcx.lltydescs += [Load(bcx, lltydescptr)];
+    let off = 0;
+    for tp in copy enclosing_cx.fcx.lltyparams {
+        let tydesc = Load(bcx, GEPi(bcx, lltydescs, [0, off]));
+        off += 1;
+        let dicts = option::map(tp.dicts, {|dicts|
+            let rslt = [];
+            for dict in dicts {
+                let dict = Load(bcx, GEPi(bcx, lltydescs, [0, off]));
+                rslt += [PointerCast(bcx, dict, T_ptr(T_dict()))];
+                off += 1;
+            }
+            rslt
+        });
+        fcx.lltyparams += [{desc: tydesc, dicts: dicts}];
     }
 
     // Populate the upvars from the environment.
@@ -382,8 +405,7 @@ fn trans_expr_fn(bcx: @block_ctxt,
     if dest == ignore { ret bcx; }
     let ccx = bcx_ccx(bcx), bcx = bcx;
     let fty = node_id_type(ccx, id);
-    check returns_non_ty_var(ccx, fty);
-    let llfnty = type_of_fn_from_ty(ccx, sp, fty, 0u);
+    let llfnty = type_of_fn_from_ty(ccx, sp, fty, []);
     let sub_cx = extend_path(bcx.fcx.lcx, ccx.names.next("anon"));
     let s = mangle_internal_name_by_path(ccx, sub_cx.path);
     let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty);
@@ -436,19 +458,25 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
     }
 
     // Figure out which tydescs we need to pass, if any.
-    let outgoing_fty_real; // the type with typarams still in it
-    let lltydescs: [ValueRef];
-    alt f_res.generic {
-      none. { outgoing_fty_real = outgoing_fty; lltydescs = []; }
+    let (outgoing_fty_real, lltydescs, param_bounds) = alt f_res.generic {
+      none. { (outgoing_fty, [], @[]) }
       some(ginfo) {
+        for bounds in *ginfo.param_bounds {
+            for bound in *bounds {
+                alt bound {
+                  ty::bound_iface(_) {
+                    fail "FIXME[impl] binding bounded types not implemented";
+                  }
+                  _ {}
+                }
+            }
+        }
         lazily_emit_all_generic_info_tydesc_glues(cx, ginfo);
-        outgoing_fty_real = ginfo.item_type;
-        lltydescs = ginfo.tydescs;
+        (ginfo.item_type, ginfo.tydescs, ginfo.param_bounds)
       }
-    }
+    };
 
-    let ty_param_count = vec::len(lltydescs);
-    if vec::len(bound) == 0u && ty_param_count == 0u {
+    if vec::len(bound) == 0u && vec::len(lltydescs) == 0u {
         // Trivial 'binding': just return the closure
         let lv = lval_maybe_callee_to_lval(f_res, pair_ty);
         bcx = lv.bcx;
@@ -480,14 +508,14 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
 
     // Actually construct the closure
     let {llbox, box_ty, bcx} = store_environment(
-        bcx, lltydescs,
+        bcx, vec::map(lltydescs, {|d| {desc: d, dicts: none}}),
         env_vals + vec::map(bound, {|x| env_expr(x)}),
         ty::closure_shared);
 
     // Make thunk
     let llthunk =
         trans_bind_thunk(cx.fcx.lcx, cx.sp, pair_ty, outgoing_fty_real, args,
-                         box_ty, ty_param_count, target_res);
+                         box_ty, *param_bounds, target_res);
 
     // Fill the function pair
     fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, llbox);
@@ -558,7 +586,7 @@ fn trans_bind_thunk(cx: @local_ctxt,
                     outgoing_fty: ty::t,
                     args: [option::t<@ast::expr>],
                     boxed_closure_ty: ty::t,
-                    ty_param_count: uint,
+                    param_bounds: [ty::param_bounds],
                     target_fn: option::t<ValueRef>)
     -> {val: ValueRef, ty: TypeRef} {
     // If we supported constraints on record fields, we could make the
@@ -606,7 +634,7 @@ fn trans_bind_thunk(cx: @local_ctxt,
     // our bound tydescs, we need to load tydescs out of the environment
     // before derived tydescs are constructed. To do this, we load them
     // in the load_env block.
-    let load_env_bcx = new_raw_block_ctxt(fcx, fcx.llloadenv);
+    let l_bcx = new_raw_block_ctxt(fcx, fcx.llloadenv);
 
     // The 'llenv' that will arrive in the thunk we're creating is an
     // environment that will contain the values of its arguments and a pointer
@@ -616,7 +644,7 @@ fn trans_bind_thunk(cx: @local_ctxt,
     // 'boxed_closure_ty', which was determined by trans_bind.
     check (type_has_static_size(ccx, boxed_closure_ty));
     let llclosure_ptr_ty = type_of(ccx, sp, boxed_closure_ty);
-    let llclosure = PointerCast(load_env_bcx, fcx.llenv, llclosure_ptr_ty);
+    let llclosure = PointerCast(l_bcx, fcx.llenv, llclosure_ptr_ty);
 
     // "target", in this context, means the function that's having some of its
     // arguments bound and that will be called inside the thunk we're
@@ -667,19 +695,32 @@ fn trans_bind_thunk(cx: @local_ctxt,
     let llargs: [ValueRef] = [llretptr, lltargetenv];
 
     // Copy in the type parameters.
-    let i: uint = 0u;
-    while i < ty_param_count {
-        // Silly check
-        check type_is_tup_like(load_env_bcx, boxed_closure_ty);
-        let lltyparam_ptr =
-            GEP_tup_like(load_env_bcx, boxed_closure_ty, llclosure,
-                         [0, abi::box_rc_field_body,
-                          abi::closure_elt_ty_params, i as int]);
-        load_env_bcx = lltyparam_ptr.bcx;
-        let td = Load(load_env_bcx, lltyparam_ptr.val);
-        llargs += [td];
-        fcx.lltydescs += [td];
-        i += 1u;
+    check type_is_tup_like(l_bcx, boxed_closure_ty);
+    let {bcx: l_bcx, val: param_record} =
+        GEP_tup_like(l_bcx, boxed_closure_ty, llclosure,
+                     [0, abi::box_rc_field_body, abi::closure_elt_ty_params]);
+    let off = 0;
+    for param in param_bounds {
+        let dsc = Load(l_bcx, GEPi(l_bcx, param_record, [0, off])),
+            dicts = none;
+        llargs += [dsc];
+        off += 1;
+        for bound in *param {
+            alt bound {
+              ty::bound_iface(_) {
+                let dict = Load(l_bcx, GEPi(l_bcx, param_record, [0, off]));
+                dict = PointerCast(l_bcx, dict, T_ptr(T_dict()));
+                llargs += [dict];
+                off += 1;
+                dicts = some(alt dicts {
+                  none. { [dict] }
+                  some(ds) { ds + [dict] }
+                });
+              }
+              _ {}
+            }
+        }
+        fcx.lltyparams += [{desc: dsc, dicts: dicts}];
     }
 
     let a: uint = 2u; // retptr, env come first
@@ -737,13 +778,11 @@ fn trans_bind_thunk(cx: @local_ctxt,
     // needs to take.
     let ccx = bcx_ccx(bcx);
 
-    check returns_non_ty_var(ccx, outgoing_fty);
     let lltargetty =
-        type_of_fn_from_ty(ccx, sp, outgoing_fty, ty_param_count);
+        type_of_fn_from_ty(ccx, sp, outgoing_fty, param_bounds);
     lltargetfn = PointerCast(bcx, lltargetfn, T_ptr(lltargetty));
     Call(bcx, lltargetfn, llargs);
     build_return(bcx);
     finish_fn(fcx, lltop);
     ret {val: llthunk, ty: llthunk_ty};
 }
-
diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs
index 9d0f14e6bd1..fa0810fcf91 100644
--- a/src/comp/middle/trans_common.rs
+++ b/src/comp/middle/trans_common.rs
@@ -92,7 +92,6 @@ type crate_ctxt =
      discrims: hashmap<ast::def_id, ValueRef>,
      discrim_symbols: hashmap<ast::node_id, str>,
      consts: hashmap<ast::node_id, ValueRef>,
-     obj_methods: hashmap<ast::node_id, ()>,
      tydescs: hashmap<ty::t, @tydesc_info>,
      module_data: hashmap<str, ValueRef>,
      lltypes: hashmap<ty::t, TypeRef>,
@@ -105,6 +104,7 @@ type crate_ctxt =
      copy_map: alias::copy_map,
      last_uses: last_use::last_uses,
      method_map: typeck::method_map,
+     dict_map: typeck::dict_map,
      stats: stats,
      upcalls: @upcall::upcalls,
      rust_object_type: TypeRef,
@@ -131,6 +131,8 @@ type val_self_pair = {v: ValueRef, t: ty::t};
 
 tag local_val { local_mem(ValueRef); local_imm(ValueRef); }
 
+type fn_ty_param = {desc: ValueRef, dicts: option::t<[ValueRef]>};
+
 // Function context.  Every LLVM function we create will have one of
 // these.
 type fn_ctxt =
@@ -236,7 +238,7 @@ type fn_ctxt =
      llobjfields: hashmap<ast::node_id, ValueRef>,
      lllocals: hashmap<ast::node_id, local_val>,
      llupvars: hashmap<ast::node_id, ValueRef>,
-     mutable lltydescs: [ValueRef],
+     mutable lltyparams: [fn_ty_param],
      derived_tydescs: hashmap<ty::t, derived_tydesc_info>,
      id: ast::node_id,
      ret_style: ast::ret_style,
@@ -320,13 +322,13 @@ fn get_res_dtor(ccx: @crate_ctxt, sp: span, did: ast::def_id, inner_t: ty::t)
         }
     }
 
-    let params = csearch::get_type_param_count(ccx.sess.get_cstore(), did);
+    let param_bounds = ty::lookup_item_type(ccx.tcx, did).bounds;
     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,
                          [{mode: ast::by_ref, ty: inner_t}],
-                         nil_res, params);
+                         nil_res, *param_bounds);
     ret trans::get_extern_const(ccx.externs, ccx.llmod,
                                 csearch::get_symbol(ccx.sess.get_cstore(),
                                                     did), f_t);
@@ -406,7 +408,7 @@ fn ty_str(tn: type_names, t: TypeRef) -> str {
     ret lib::llvm::type_to_str(tn, t);
 }
 
-fn val_ty(v: ValueRef) -> TypeRef { ret llvm::LLVMTypeOf(v); }
+fn val_ty(&&v: ValueRef) -> TypeRef { ret llvm::LLVMTypeOf(v); }
 
 fn val_str(tn: type_names, v: ValueRef) -> str { ret ty_str(tn, val_ty(v)); }
 
@@ -533,11 +535,9 @@ fn T_size_t(targ_cfg: @session::config) -> TypeRef {
     ret T_int(targ_cfg);
 }
 
-fn T_fn(inputs: [TypeRef], output: TypeRef) -> TypeRef {
-    unsafe {
-        ret llvm::LLVMFunctionType(output, to_ptr(inputs),
-                                   vec::len::<TypeRef>(inputs), False);
-    }
+fn T_fn(inputs: [TypeRef], output: TypeRef) -> TypeRef unsafe {
+    ret llvm::LLVMFunctionType(output, to_ptr(inputs),
+                               vec::len::<TypeRef>(inputs), False);
 }
 
 fn T_fn_pair(cx: @crate_ctxt, tfn: TypeRef) -> TypeRef {
@@ -546,10 +546,8 @@ fn T_fn_pair(cx: @crate_ctxt, tfn: TypeRef) -> TypeRef {
 
 fn T_ptr(t: TypeRef) -> TypeRef { ret llvm::LLVMPointerType(t, 0u); }
 
-fn T_struct(elts: [TypeRef]) -> TypeRef {
-    unsafe {
-        ret llvm::LLVMStructType(to_ptr(elts), vec::len(elts), False);
-    }
+fn T_struct(elts: [TypeRef]) -> TypeRef unsafe {
+    ret llvm::LLVMStructType(to_ptr(elts), vec::len(elts), False);
 }
 
 fn T_named_struct(name: str) -> TypeRef {
@@ -574,6 +572,12 @@ fn T_rust_object() -> TypeRef {
     ret t;
 }
 
+// A dict is, in reality, a vtable pointer followed by zero or more pointers
+// to tydescs and other dicts that it closes over. But the types and number of
+// those are rarely known to the code that needs to manipulate them, so they
+// are described by this opaque type.
+fn T_dict() -> TypeRef { T_array(T_ptr(T_i8()), 1u) }
+
 fn T_task(targ_cfg: @session::config) -> TypeRef {
     let t = T_named_struct("task");
 
diff --git a/src/comp/middle/trans_impl.rs b/src/comp/middle/trans_impl.rs
new file mode 100644
index 00000000000..04bff49877f
--- /dev/null
+++ b/src/comp/middle/trans_impl.rs
@@ -0,0 +1,172 @@
+import trans::*;
+import trans_common::*;
+import trans_build::*;
+import option::{some, none};
+import syntax::{ast, ast_util};
+import back::link;
+import lib::llvm;
+import llvm::llvm::{ValueRef, TypeRef, LLVMGetParam};
+
+fn trans_impl(cx: @local_ctxt, name: ast::ident, methods: [@ast::method],
+              id: ast::node_id, tps: [ast::ty_param],
+              _ifce: option::t<@ast::ty>) {
+    let sub_cx = extend_path(cx, name);
+    for m in methods {
+        alt cx.ccx.item_ids.find(m.id) {
+          some(llfn) {
+            trans_fn(extend_path(sub_cx, m.ident), m.span, m.decl, m.body,
+                     llfn, impl_self(ty::node_id_to_monotype(cx.ccx.tcx, id)),
+                     tps + m.tps, m.id);
+          }
+        }
+    }
+}
+
+fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result {
+    let tz = [], tr = [];
+    let basety = ty::expr_ty(bcx_tcx(bcx), base);
+    let {bcx, val} = trans_arg_expr(bcx, {mode: ast::by_ref, ty: basety},
+                                    T_ptr(type_of_or_i8(bcx, basety)), tz,
+                                    tr, base);
+    rslt(bcx, PointerCast(bcx, val, T_opaque_boxed_closure_ptr(bcx_ccx(bcx))))
+}
+
+fn trans_static_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr,
+                       did: ast::def_id) -> lval_maybe_callee {
+    let {bcx, val} = trans_self_arg(bcx, base);
+    {env: obj_env(val) with lval_static_fn(bcx, did, e.id)}
+}
+
+fn trans_dict_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr,
+                     iface_id: ast::def_id, n_method: uint,
+                     n_param: uint, n_bound: uint) -> lval_maybe_callee {
+    let tcx = bcx_tcx(bcx);
+    let {bcx, val} = trans_self_arg(bcx, base);
+    let dict = option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound];
+    let method = ty::iface_methods(tcx, iface_id)[n_method];
+    let fty = ty::expr_ty(tcx, e);
+    let bare_fn_ty = type_of_fn_from_ty(bcx_ccx(bcx), ast_util::dummy_sp(),
+                                        fty, *method.tps);
+    let {inputs: bare_inputs, output} = llfn_arg_tys(bare_fn_ty);
+    let fn_ty = T_fn([val_ty(dict)] + bare_inputs, output);
+    let vtable = PointerCast(bcx, Load(bcx, GEPi(bcx, dict, [0, 0])),
+                             T_ptr(T_array(T_ptr(fn_ty), n_method + 1u)));
+    let mptr = Load(bcx, GEPi(bcx, vtable, [0, n_method as int]));
+    let generic = none;
+    if vec::len(*method.tps) > 0u {
+        let tydescs = [], tis = [];
+        for t in ty::node_id_to_type_params(tcx, e.id) {
+            // TODO: Doesn't always escape.
+            let ti = none;
+            let td = get_tydesc(bcx, t, true, tps_normal, ti).result;
+            tis += [ti];
+            tydescs += [td.val];
+            bcx = td.bcx;
+        }
+        generic = some({item_type: fty,
+                        static_tis: tis,
+                        tydescs: tydescs,
+                        param_bounds: method.tps,
+                        origins: bcx_ccx(bcx).dict_map.find(e.id)});
+    }
+    {bcx: bcx, val: mptr, kind: owned,
+     env: dict_env(dict, val),
+     generic: generic}
+}
+
+fn llfn_arg_tys(ft: TypeRef) -> {inputs: [TypeRef], output: TypeRef} {
+    let out_ty = llvm::llvm::LLVMGetReturnType(ft);
+    let n_args = llvm::llvm::LLVMCountParamTypes(ft);
+    let args = vec::init_elt(0 as TypeRef, n_args);
+    unsafe { llvm::llvm::LLVMGetParamTypes(ft, vec::to_ptr(args)); }
+    {inputs: args, output: out_ty}
+}
+
+fn trans_wrapper(ccx: @crate_ctxt, pt: [ast::ident],
+                 extra_tps: [ty::param_bounds], m: @ast::method) -> ValueRef {
+    let real_fn = ccx.item_ids.get(m.id);
+    let {inputs: real_args, output: real_ret} =
+        llfn_arg_tys(llvm::llvm::LLVMGetElementType(val_ty(real_fn)));
+    let extra_ptrs = [];
+    for tp in extra_tps {
+        extra_ptrs += [T_ptr(ccx.tydesc_type)];
+        for bound in *tp {
+            alt bound {
+              ty::bound_iface(_) { extra_ptrs += [T_ptr(T_dict())]; }
+              _ {}
+            }
+        }
+    }
+    let env_ty = T_ptr(T_struct([T_ptr(T_i8())] + extra_ptrs));
+    let n_extra_ptrs = vec::len(extra_ptrs);
+
+    let wrap_args = [T_ptr(T_dict())] + vec::slice(real_args, 0u, 2u) +
+        vec::slice(real_args, 2u + vec::len(extra_ptrs), vec::len(real_args));
+    let llfn_ty = T_fn(wrap_args, real_ret);
+
+    let lcx = @{path: pt + ["wrapper", m.ident], module_path: [],
+                obj_typarams: [], obj_fields: [], ccx: ccx};
+    let name = link::mangle_internal_name_by_path_and_seq(ccx, pt, m.ident);
+    let llfn = decl_internal_cdecl_fn(ccx.llmod, name, llfn_ty);
+    let fcx = new_fn_ctxt(lcx, ast_util::dummy_sp(), llfn);
+    let bcx = new_top_block_ctxt(fcx), lltop = bcx.llbb;
+
+    let dict = PointerCast(bcx, LLVMGetParam(llfn, 0u), env_ty);
+    // retptr, self
+    let args = [LLVMGetParam(llfn, 1u), LLVMGetParam(llfn, 2u)], i = 0u;
+    // saved tydescs/dicts
+    while i < n_extra_ptrs {
+        i += 1u;
+        args += [load_inbounds(bcx, dict, [0, i as int])];
+    }
+    // the rest of the parameters
+    let i = 3u, params_total = llvm::llvm::LLVMCountParamTypes(llfn_ty);
+    while i < params_total {
+        args += [LLVMGetParam(llfn, i)];
+        i += 1u;
+    }
+    Call(bcx, ccx.item_ids.get(m.id), args);
+    build_return(bcx);
+    finish_fn(fcx, lltop);
+    ret llfn;
+}
+
+// FIXME[impl] cache these on the function level somehow
+fn get_dict(bcx: @block_ctxt, origin: typeck::dict_origin) -> result {
+    let bcx = bcx, ccx = bcx_ccx(bcx);
+    alt origin {
+      typeck::dict_static(impl_did, tys, sub_origins) {
+        assert impl_did.crate == ast::local_crate; // FIXME[impl]
+        let vtable = ccx.item_ids.get(impl_did.node);
+        let impl_params = ty::lookup_item_type(ccx.tcx, impl_did).bounds;
+        let ptrs = [vtable], i = 0u, origin = 0u, ti = none;
+        for param in *impl_params {
+            let rslt = get_tydesc(bcx, tys[i], false, tps_normal, ti).result;
+            ptrs += [rslt.val];
+            bcx = rslt.bcx;
+            for bound in *param {
+                alt bound {
+                  ty::bound_iface(_) {
+                    let res = get_dict(bcx, sub_origins[origin]);
+                    ptrs += [res.val];
+                    bcx = res.bcx;
+                    origin += 1u;
+                  }
+                  _ {}
+                }
+            }
+            i += 1u;
+        }
+        let pty = T_ptr(T_i8()), dict_ty = T_array(pty, vec::len(ptrs));
+        let dict = alloca(bcx, dict_ty), i = 0;
+        for ptr in ptrs {
+            Store(bcx, PointerCast(bcx, ptr, pty), GEPi(bcx, dict, [0, i]));
+            i += 1;
+        }
+        rslt(bcx, PointerCast(bcx, dict, T_ptr(T_dict())))
+      }
+      typeck::dict_param(n_param, n_bound) {
+        rslt(bcx, option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound])
+      }
+    }
+}
\ No newline at end of file
diff --git a/src/comp/middle/trans_objects.rs b/src/comp/middle/trans_objects.rs
index db50c28b85a..913cca74192 100644
--- a/src/comp/middle/trans_objects.rs
+++ b/src/comp/middle/trans_objects.rs
@@ -158,7 +158,7 @@ fn trans_obj(cx: @local_ctxt, sp: span, ob: ast::_obj, ctor_id: ast::node_id,
         let typarams_ty: ty::t = ty::mk_tup(ccx.tcx, tps);
         let i: int = 0;
         for tp: ast::ty_param in ty_params {
-            let typaram = bcx.fcx.lltydescs[i];
+            let typaram = bcx.fcx.lltyparams[i].desc;
             // Silly check
             check type_is_tup_like(bcx, typarams_ty);
             let capture =
@@ -880,8 +880,9 @@ fn process_normal_mthd(cx: @local_ctxt, m: @ast::method, self_ty: ty::t,
       ty::ty_fn(f) {
         let out = f.output;
         check non_ty_var(ccx, out);
-        llfnty = type_of_fn(ccx, m.span, true, f.inputs, out,
-                            vec::len(ty_params));
+        llfnty = type_of_fn(
+            ccx, m.span, true, f.inputs, out,
+            vec::map(ty_params, {|p| param_bounds(ccx, p)}));
       }
     }
     let mcx: @local_ctxt =
@@ -933,7 +934,8 @@ fn type_of_meth(ccx: @crate_ctxt, sp: span, m: @ty::method,
                 tps: [ast::ty_param]) -> TypeRef {
     let out_ty = m.fty.output;
     check non_ty_var(ccx, out_ty);
-    type_of_fn(ccx, sp, true, m.fty.inputs, out_ty, vec::len(tps))
+    type_of_fn(ccx, sp, true, m.fty.inputs, out_ty,
+               vec::map(tps, {|p| param_bounds(ccx, p)}))
 }
 
 //
diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs
index 5b12477d873..10d311a5af9 100644
--- a/src/comp/middle/tstate/auxiliary.rs
+++ b/src/comp/middle/tstate/auxiliary.rs
@@ -50,12 +50,14 @@ fn comma_str(args: [@constr_arg_use]) -> str {
 fn constraint_to_str(tcx: ty::ctxt, c: sp_constr) -> str {
     alt c.node {
       ninit(id, i) {
-        ret #fmt("init(%s id=%d [%s])",
+        ret #fmt("init(%s id=%d - arising from %s)",
                  i, id, tcx.sess.span_str(c.span));
       }
       npred(p, _, args) {
-        ret path_to_str(p) + "(" + comma_str(args) + ")" + "[" +
-                tcx.sess.span_str(c.span) + "]";
+          ret #fmt("%s(%s) - arising from %s",
+                   path_to_str(p),
+                   comma_str(args),
+                   tcx.sess.span_str(c.span));
       }
     }
 }
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 2f853f5eb84..1a0a295ed45 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -18,7 +18,7 @@ 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_1;
+import util::ppaux::mode_str;
 import syntax::print::pprust::*;
 
 export node_id_to_monotype;
@@ -185,7 +185,7 @@ export closure_kind;
 export closure_block;
 export closure_shared;
 export closure_send;
-export param_bound, bound_copy, bound_send, bound_iface;
+export param_bound, param_bounds, bound_copy, bound_send, bound_iface;
 export param_bounds_to_kind;
 
 // Data types
@@ -194,7 +194,9 @@ type arg = {mode: mode, ty: t};
 
 type field = {ident: ast::ident, mt: mt};
 
-type method = {ident: ast::ident, tps: [@[param_bound]], fty: fn_ty};
+type param_bounds = @[param_bound];
+
+type method = {ident: ast::ident, tps: @[param_bounds], fty: fn_ty};
 
 type constr_table = hashmap<ast::node_id, [constr]>;
 
@@ -218,9 +220,9 @@ type ctxt =
       needs_drop_cache: hashmap<t, bool>,
       kind_cache: hashmap<t, kind>,
       ast_ty_to_ty_cache: hashmap<@ast::ty, option::t<t>>,
-      tag_var_cache: hashmap<ast::def_id, @[variant_info]>,
+      tag_var_cache: hashmap<def_id, @[variant_info]>,
       iface_method_cache: hashmap<def_id, @[method]>,
-      ty_param_bounds: hashmap<def_id, @[param_bound]>};
+      ty_param_bounds: hashmap<ast::node_id, param_bounds>};
 
 type ty_ctxt = ctxt;
 
@@ -268,7 +270,7 @@ tag sty {
     ty_tup([t]);
     ty_var(int); // type variable
 
-    ty_param(uint, @[param_bound]); // fn/tag type param
+    ty_param(uint, def_id); // fn/tag type param
 
     ty_type; // type_desc*
     ty_send_type; // type_desc* that has been cloned into exchange heap
@@ -308,7 +310,7 @@ tag param_bound {
     bound_iface(t);
 }
 
-fn param_bounds_to_kind(bounds: @[param_bound]) -> kind {
+fn param_bounds_to_kind(bounds: param_bounds) -> kind {
     let kind = kind_noncopyable;
     for bound in *bounds {
         alt bound {
@@ -322,7 +324,7 @@ fn param_bounds_to_kind(bounds: @[param_bound]) -> kind {
     kind
 }
 
-type ty_param_bounds_and_ty = @{bounds: [@[param_bound]], ty: t};
+type ty_param_bounds_and_ty = {bounds: @[param_bounds], ty: t};
 
 type type_cache = hashmap<ast::def_id, ty_param_bounds_and_ty>;
 
@@ -439,7 +441,7 @@ 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),
           tag_var_cache: new_def_hash(),
           iface_method_cache: new_def_hash(),
-          ty_param_bounds: new_def_hash()};
+          ty_param_bounds: map::new_int_hash()};
     populate_type_store(cx);
     ret cx;
 }
@@ -624,7 +626,7 @@ fn mk_res(cx: ctxt, did: ast::def_id, inner: t, tps: [t]) -> t {
 
 fn mk_var(cx: ctxt, v: int) -> t { ret gen_ty(cx, ty_var(v)); }
 
-fn mk_param(cx: ctxt, n: uint, k: @[param_bound]) -> t {
+fn mk_param(cx: ctxt, n: uint, k: def_id) -> t {
     ret gen_ty(cx, ty_param(n, k));
 }
 
@@ -722,7 +724,7 @@ fn walk_ty(cx: ctxt, walker: ty_walk, ty: t) {
 
 tag fold_mode {
     fm_var(fn@(int) -> t);
-    fm_param(fn@(uint, @[param_bound]) -> t);
+    fm_param(fn@(uint, def_id) -> t);
     fm_general(fn@(t) -> t);
 }
 
@@ -813,8 +815,14 @@ fn fold_ty(cx: ctxt, fld: fold_mode, ty_0: t) -> t {
       ty_var(id) {
         alt fld { fm_var(folder) { ty = folder(id); } _ {/* no-op */ } }
       }
-      ty_param(id, k) {
-        alt fld { fm_param(folder) { ty = folder(id, k); } _ {/* no-op */ } }
+      ty_param(id, did) {
+        alt fld { fm_param(folder) { ty = folder(id, did); } _ {} }
+      }
+      ty_constr(subty, cs) {
+          ty = mk_constr(cx, fold_ty(cx, fld, subty), cs);
+      }
+      _ {
+          cx.sess.fatal("Unsupported sort of type in fold_ty");
       }
     }
 
@@ -1083,7 +1091,9 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
       }
       // Resources are always noncopyable.
       ty_res(did, inner, tps) { kind_noncopyable }
-      ty_param(_, bounds) { param_bounds_to_kind(bounds) }
+      ty_param(_, did) {
+          param_bounds_to_kind(cx.ty_param_bounds.get(did.node))
+      }
       ty_constr(t, _) { type_kind(cx, t) }
     };
 
@@ -1131,7 +1141,7 @@ fn type_structurally_contains(cx: ctxt, ty: t, test: fn(sty) -> bool) ->
     }
 }
 
-pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool {
+pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool unchecked {
 
     /* type_structurally_contains can't be declared pure
     because it takes a function argument. But it should be
@@ -1141,15 +1151,9 @@ pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool {
     actually checkable. It seems to me like a lot of properties
     that the type context tracks about types should be immutable.)
     */
-    unchecked{
-        type_structurally_contains(cx, ty,
-                                   fn (sty: sty) -> bool {
-                                       ret alt sty {
-                                             ty_param(_, _) { true }
-                                             _ { false }
-                                           };
-                                   })
-    }
+    type_structurally_contains(cx, ty, fn (sty: sty) -> bool {
+        alt sty { ty_param(_, _) { true } _ { false }}
+    })
 }
 
 // Returns true for noncopyable types and types where a copy of a value can be
@@ -1464,8 +1468,6 @@ fn constrs_eq(cs: [@constr], ds: [@constr]) -> bool {
 // Type lookups
 fn node_id_to_ty_param_substs_opt_and_ty(cx: ctxt, id: ast::node_id) ->
    ty_param_substs_opt_and_ty {
-
-
     // Pull out the node type table.
     alt smallintmap::find(*cx.node_types, id as uint) {
       none. {
@@ -1737,6 +1739,7 @@ mod unify {
     export ures_ok;
     export ures_err;
     export var_bindings;
+    export precise, in_bindings;
 
     tag result { ures_ok(t); ures_err(type_err); }
     tag union_result { unres_ok; unres_err(type_err); }
@@ -1747,7 +1750,11 @@ mod unify {
     type var_bindings =
         {sets: ufind::ufind, types: smallintmap::smallintmap<t>};
 
-    type ctxt = {vb: option::t<@var_bindings>, tcx: ty_ctxt};
+    tag unify_style {
+        precise;
+        in_bindings(@var_bindings);
+    }
+    type ctxt = {st: unify_style, tcx: ty_ctxt};
 
     fn mk_var_bindings() -> @var_bindings {
         ret @{sets: ufind::make(), types: smallintmap::mk::<t>()};
@@ -1756,7 +1763,9 @@ mod unify {
     // Unifies two sets.
     fn union(cx: @ctxt, set_a: uint, set_b: uint,
              variance: variance) -> union_result {
-        let vb = option::get(cx.vb);
+        let vb = alt cx.st {
+            in_bindings(vb) { vb }
+        };
         ufind::grow(vb.sets, float::max(set_a, set_b) + 1u);
         let root_a = ufind::find(vb.sets, set_a);
         let root_b = ufind::find(vb.sets, set_b);
@@ -1806,7 +1815,7 @@ mod unify {
     fn record_var_binding(
         cx: @ctxt, key: int, typ: t, variance: variance) -> result {
 
-        let vb = option::get(cx.vb);
+        let vb = alt cx.st { in_bindings(vb) { vb } };
         ufind::grow(vb.sets, (key as uint) + 1u);
         let root = ufind::find(vb.sets, key as uint);
         let result_type = typ;
@@ -2142,7 +2151,6 @@ mod unify {
           // If the RHS is a variable type, then just do the
           // appropriate binding.
           ty::ty_var(actual_id) {
-            assert option::is_some(cx.vb);
             let actual_n = actual_id as uint;
             alt struct(cx.tcx, expected) {
               ty::ty_var(expected_id) {
@@ -2167,7 +2175,6 @@ mod unify {
         }
         alt struct(cx.tcx, expected) {
           ty::ty_var(expected_id) {
-            assert option::is_some(cx.vb);
             // Add a binding. (`actual` can't actually be a var here.)
             alt record_var_binding_for_expected(
                 cx, expected_id, actual,
@@ -2205,7 +2212,14 @@ mod unify {
               _ { ret ures_err(terr_mismatch); }
             }
           }
-          ty::ty_param(_, _) { ret struct_cmp(cx, expected, actual); }
+          ty::ty_param(expected_n, _) {
+            alt struct(cx.tcx, actual) {
+              ty::ty_param(actual_n, _) when expected_n == actual_n {
+                ret ures_ok(expected);
+              }
+              _ { ret ures_err(terr_mismatch); }
+            }
+          }
           ty::ty_tag(expected_id, expected_tps) {
             alt struct(cx.tcx, actual) {
               ty::ty_tag(actual_id, actual_tps) {
@@ -2477,9 +2491,9 @@ mod unify {
           }
         }
     }
-    fn unify(expected: t, actual: t, vb: option::t<@var_bindings>,
+    fn unify(expected: t, actual: t, st: unify_style,
              tcx: ty_ctxt) -> result {
-        let cx = @{vb: vb, tcx: tcx};
+        let cx = @{st: st, tcx: tcx};
         ret unify_step(cx, expected, actual, covariant);
     }
     fn dump_var_bindings(tcx: ty_ctxt, vb: @var_bindings) {
@@ -2552,7 +2566,7 @@ mod unify {
 }
 
 fn same_type(cx: ctxt, a: t, b: t) -> bool {
-    alt unify::unify(a, b, none, cx) {
+    alt unify::unify(a, b, unify::precise, cx) {
       unify::ures_ok(_) { true }
       _ { false }
     }
@@ -2602,8 +2616,8 @@ fn type_err_to_str(err: ty::type_err) -> str {
                 "' but found one with method '" + a_meth + "'";
       }
       terr_mode_mismatch(e_mode, a_mode) {
-        ret "expected argument mode " + mode_str_1(e_mode) + " but found " +
-                mode_str_1(a_mode);
+        ret "expected argument mode " + mode_str(e_mode) + " but found " +
+                mode_str(a_mode);
       }
       terr_constr_len(e_len, a_len) {
         ret "Expected a type with " + uint::str(e_len) +
@@ -2621,25 +2635,17 @@ fn type_err_to_str(err: ty::type_err) -> str {
 
 // Converts type parameters in a type to type variables and returns the
 // resulting type along with a list of type variable IDs.
-fn bind_params_in_type(sp: span, cx: ctxt, next_ty_var: fn@() -> int, typ: t,
+fn bind_params_in_type(cx: ctxt, next_ty_var: block() -> int, typ: t,
                        ty_param_count: uint) -> {ids: [int], ty: t} {
-    let param_var_ids: @mutable [int] = @mutable [];
-    let i = 0u;
-    while i < ty_param_count { *param_var_ids += [next_ty_var()]; i += 1u; }
-    fn binder(sp: span, cx: ctxt, param_var_ids: @mutable [int],
-              _next_ty_var: fn@() -> int, index: uint,
-              _bounds: @[param_bound]) -> t {
-        if index < vec::len(*param_var_ids) {
-            ret mk_var(cx, param_var_ids[index]);
-        } else {
-            cx.sess.span_fatal(sp, "Unbound type parameter in callee's type");
-        }
+    let param_var_ids = [], i = 0u;
+    while i < ty_param_count { param_var_ids += [next_ty_var()]; i += 1u; }
+    let param_var_ids = @param_var_ids;
+    fn binder(cx: ctxt, param_var_ids: @[int], index: uint,
+              _did: def_id) -> t {
+        ret mk_var(cx, param_var_ids[index]);
     }
-    let new_typ =
-        fold_ty(cx,
-                fm_param(bind binder(sp, cx, param_var_ids, next_ty_var, _,
-                                     _)), typ);
-    ret {ids: *param_var_ids, ty: new_typ};
+    {ids: *param_var_ids,
+     ty: fold_ty(cx, fm_param(bind binder(cx, param_var_ids, _, _)), typ)}
 }
 
 
@@ -2647,9 +2653,8 @@ fn bind_params_in_type(sp: span, cx: ctxt, next_ty_var: fn@() -> int, typ: t,
 // substitions.
 fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t {
     if !type_contains_params(cx, typ) { ret typ; }
-    fn substituter(_cx: ctxt, substs: @[ty::t], idx: uint,
-                   _bounds: @[param_bound])
-       -> t {
+    fn substituter(_cx: ctxt, substs: @[ty::t], idx: uint, _did: def_id)
+        -> t {
         // FIXME: bounds check can fail
         ret substs[idx];
     }
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index 5d65ce78049..2becc452ebc 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -7,7 +7,7 @@ import driver::session;
 import util::common::*;
 import syntax::codemap::span;
 import middle::ty;
-import middle::ty::{node_id_to_type, arg, bind_params_in_type, block_ty,
+import middle::ty::{node_id_to_type, arg, block_ty,
                     expr_ty, field, node_type_table, mk_nil,
                     ty_param_substs_opt_and_ty, ty_param_bounds_and_ty};
 import util::ppaux::ty_to_str;
@@ -18,9 +18,24 @@ import std::map::{hashmap, new_int_hash};
 import option::{none, some};
 import syntax::print::pprust::*;
 
-export check_crate, method_map;
+export check_crate, method_map, method_origin, method_static, method_param;
+export dict_map, dict_res, dict_origin, dict_static, dict_param;
 
-type method_map = hashmap<ast::node_id, ast::def_id>;
+tag method_origin {
+    method_static(ast::def_id);
+    // iface id, method num, param num, bound num
+    method_param(ast::def_id, uint, uint, uint);
+}
+type method_map = hashmap<ast::node_id, method_origin>;
+
+// Resolutions for bounds of all parameters, left to right, for a given path.
+type dict_res = @[dict_origin];
+tag dict_origin {
+    dict_static(ast::def_id, [ty::t], dict_res);
+    // Param number, bound number
+    dict_param(uint, uint);
+}
+type dict_map = hashmap<ast::node_id, dict_res>;
 
 type ty_table = hashmap<ast::def_id, ty::t>;
 
@@ -33,6 +48,7 @@ tag self_info {
 type crate_ctxt = {mutable self_infos: [self_info],
                    impl_map: resolve::impl_map,
                    method_map: method_map,
+                   dict_map: dict_map,
                    tcx: ty::ctxt};
 
 type fn_ctxt =
@@ -76,22 +92,22 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
       ast::def_arg(id, _) {
         assert (fcx.locals.contains_key(id.node));
         let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, id.node));
-        ret @{bounds: [], ty: typ};
+        ret {bounds: @[], ty: typ};
       }
       ast::def_local(id, _) {
         assert (fcx.locals.contains_key(id.node));
         let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, id.node));
-        ret @{bounds: [], ty: typ};
+        ret {bounds: @[], ty: typ};
       }
       ast::def_obj_field(id, _) {
         assert (fcx.locals.contains_key(id.node));
         let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, id.node));
-        ret @{bounds: [], ty: typ};
+        ret {bounds: @[], ty: typ};
       }
       ast::def_self(id) {
         alt get_self_info(fcx.ccx) {
           some(self_obj(_, obj_t)) | some(self_impl(obj_t)) {
-            ret @{bounds: [], ty: obj_t};
+            ret {bounds: @[], ty: obj_t};
           }
         }
       }
@@ -102,12 +118,12 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
       ast::def_binding(id) {
         assert (fcx.locals.contains_key(id.node));
         let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, id.node));
-        ret @{bounds: [], ty: typ};
+        ret {bounds: @[], ty: typ};
       }
       ast::def_mod(_) {
         // Hopefully part of a path.
         // TODO: return a type that's more poisonous, perhaps?
-        ret @{bounds: [], ty: ty::mk_nil(fcx.ccx.tcx)};
+        ret {bounds: @[], ty: ty::mk_nil(fcx.ccx.tcx)};
       }
       ast::def_ty(_) {
         fcx.ccx.tcx.sess.span_fatal(sp, "expected value but found type");
@@ -122,16 +138,18 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
     }
 }
 
+fn bind_params(fcx: @fn_ctxt, tp: ty::t, count: uint)
+    -> {ids: [int], ty: ty::t} {
+    ty::bind_params_in_type(fcx.ccx.tcx, {|| next_ty_var_id(fcx)}, tp, count)
+}
 
 // Instantiates the given path, which must refer to an item with the given
 // number of type parameters and type.
 fn instantiate_path(fcx: @fn_ctxt, pth: @ast::path,
                     tpt: ty_param_bounds_and_ty, sp: span)
     -> ty_param_substs_opt_and_ty {
-    let ty_param_count = vec::len(tpt.bounds);
-    let bind_result =
-        bind_params_in_type(sp, fcx.ccx.tcx, bind next_ty_var_id(fcx), tpt.ty,
-                            ty_param_count);
+    let ty_param_count = vec::len(*tpt.bounds);
+    let bind_result = bind_params(fcx, tpt.ty, ty_param_count);
     let ty_param_vars = bind_result.ids;
     let ty_substs_opt;
     let ty_substs_len = vec::len::<@ast::ty>(pth.node.types);
@@ -282,13 +300,13 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t {
         // "foo = int" like OCaml?
 
         let ty_param_bounds_and_ty = getter(tcx, mode, id);
-        if vec::len(ty_param_bounds_and_ty.bounds) == 0u {
+        if vec::len(*ty_param_bounds_and_ty.bounds) == 0u {
             ret ty_param_bounds_and_ty.ty;
         }
 
         // The typedef is type-parametric. Do the type substitution.
         let param_bindings: [ty::t] = [];
-        if vec::len(args) != vec::len(ty_param_bounds_and_ty.bounds) {
+        if vec::len(args) != vec::len(*ty_param_bounds_and_ty.bounds) {
             tcx.sess.span_fatal(sp, "Wrong number of type arguments for a \
                                      polymorphic type");
         }
@@ -343,7 +361,7 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t {
           }
           some(ast::def_native_ty(id)) { typ = getter(tcx, mode, id).ty; }
           some(ast::def_ty_param(id, n)) {
-            typ = ty::mk_param(tcx, n, tcx.ty_param_bounds.get(id));
+            typ = ty::mk_param(tcx, n, id);
           }
           some(_) {
             tcx.sess.span_fatal(ast_ty.span,
@@ -382,7 +400,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
     alt it.node {
       ast::item_const(t, _) {
         let typ = ast_ty_to_ty(tcx, mode, t);
-        let tpt = @{bounds: [], ty: typ};
+        let tpt = {bounds: @[], ty: typ};
         tcx.tcache.insert(local_def(it.id), tpt);
         ret tpt;
       }
@@ -401,7 +419,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
         }
         // Tell ast_ty_to_ty() that we want to perform a recursive
         // call to resolve any named types.
-        let tpt = @{bounds: ty_param_bounds(tcx, mode, tps),
+        let tpt = {bounds: ty_param_bounds(tcx, mode, tps),
                    ty: ty::mk_named(tcx, ast_ty_to_ty(tcx, mode, t),
                                     @it.ident)};
         tcx.tcache.insert(local_def(it.id), tpt);
@@ -413,7 +431,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
         let t = ty::mk_named(tcx, ty::mk_res(tcx, local_def(it.id), t_arg.ty,
                                              params),
                              @it.ident);
-        let t_res = @{bounds: bounds, ty: t};
+        let t_res = {bounds: bounds, ty: t};
         tcx.tcache.insert(local_def(it.id), t_res);
         ret t_res;
       }
@@ -422,7 +440,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
         let {bounds, params} = mk_ty_params(tcx, tps);
         let t = ty::mk_named(tcx, ty::mk_tag(tcx, local_def(it.id), params),
                              @it.ident);
-        let tpt = @{bounds: bounds, ty: t};
+        let tpt = {bounds: bounds, ty: t};
         tcx.tcache.insert(local_def(it.id), tpt);
         ret tpt;
       }
@@ -431,7 +449,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
         let t = ty::mk_named(tcx, ty::mk_iface(tcx, local_def(it.id),
                                                params),
                              @it.ident);
-        let tpt = @{bounds: bounds, ty: t};
+        let tpt = {bounds: bounds, ty: t};
         tcx.tcache.insert(local_def(it.id), tpt);
         ty::store_iface_methods(tcx, it.id, @vec::map(ms, {|m|
             ty_of_ty_method(tcx, m_collect, m)
@@ -455,7 +473,7 @@ fn ty_of_native_item(tcx: ty::ctxt, mode: mode, it: @ast::native_item)
           none. { }
         }
         let t = ty::mk_native(tcx, ast_util::local_def(it.id));
-        let tpt = @{bounds: [], ty: t};
+        let tpt = {bounds: @[], ty: t};
         tcx.tcache.insert(local_def(it.id), tpt);
         ret tpt;
       }
@@ -483,7 +501,7 @@ fn ty_of_fn(tcx: ty::ctxt, mode: mode, decl: ast::fn_decl,
     -> ty::ty_param_bounds_and_ty {
     let bounds = ty_param_bounds(tcx, mode, ty_params);
     let tofd = ty_of_fn_decl(tcx, mode, ast::proto_bare, decl);
-    let tpt = @{bounds: bounds, ty: ty::mk_fn(tcx, tofd)};
+    let tpt = {bounds: bounds, ty: ty::mk_fn(tcx, tofd)};
     tcx.tcache.insert(def_id, tpt);
     ret tpt;
 }
@@ -495,15 +513,15 @@ fn ty_of_native_fn_decl(tcx: ty::ctxt, mode: mode, decl: ast::fn_decl,
     let output_ty = ast_ty_to_ty(tcx, mode, decl.output);
 
     let t_fn = ty::mk_native_fn(tcx, input_tys, output_ty);
-    let tpt = @{bounds: bounds, ty: t_fn};
+    let tpt = {bounds: bounds, ty: t_fn};
     tcx.tcache.insert(def_id, tpt);
     ret tpt;
 }
 fn ty_param_bounds(tcx: ty::ctxt, mode: mode, params: [ast::ty_param])
-    -> [@[ty::param_bound]] {
+    -> @[ty::param_bounds] {
     let result = [];
     for param in params {
-        result += [alt tcx.ty_param_bounds.find(local_def(param.id)) {
+        result += [alt tcx.ty_param_bounds.find(param.id) {
           some(bs) { bs }
           none. {
             let bounds = [];
@@ -517,12 +535,12 @@ fn ty_param_bounds(tcx: ty::ctxt, mode: mode, params: [ast::ty_param])
                 }];
             }
             let boxed = @bounds;
-            tcx.ty_param_bounds.insert(local_def(param.id), boxed);
+            tcx.ty_param_bounds.insert(param.id, boxed);
             boxed
           }
         }];
     }
-    result
+    @result
 }
 fn ty_of_method(tcx: ty::ctxt, mode: mode, m: @ast::method) -> ty::method {
     {ident: m.ident, tps: ty_param_bounds(tcx, mode, m.tps),
@@ -539,7 +557,7 @@ fn ty_of_obj(tcx: ty::ctxt, mode: mode, id: ast::ident, ob: ast::_obj,
     let methods = vec::map(ob.methods, {|m| ty_of_method(tcx, mode, m)});
     let t_obj = ty::mk_named(tcx, ty::mk_obj(tcx, ty::sort_methods(methods)),
                              @id);
-    ret @{bounds: bounds, ty: t_obj};
+    ret {bounds: bounds, ty: t_obj};
 }
 fn ty_of_obj_ctor(tcx: ty::ctxt, mode: mode, id: ast::ident, ob: ast::_obj,
             ctor_id: ast::node_id, ty_params: [ast::ty_param])
@@ -553,7 +571,7 @@ fn ty_of_obj_ctor(tcx: ty::ctxt, mode: mode, id: ast::ident, ob: ast::_obj,
     let t_fn = ty::mk_fn(tcx, {proto: ast::proto_shared(ast::sugar_normal),
                                inputs: t_inputs, output: t_obj.ty,
                                ret_style: ast::return_val, constraints: []});
-    let tpt = @{bounds: ty_param_bounds(tcx, mode, ty_params), ty: t_fn};
+    let tpt = {bounds: ty_param_bounds(tcx, mode, ty_params), ty: t_fn};
     tcx.tcache.insert(local_def(ctor_id), tpt);
     ret tpt;
 }
@@ -622,11 +640,11 @@ mod write {
 }
 
 fn mk_ty_params(tcx: ty::ctxt, atps: [ast::ty_param])
-    -> {bounds: [@[ty::param_bound]], params: [ty::t]} {
+    -> {bounds: @[ty::param_bounds], params: [ty::t]} {
     let i = 0u, bounds = ty_param_bounds(tcx, m_collect, atps);
     {bounds: bounds,
-     params: vec::map(atps, {|_atp|
-         let t = ty::mk_param(tcx, i, bounds[i]);
+     params: vec::map(atps, {|atp|
+         let t = ty::mk_param(tcx, i, local_def(atp.id));
          i += 1u;
          t
      })}
@@ -674,7 +692,7 @@ mod collect {
                            inputs: args, output: tag_ty,
                            ret_style: ast::return_val, constraints: []})
             };
-            let tpt = @{bounds: ty_param_bounds(cx.tcx, m_collect, ty_params),
+            let tpt = {bounds: ty_param_bounds(cx.tcx, m_collect, ty_params),
                        ty: result_ty};
             cx.tcx.tcache.insert(local_def(variant.node.id), tpt);
             write::ty_only(cx.tcx, variant.node.id, result_ty);
@@ -690,14 +708,15 @@ mod collect {
             get_tag_variant_types(cx, tpt.ty, variants, ty_params);
           }
           ast::item_impl(tps, _, selfty, ms) {
-            ty_param_bounds(cx.tcx, m_collect, tps);
+            let i_bounds = ty_param_bounds(cx.tcx, m_collect, tps);
             for m in ms {
                 let bounds = ty_param_bounds(cx.tcx, m_collect, m.tps);
                 let ty = ty::mk_fn(cx.tcx,
                                    ty_of_fn_decl(cx.tcx, m_collect,
                                                  ast::proto_bare, m.decl));
-                cx.tcx.tcache.insert(local_def(m.id), @{bounds: bounds,
-                                                       ty: ty});
+                cx.tcx.tcache.insert(local_def(m.id),
+                                     {bounds: @(*i_bounds + *bounds),
+                                      ty: ty});
                 write::ty_only(cx.tcx, m.id, ty);
             }
             write::ty_only(cx.tcx, it.id, ast_ty_to_ty(cx.tcx, m_collect,
@@ -754,8 +773,7 @@ mod collect {
             write::ty_only(cx.tcx, it.id, t_res);
             write::ty_only(cx.tcx, ctor_id, t_ctor);
             cx.tcx.tcache.insert(local_def(ctor_id),
-                                 @{bounds: bounds,
-                                  ty: t_ctor});
+                                 {bounds: bounds, ty: t_ctor});
             write::ty_only(cx.tcx, dtor_id, t_dtor);
           }
           _ {
@@ -798,7 +816,8 @@ mod collect {
 mod unify {
     fn unify(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) ->
        ty::unify::result {
-        ret ty::unify::unify(expected, actual, some(fcx.var_bindings),
+        ret ty::unify::unify(expected, actual,
+                             ty::unify::in_bindings(fcx.var_bindings),
                              fcx.ccx.tcx);
     }
 }
@@ -1115,7 +1134,8 @@ fn gather_locals(ccx: @crate_ctxt,
             alt ty_opt {
               none. {/* nothing to do */ }
               some(typ) {
-                ty::unify::unify(ty::mk_var(tcx, var_id), typ, some(vb), tcx);
+                ty::unify::unify(ty::mk_var(tcx, var_id), typ,
+                                 ty::unify::in_bindings(vb), tcx);
               }
             }
         };
@@ -1465,39 +1485,92 @@ fn check_expr_with(fcx: @fn_ctxt, expr: @ast::expr, expected: ty::t) -> bool {
     ret check_expr_with_unifier(fcx, expr, demand::simple, expected);
 }
 
+fn impl_self_ty(tcx: ty::ctxt, did: ast::def_id) -> {n_tps: uint, ty: ty::t} {
+    if did.crate == ast::local_crate {
+        alt tcx.items.get(did.node) {
+          ast_map::node_item(@{node: ast::item_impl(ts, _, st, _),
+                               _}) {
+            {n_tps: vec::len(ts), ty: ast_ty_to_ty(tcx, m_check, st)}
+          }
+        }
+    } else {
+        let tpt = csearch::get_type(tcx, did);
+        {n_tps: vec::len(*tpt.bounds), ty: tpt.ty}
+    }
+}
+
 fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes,
                  name: ast::ident, ty: ty::t, sp: span)
-    -> option::t<{method: @resolve::method_info, ids: [int]}> {
+    -> option::t<{method_ty: ty::t, n_tps: uint, substs: [ty::t],
+                  origin: method_origin}> {
+    let tcx = fcx.ccx.tcx;
+
+    // First, see whether this is an interface-bounded parameter
+    alt ty::struct(tcx, ty) {
+      ty::ty_param(n, did) {
+        let bound_n = 0u;
+        for bound in *tcx.ty_param_bounds.get(did.node) {
+            alt bound {
+              ty::bound_iface(t) {
+                let (iid, tps) = alt ty::struct(tcx, t) {
+                    ty::ty_iface(i, tps) { (i, tps) }
+                    _ { cont; }
+                };
+                let ifce_methods = ty::iface_methods(tcx, iid);
+                alt vec::position_pred(*ifce_methods, {|m| m.ident == name}) {
+                  some(pos) {
+                    let m = ifce_methods[pos];
+                    ret some({method_ty: ty::mk_fn(tcx, m.fty),
+                              n_tps: vec::len(*m.tps),
+                              substs: tps,
+                              origin: method_param(iid, pos, n, bound_n)});
+                  }
+                  _ {}
+                }
+                bound_n += 1u;
+              }
+              _ {}
+            }
+        }
+        ret none;
+      }
+      _ {}
+    }
+
+    fn ty_from_did(tcx: ty::ctxt, did: ast::def_id) -> ty::t {
+        if did.crate == ast::local_crate {
+            alt tcx.items.get(did.node) {
+              ast_map::node_method(m) {
+                let mt = ty_of_method(tcx, m_check, m);
+                ty::mk_fn(tcx, mt.fty)
+              }
+            }
+        } else { csearch::get_type(tcx, did).ty }
+    }
+
     let result = none;
     std::list::iter(isc) {|impls|
         if option::is_some(result) { ret; }
         for @{did, methods, _} in *impls {
             alt vec::find(methods, {|m| m.ident == name}) {
               some(m) {
-                let (n_tps, self_ty) = if did.crate == ast::local_crate {
-                    alt fcx.ccx.tcx.items.get(did.node) {
-                      ast_map::node_item(@{node: ast::item_impl(ts, _, st, _),
-                                           _}) {
-                        (vec::len(ts), ast_ty_to_ty_crate(fcx.ccx, st))
-                      }
-                    }
-                } else {
-                    let tpt = csearch::get_type(fcx.ccx.tcx, did);
-                    (vec::len(tpt.bounds), tpt.ty)
-                };
+                let {n_tps, ty: self_ty} = impl_self_ty(tcx, did);
                 let {ids, ty: self_ty} = if n_tps > 0u {
-                    bind_params_in_type(ast_util::dummy_sp(), fcx.ccx.tcx,
-                                        bind next_ty_var_id(fcx), self_ty,
-                                        n_tps)
+                    bind_params(fcx, self_ty, n_tps)
                 } else { {ids: [], ty: self_ty} };
                 alt unify::unify(fcx, ty, self_ty) {
                   ures_ok(_) {
                     if option::is_some(result) {
                         // FIXME[impl] score specificity to resolve ambiguity?
-                        fcx.ccx.tcx.sess.span_err(
+                        tcx.sess.span_err(
                             sp, "multiple applicable methods in scope");
                     } else {
-                        result = some({method: m, ids: ids});
+                        result = some({
+                            method_ty: ty_from_did(tcx, m.did),
+                            n_tps: m.n_tps,
+                            substs: vec::map(ids, {|id| ty::mk_var(tcx, id)}),
+                            origin: method_static(m.did)
+                        });
                     }
                   }
                   _ {}
@@ -1680,7 +1753,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
         let element_ty = demand::simple(fcx, local.span, element_ty,
                                         ty::mk_var(fcx.ccx.tcx, locid));
         let bot = check_decl_local(fcx, local);
-        check_block(fcx, body);
+        check_block_no_value(fcx, body);
         // Unify type of decl with element type of the seq
         demand::simple(fcx, local.span,
                        ty::node_id_to_type(fcx.ccx.tcx, local.node.id),
@@ -1695,22 +1768,27 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
     fn check_then_else(fcx: @fn_ctxt, thn: ast::blk,
                        elsopt: option::t<@ast::expr>, id: ast::node_id,
                        _sp: span) -> bool {
-        let then_bot = check_block(fcx, thn);
-        let els_bot = false;
-        let if_t =
+        let (if_t, if_bot) =
             alt elsopt {
               some(els) {
+                let thn_bot = check_block(fcx, thn);
                 let thn_t = block_ty(fcx.ccx.tcx, thn);
-                els_bot = check_expr_with(fcx, els, thn_t);
-                let elsopt_t = expr_ty(fcx.ccx.tcx, els);
-                if !ty::type_is_bot(fcx.ccx.tcx, elsopt_t) {
-                    elsopt_t
-                } else { thn_t }
+                let els_bot = check_expr_with(fcx, els, thn_t);
+                let els_t = expr_ty(fcx.ccx.tcx, els);
+                let if_t = if !ty::type_is_bot(fcx.ccx.tcx, els_t) {
+                    els_t
+                } else {
+                    thn_t
+                };
+                (if_t, thn_bot & els_bot)
+              }
+              none. {
+                check_block_no_value(fcx, thn);
+                (ty::mk_nil(fcx.ccx.tcx), false)
               }
-              none. { ty::mk_nil(fcx.ccx.tcx) }
             };
         write::ty_only_fixup(fcx, id, if_t);
-        ret then_bot & els_bot;
+        ret if_bot;
     }
 
     // Checks the compatibility
@@ -1932,12 +2010,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
       }
       ast::expr_while(cond, body) {
         bot = check_expr_with(fcx, cond, ty::mk_bool(tcx));
-        check_block(fcx, body);
+        check_block_no_value(fcx, body);
         write::ty_only_fixup(fcx, id, ty::mk_nil(tcx));
       }
       ast::expr_do_while(body, cond) {
         bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)) |
-              check_block(fcx, body);
+              check_block_no_value(fcx, body);
         write::ty_only_fixup(fcx, id, block_ty(tcx, body));
       }
       ast::expr_alt(expr, arms) {
@@ -2153,7 +2231,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
       }
       ast::expr_field(base, field, tys) {
         bot |= check_expr(fcx, base);
-        let expr_t = expr_ty(tcx, base);
+        let expr_t = structurally_resolved_type(fcx, expr.span,
+                                                expr_ty(tcx, base));
         let base_t = do_autoderef(fcx, expr.span, expr_t);
         let handled = false, n_tys = vec::len(tys);
         alt structure_of(fcx, expr.span, base_t) {
@@ -2191,51 +2270,35 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
         if !handled {
             let iscope = fcx.ccx.impl_map.get(expr.id);
             alt lookup_method(fcx, iscope, field, expr_t, expr.span) {
-              some({method, ids}) {
-                let fty = if method.did.crate == ast::local_crate {
-                    alt tcx.items.get(method.did.node) {
-                      ast_map::node_method(m) {
-                        let mt = ty_of_method(tcx, m_check, m);
-                        ty::mk_fn(tcx, mt.fty)
-                      }
-                    }
-                } else { csearch::get_type(tcx, method.did).ty };
-                let tvars = vec::map(ids, {|id| ty::mk_var(tcx, id)});
-                let n_tps = vec::len(ids);
-                if method.n_tps + n_tps  > 0u {
-                    let b = bind_params_in_type(expr.span, tcx,
-                                                bind next_ty_var_id(fcx), fty,
-                                                n_tps + method.n_tps);
-                    let _tvars = vec::map(b.ids, {|id| ty::mk_var(tcx, id)});
-                    let i = 0;
-                    for v in tvars {
-                        demand::simple(fcx, expr.span, v, _tvars[i]);
-                        i += 1;
-                    }
-                    tvars = _tvars;
-                    fty = b.ty;
+              some({method_ty: fty, n_tps: method_n_tps, substs, origin}) {
+                let substs = substs, n_tps = vec::len(substs);
+                if method_n_tps + n_tps > 0u {
                     if n_tys > 0u {
-                        if n_tys != method.n_tps {
+                        if n_tys != method_n_tps {
                             tcx.sess.span_fatal
                                 (expr.span, "incorrect number of type \
                                            parameters given for this method");
 
                         }
-                        let i = 0u;
                         for ty in tys {
-                            let tvar = tvars[i + n_tps];
-                            let t_subst = ast_ty_to_ty_crate(fcx.ccx, ty);
-                            demand::simple(fcx, expr.span, tvar, t_subst);
+                            substs += [ast_ty_to_ty_crate(fcx.ccx, ty)];
+                        }
+                    } else {
+                        let i = 0u;
+                        while i < method_n_tps {
+                            substs += [ty::mk_var(tcx, next_ty_var_id(fcx))];
                             i += 1u;
                         }
                     }
+                    write::ty_fixup(fcx, id, {substs: some(substs), ty: fty});
                 } else if n_tys > 0u {
                     tcx.sess.span_fatal(expr.span,
                                         "this method does not take type \
                                          parameters");
+                } else {
+                    write::ty_only_fixup(fcx, id, fty);
                 }
-                write::ty_fixup(fcx, id, {substs: some(tvars), ty: fty});
-                fcx.ccx.method_map.insert(id, method.did);
+                fcx.ccx.method_map.insert(id, origin);
               }
               none. {
                 let t_err = resolve_type_vars_if_possible(fcx, expr_t);
@@ -2437,6 +2500,16 @@ fn check_stmt(fcx: @fn_ctxt, stmt: @ast::stmt) -> bool {
     ret bot;
 }
 
+fn check_block_no_value(fcx: @fn_ctxt, blk: ast::blk) -> bool {
+    let bot = check_block(fcx, blk);
+    if !bot {
+        let blkty = ty::node_id_to_monotype(fcx.ccx.tcx, blk.node.id);
+        let nilty = ty::mk_nil(fcx.ccx.tcx);
+        demand::simple(fcx, blk.span, nilty, blkty);
+    }
+    ret bot;
+}
+
 fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
     let fcx = alt blk.node.rules {
       ast::unchecked_blk. { @{purity: ast::impure_fn with *fcx0} }
@@ -2655,6 +2728,7 @@ fn check_fn(ccx: @crate_ctxt,
     // If we have an enclosing function scope, our type variables will be
     // resolved when the enclosing scope finishes up.
     if option::is_none(old_fcx) {
+        dict::resolve_in_block(fcx, body);
         writeback::resolve_type_vars_in_block(fcx, body);
     }
 }
@@ -2663,16 +2737,40 @@ fn check_method(ccx: @crate_ctxt, method: @ast::method) {
     check_fn(ccx, ast::proto_bare, method.decl, method.body, method.id, none);
 }
 
+fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method,
+                       impl_tps: uint, if_m: ty::method, substs: [ty::t]) {
+    if impl_m.tps != if_m.tps {
+        tcx.sess.span_err(sp, "method `" + if_m.ident +
+                          "` has an incompatible set of type parameters");
+    } else {
+        let impl_fty = ty::mk_fn(tcx, impl_m.fty);
+        // Add dummy substs for the parameters of the impl method
+        let substs = substs + vec::init_fn({|i|
+            ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0})
+        }, vec::len(*if_m.tps));
+        let if_fty = ty::substitute_type_params(tcx, substs,
+                                                ty::mk_fn(tcx, if_m.fty));
+        alt ty::unify::unify(impl_fty, if_fty, ty::unify::precise, tcx) {
+          ty::unify::ures_err(err) {
+            tcx.sess.span_err(sp, "method `" + if_m.ident +
+                              "` has an incompatible type: " +
+                              ty::type_err_to_str(err));
+          }
+          _ {}
+        }
+    }
+}
+
 fn check_item(ccx: @crate_ctxt, it: @ast::item) {
     alt it.node {
       ast::item_const(_, e) { check_const(ccx, it.span, e, it.id); }
-      ast::item_fn(decl, _, body) {
+      ast::item_fn(decl, tps, body) {
         check_fn(ccx, ast::proto_bare, decl, body, it.id, none);
       }
-      ast::item_res(decl, _, body, dtor_id, _) {
+      ast::item_res(decl, tps, body, dtor_id, _) {
         check_fn(ccx, ast::proto_bare, decl, body, dtor_id, none);
       }
-      ast::item_obj(ob, _, _) {
+      ast::item_obj(ob, tps, _) {
         // We're entering an object, so gather up the info we need.
         ccx.self_infos += [self_obj(ob.fields,
                                     ccx.tcx.tcache.get(local_def(it.id)).ty)];
@@ -2681,7 +2779,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
         // Now remove the info from the stack.
         vec::pop(ccx.self_infos);
       }
-      ast::item_impl(_, ifce, ty, ms) {
+      ast::item_impl(tps, ifce, ty, ms) {
         ccx.self_infos += [self_impl(ast_ty_to_ty(ccx.tcx, m_check, ty))];
         let my_methods = vec::map(ms, {|m|
             check_method(ccx, m);
@@ -2690,16 +2788,14 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
         vec::pop(ccx.self_infos);
         alt ifce {
           some(ty) {
-            alt ty::struct(ccx.tcx, ast_ty_to_ty(ccx.tcx, m_check, ty)) {
+            let iface_ty = ast_ty_to_ty(ccx.tcx, m_check, ty);
+            alt ty::struct(ccx.tcx, iface_ty) {
               ty::ty_iface(did, tys) {
                 for if_m in *ty::iface_methods(ccx.tcx, did) {
                     alt vec::find(my_methods, {|m| if_m.ident == m.ident}) {
                       some(m) {
-                        if !ty::same_method(ccx.tcx, m, if_m) {
-                            ccx.tcx.sess.span_err(
-                                ty.span, "method `" + if_m.ident +
-                                "` has the wrong type");
-                        }
+                        compare_impl_method(ccx.tcx, ty.span, m,
+                                            vec::len(tps), if_m, tys);
                       }
                       none. {
                         ccx.tcx.sess.span_err(ty.span, "missing method `" +
@@ -2707,6 +2803,9 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
                       }
                     }
                 }
+                let tpt = {bounds: ty_param_bounds(ccx.tcx, m_check, tps),
+                           ty: iface_ty};
+                ccx.tcx.tcache.insert(local_def(it.id), tpt);
               }
               _ {
                 ccx.tcx.sess.span_err(ty.span, "can only implement interface \
@@ -2721,6 +2820,29 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
     }
 }
 
+fn check_ty_params(ccx: @crate_ctxt, tps: [ast::ty_param]) {
+    for tp in tps {
+        let i = 0u;
+        for bound in *tp.bounds {
+            alt bound {
+              ast::bound_iface(at) {
+                let tbound = ccx.tcx.ty_param_bounds.get(tp.id)[i];
+                let bound_ty = alt tbound { ty::bound_iface(t) { t } };
+                alt ty::struct(ccx.tcx, bound_ty) {
+                  ty::ty_iface(_, _) {}
+                  _ {
+                    ccx.tcx.sess.span_err(at.span, "type parameter bounds \
+                                                    must be interface types");
+                  }
+                }
+              }
+              _ {}
+            }
+            i += 1u;
+        }
+    }
+}
+
 fn arg_is_argv_ty(tcx: ty::ctxt, a: ty::arg) -> bool {
     alt ty::struct(tcx, a.ty) {
       ty::ty_vec(mt) {
@@ -2769,21 +2891,209 @@ fn check_for_main_fn(tcx: ty::ctxt, crate: @ast::crate) {
     }
 }
 
+mod dict {
+    fn has_iface_bounds(tps: [ty::param_bounds]) -> bool {
+        vec::any(tps, {|bs|
+            vec::any(*bs, {|b|
+                alt b { ty::bound_iface(_) { true } _ { false } }
+            })
+        })
+    }
+
+    fn lookup_dicts(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
+                    bounds: @[ty::param_bounds], tys: [ty::t])
+        -> dict_res {
+        let tcx = fcx.ccx.tcx, result = [], i = 0u;
+        for ty in tys {
+            for bound in *bounds[i] {
+                alt bound {
+                  ty::bound_iface(i_ty) {
+                    let i_ty = ty::substitute_type_params(tcx, tys, i_ty);
+                    result += [lookup_dict(fcx, isc, sp, ty, i_ty)];
+                  }
+                  _ {}
+                }
+            }
+            i += 1u;
+        }
+        @result
+    }
+
+    fn lookup_dict(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
+                   ty: ty::t, iface_ty: ty::t) -> dict_origin {
+        let tcx = fcx.ccx.tcx;
+        let (iface_id, iface_tps) = alt ty::struct(tcx, iface_ty) {
+            ty::ty_iface(did, tps) { (did, tps) }
+            _ { tcx.sess.abort_if_errors(); fail; }
+        };
+        let ty = fixup_ty(fcx, sp, ty);
+        alt ty::struct(tcx, ty) {
+          ty::ty_param(n, did) {
+            let n_bound = 0u;
+            for bound in *tcx.ty_param_bounds.get(did.node) {
+                alt bound {
+                  ty::bound_iface(ity) {
+                    alt ty::struct(tcx, ity) {
+                      ty::ty_iface(idid, _) {
+                        if iface_id == idid { ret dict_param(n, n_bound); }
+                      }
+                    }
+                    n_bound += 1u;
+                  }
+                  _ {}
+                }
+            }
+          }
+          _ {
+            let found = none;
+            std::list::iter(isc) {|impls|
+                if option::is_some(found) { ret; }
+                for im in *impls {
+                    if im.iface_did == some(iface_id) {
+                        let {n_tps, ty: self_ty} = impl_self_ty(tcx, im.did);
+                        let {ids, ty: self_ty} = if n_tps > 0u {
+                            bind_params(fcx, self_ty, n_tps)
+                        } else { {ids: [], ty: self_ty} };
+                        let vars = vec::map(ids, {|id| ty::mk_var(tcx, id)});
+                        let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
+                        // FIXME[impl] don't do this in fcx (or make
+                        // unify transactional by scrubbing bindings on fail)
+                        alt unify::unify(fcx, ty, self_ty) {
+                          ures_ok(_) {
+                            if option::is_some(found) {
+                                tcx.sess.span_err(
+                                    sp, "multiple applicable implementations \
+                                         in scope");
+                            } else {
+                                connect_iface_tps(fcx, sp, vars, iface_tps,
+                                                  im.did);
+                                let params = vec::map(vars, {|t|
+                                    fixup_ty(fcx, sp, t)});
+                                let subres = lookup_dicts(fcx, isc, sp, im_bs,
+                                                          params);
+                                found = some(dict_static(im.did, params,
+                                                         subres));
+                            }
+                          }
+                          _ {}
+                        }
+                    }
+                }
+            }
+            alt found {
+              some(rslt) { ret rslt; }
+              _ {}
+            }
+          }
+        }
+
+        tcx.sess.span_fatal(
+            sp, "failed to find an implementation of interface " +
+            ty_to_str(tcx, iface_ty) + " for " +
+            ty_to_str(tcx, ty));
+    }
+
+    fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
+        let tcx = fcx.ccx.tcx;
+        alt ty::unify::fixup_vars(tcx, some(sp), fcx.var_bindings, ty) {
+          fix_ok(new_type) { new_type }
+          fix_err(vid) {
+            tcx.sess.span_fatal(sp, "could not determine a type for a \
+                                     bounded type parameter");
+          }
+        }
+    }
+
+    fn connect_iface_tps(fcx: @fn_ctxt, sp: span, impl_tys: [ty::t],
+                         iface_tys: [ty::t], impl_did: ast::def_id) {
+        let tcx = fcx.ccx.tcx;
+        // FIXME[impl]
+        assert impl_did.crate == ast::local_crate;
+        let ity = alt tcx.items.get(impl_did.node) {
+          ast_map::node_item(@{node: ast::item_impl(_, some(ity), _, _), _}) {
+              ast_ty_to_ty(tcx, m_check, ity)
+          }
+        };
+        let iface_ty = ty::substitute_type_params(tcx, impl_tys, ity);
+        alt ty::struct(tcx, iface_ty) {
+          ty::ty_iface(_, tps) {
+            vec::iter2(tps, iface_tys,
+                       {|a, b| demand::simple(fcx, sp, a, b);});
+          }
+        }
+    }
+
+    fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
+        let cx = fcx.ccx;
+        alt ex.node {
+          ast::expr_path(_) {
+            let substs = ty::node_id_to_ty_param_substs_opt_and_ty(
+                cx.tcx, ex.id);
+            alt substs.substs {
+              some(ts) {
+                let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
+                let item_ty = ty::lookup_item_type(cx.tcx, did);
+                if has_iface_bounds(*item_ty.bounds) {
+                    let impls = cx.impl_map.get(ex.id);
+                    cx.dict_map.insert(ex.id, lookup_dicts(
+                        fcx, impls, ex.span, item_ty.bounds, ts));
+                }
+              }
+              _ {}
+            }
+          }
+          // Must resolve bounds on methods with bounded params
+          ast::expr_field(_, _, _) {
+            alt cx.method_map.find(ex.id) {
+              some(method_static(did)) {
+                let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
+                if has_iface_bounds(*bounds) {
+                    let ts = ty::node_id_to_type_params(cx.tcx, ex.id);
+                    let iscs = cx.impl_map.get(ex.id);
+                    cx.dict_map.insert(ex.id, lookup_dicts(
+                        fcx, iscs, ex.span, bounds, ts));
+                }
+              }
+              _ {}
+            }
+          }
+          ast::expr_fn(ast::proto_block., _, _, _) {}
+          ast::expr_fn(_, _, _, _) { ret; }
+          _ {}
+        }
+        visit::visit_expr(ex, fcx, v);
+    }
+
+    // Detect points where an interface-bounded type parameter is
+    // instantiated, resolve the impls for the parameters.
+    fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) {
+        visit::visit_block(bl, fcx, visit::mk_vt(@{
+            visit_expr: resolve_expr,
+            visit_item: fn(_i: @ast::item, &&_e: @fn_ctxt,
+                           _v: visit::vt<@fn_ctxt>) {}
+            with *visit::default_visitor()
+        }));
+    }
+}
+
 fn check_crate(tcx: ty::ctxt, impl_map: resolve::impl_map,
-               crate: @ast::crate) -> method_map {
+               crate: @ast::crate) -> (method_map, dict_map) {
     collect::collect_item_types(tcx, crate);
 
     let ccx = @{mutable self_infos: [],
                 impl_map: impl_map,
                 method_map: std::map::new_int_hash(),
+                dict_map: std::map::new_int_hash(),
                 tcx: tcx};
-    let visit =
-        visit::mk_simple_visitor(@{visit_item: bind check_item(ccx, _)
-                                      with *visit::default_simple_visitor()});
+    let visit = visit::mk_simple_visitor(@{
+        visit_item: bind check_item(ccx, _),
+        visit_ty_params: bind check_ty_params(ccx, _)
+        with *visit::default_simple_visitor()
+    });
     visit::visit_crate(*crate, (), visit);
     check_for_main_fn(tcx, crate);
     tcx.sess.abort_if_errors();
-    ccx.method_map
+    (ccx.method_map, ccx.dict_map)
 }
 //
 // Local Variables:
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
index 883d8b6f046..2738f3e4b82 100644
--- a/src/comp/rustc.rc
+++ b/src/comp/rustc.rc
@@ -22,6 +22,7 @@ mod middle {
     mod trans_uniq;
     mod trans_closure;
     mod trans_vec;
+    mod trans_impl;
     mod ty;
     mod ast_map;
     mod resolve;
diff --git a/src/comp/syntax/parse/eval.rs b/src/comp/syntax/parse/eval.rs
index 1b1713aff33..b13f0cff92a 100644
--- a/src/comp/syntax/parse/eval.rs
+++ b/src/comp/syntax/parse/eval.rs
@@ -56,10 +56,10 @@ fn parse_companion_mod(cx: ctx, prefix: str, suffix: option::t<str>)
     -> ([@ast::view_item], [@ast::item], [ast::attribute]) {
 
     fn companion_file(prefix: str, suffix: option::t<str>) -> str {
-        alt suffix {
+        ret alt suffix {
           option::some(s) { fs::connect(prefix, s) }
           option::none. { prefix }
-        } + ".rs"
+        } + ".rs";
     }
 
     fn file_exists(path: str) -> bool {
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index dd649779235..a1e4ddbe502 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -10,7 +10,12 @@ import util::interner;
 import ast::{node_id, spanned};
 import front::attr;
 
-tag restriction { UNRESTRICTED; RESTRICT_NO_CALL_EXPRS; RESTRICT_NO_BAR_OP; }
+tag restriction {
+    UNRESTRICTED;
+    RESTRICT_STMT_EXPR;
+    RESTRICT_NO_CALL_EXPRS;
+    RESTRICT_NO_BAR_OP;
+}
 
 tag file_type { CRATE_FILE; SOURCE_FILE; }
 
@@ -977,28 +982,45 @@ fn parse_syntax_ext_naked(p: parser, lo: uint) -> @ast::expr {
 
 fn parse_dot_or_call_expr(p: parser) -> @ast::expr {
     let b = parse_bottom_expr(p);
-    if expr_has_value(b) { parse_dot_or_call_expr_with(p, b) }
-    else { b }
+    parse_dot_or_call_expr_with(p, b)
+}
+
+fn permits_call(p: parser) -> bool {
+    ret p.get_restriction() != RESTRICT_NO_CALL_EXPRS;
 }
 
 fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr {
     let lo = e.span.lo;
     let hi = e.span.hi;
     let e = e;
-    while true {
+    while !expr_is_complete(p, e) {
         alt p.peek() {
-          token::LPAREN. {
-            if p.get_restriction() == RESTRICT_NO_CALL_EXPRS {
-                ret e;
-            } else {
-                // Call expr.
-                let es = parse_seq(token::LPAREN, token::RPAREN,
-                                   seq_sep(token::COMMA), parse_expr, p);
-                hi = es.span.hi;
-                let nd = ast::expr_call(e, es.node, false);
-                e = mk_expr(p, lo, hi, nd);
+          // expr(...)
+          token::LPAREN. when permits_call(p) {
+            let es = parse_seq(token::LPAREN, token::RPAREN,
+                               seq_sep(token::COMMA), parse_expr, p);
+            hi = es.span.hi;
+            let nd = ast::expr_call(e, es.node, false);
+            e = mk_expr(p, lo, hi, nd);
+          }
+
+          // expr { || ... }
+          token::LBRACE. when is_bar(p.look_ahead(1u)) && permits_call(p) {
+            p.bump();
+            let blk = parse_fn_block_expr(p);
+            alt e.node {
+              ast::expr_call(f, args, false) {
+                e = @{node: ast::expr_call(f, args + [blk], true)
+                      with *e};
+              }
+              _ {
+                e = mk_expr(p, lo, p.get_last_hi_pos(),
+                            ast::expr_call(e, [blk], true));
+              }
             }
           }
+
+          // expr[...]
           token::LBRACKET. {
             p.bump();
             let ix = parse_expr(p);
@@ -1006,6 +1028,8 @@ fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr {
             expect(p, token::RBRACKET);
             e = mk_expr(p, lo, hi, ast::expr_index(e, ix));
           }
+
+          // expr.f
           token::DOT. {
             p.bump();
             alt p.peek() {
@@ -1022,6 +1046,7 @@ fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr {
               t { unexpected(p, t); }
             }
           }
+
           _ { ret e; }
         }
     }
@@ -1126,7 +1151,7 @@ const ternary_prec: int = 0;
 
 fn parse_more_binops(p: parser, lhs: @ast::expr, min_prec: int) ->
    @ast::expr {
-    if !expr_has_value(lhs) { ret lhs; }
+    if expr_is_complete(p, lhs) { ret lhs; }
     let peeked = p.peek();
     if peeked == token::BINOP(token::OR) &&
        p.get_restriction() == RESTRICT_NO_BAR_OP { ret lhs; }
@@ -1207,11 +1232,6 @@ fn parse_if_expr_1(p: parser) ->
         let elexpr = parse_else_expr(p);
         els = some(elexpr);
         hi = elexpr.span.hi;
-    } else if !option::is_none(thn.node.expr) {
-        let sp = option::get(thn.node.expr).span;
-        p.span_fatal(sp, "`if` without `else` can not produce a result");
-        //TODO: If a suggestion mechanism appears, suggest that the
-        //user may have forgotten a ';'
     }
     ret {cond: cond, then: thn, els: els, lo: lo, hi: hi};
 }
@@ -1550,84 +1570,52 @@ fn parse_stmt(p: parser) -> @ast::stmt {
           }
         }
 
-        let maybe_item = parse_item(p, item_attrs);
-
-        // If we have attributes then we should have an item
-        if vec::len(item_attrs) > 0u {
-            alt maybe_item {
-              some(_) {/* fallthrough */ }
-              _ { ret p.fatal("expected item"); }
-            }
-        }
-
-        alt maybe_item {
+        alt parse_item(p, item_attrs) {
           some(i) {
             let hi = i.span.hi;
             let decl = @spanned(lo, hi, ast::decl_item(i));
             ret @spanned(lo, hi, ast::stmt_decl(decl, p.get_id()));
           }
-          none. {
-            // Remainder are line-expr stmts.
-            let e = parse_expr(p);
-            // See if it is a block call
-            if expr_has_value(e) && p.peek() == token::LBRACE &&
-               is_bar(p.look_ahead(1u)) {
-                p.bump();
-                let blk = parse_fn_block_expr(p);
-                alt e.node {
-                  ast::expr_call(f, args, false) {
-                    e = @{node: ast::expr_call(f, args + [blk], true)
-                        with *e};
-                  }
-                  _ {
-                    e = mk_expr(p, lo, p.get_last_hi_pos(),
-                                ast::expr_call(e, [blk], true));
-                  }
-                }
-            }
-            ret @spanned(lo, e.span.hi, ast::stmt_expr(e, p.get_id()));
-          }
-          _ { p.fatal("expected statement"); }
+          none() { /* fallthrough */ }
         }
+
+        // If we have attributes then we should have an item
+        if vec::len(item_attrs) > 0u {
+            ret p.fatal("expected item");
+        }
+
+        // Remainder are line-expr stmts.
+        let e = parse_expr_res(p, RESTRICT_STMT_EXPR);
+        ret @spanned(lo, e.span.hi, ast::stmt_expr(e, p.get_id()));
     }
 }
 
-fn expr_has_value(e: @ast::expr) -> bool {
+fn expr_is_complete(p: parser, e: @ast::expr) -> bool {
+    log(debug, ("expr_is_complete", p.get_restriction(),
+                print::pprust::expr_to_str(e),
+                expr_requires_semi_to_be_stmt(e)));
+    ret p.get_restriction() == RESTRICT_STMT_EXPR &&
+        !expr_requires_semi_to_be_stmt(e);
+}
+
+fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
     alt e.node {
-      ast::expr_if(_, th, els) | ast::expr_if_check(_, th, els) {
-        if option::is_none(els) { false }
-        else { !option::is_none(th.node.expr) ||
-            expr_has_value(option::get(els)) }
+      ast::expr_if(_, _, _) | ast::expr_if_check(_, _, _)
+      | ast::expr_alt(_, _) | ast::expr_block(_)
+      | ast::expr_do_while(_, _) | ast::expr_while(_, _)
+      | ast::expr_for(_, _, _)
+      | ast::expr_call(_, _, true) {
+        false
       }
-      ast::expr_alt(_, arms) {
-        let found_expr = false;
-        for arm in arms {
-            if !option::is_none(arm.body.node.expr) { found_expr = true; }
-        }
-        found_expr
-      }
-      ast::expr_block(blk) | ast::expr_while(_, blk) |
-      ast::expr_for(_, _, blk) | ast::expr_do_while(blk, _) {
-        !option::is_none(blk.node.expr)
-      }
-      ast::expr_call(_, _, true) { false }
       _ { true }
     }
 }
 
-fn stmt_is_expr(stmt: @ast::stmt) -> bool {
-    ret alt stmt.node {
-      ast::stmt_expr(e, _) { expr_has_value(e) }
-      _ { false }
-    };
-}
-
 fn stmt_to_expr(stmt: @ast::stmt) -> option::t<@ast::expr> {
-    ret if stmt_is_expr(stmt) {
-        alt stmt.node {
-          ast::stmt_expr(e, _) { some(e) }
-        }
-    } else { none };
+    alt stmt.node {
+      ast::stmt_expr(e, _) { some(e) }
+      _ { none }
+    }
 }
 
 fn stmt_ends_with_semi(stmt: ast::stmt) -> bool {
@@ -1639,7 +1627,7 @@ fn stmt_ends_with_semi(stmt: ast::stmt) -> bool {
             }
       }
       ast::stmt_expr(e, _) {
-        ret expr_has_value(e);
+        ret expr_requires_semi_to_be_stmt(e);
       }
     }
 }
@@ -1659,14 +1647,10 @@ fn parse_block(p: parser) -> ast::blk {
 }
 
 fn parse_block_no_value(p: parser) -> ast::blk {
-    let blk = parse_block(p);
-    if !option::is_none(blk.node.expr) {
-        let sp = option::get(blk.node.expr).span;
-        p.span_fatal(sp, "this block must not have a result");
-        //TODO: If a suggestion mechanism appears, suggest that the
-        //user may have forgotten a ';'
-    }
-    ret blk;
+    // We parse blocks that cannot have a value the same as any other block;
+    // the type checker will make sure that the tail expression (if any) has
+    // unit type.
+    ret parse_block(p);
 }
 
 // Precondition: already parsed the '{' or '#{'
@@ -1850,34 +1834,29 @@ fn parse_item_iface(p: parser, attrs: [ast::attribute]) -> @ast::item {
                 ast::item_iface(tps, meths), attrs);
 }
 
+// Parses three variants (with the initial params always optional):
+//    impl <T: copy> of to_str for [T] { ... }
+//    impl name<T> of to_str for [T] { ... }
+//    impl name<T> for [T] { ... }
 fn parse_item_impl(p: parser, attrs: [ast::attribute]) -> @ast::item {
-    let lo = p.get_last_lo_pos(), ident, tps, ifce;
+    let lo = p.get_last_lo_pos();
     fn wrap_path(p: parser, pt: @ast::path) -> @ast::ty {
         @{node: ast::ty_path(pt, p.get_id()), span: pt.span}
     }
-    if eat_word(p, "of") {
+    let (ident, tps) = if !is_word(p, "of") {
+        if p.peek() == token::LT { (none, parse_ty_params(p)) }
+        else { (some(parse_ident(p)), parse_ty_params(p)) }
+    } else { (none, []) };
+    let ifce = if eat_word(p, "of") {
         let path = parse_path_and_ty_param_substs(p, false);
-        tps = vec::map(path.node.types, {|tp|
-            alt tp.node {
-              ast::ty_path(pt, _) {
-                if vec::len(pt.node.idents) == 1u &&
-                   vec::len(pt.node.types) == 0u {
-                     ret {ident: pt.node.idents[0], id: p.get_id(),
-                          bounds: @[]};
-                }
-              }
-              _ {}
-            }
-            p.fatal("only single-word, parameter-less types allowed here");
-        });
-        ident = path.node.idents[vec::len(path.node.idents)-1u];
-        ifce = some(wrap_path(p, path));
-    } else {
-        ident = parse_ident(p);
-        tps = parse_ty_params(p);
-        ifce = if eat_word(p, "of") {
-            some(wrap_path(p, parse_path_and_ty_param_substs(p, false)))
-        } else { none };
+        if option::is_none(ident) {
+            ident = some(path.node.idents[vec::len(path.node.idents) - 1u]);
+        }
+        some(wrap_path(p, path))
+    } else { none };
+    let ident = alt ident {
+        some(name) { name }
+        none. { expect_word(p, "of"); fail; }
     };
     expect_word(p, "for");
     let ty = parse_ty(p, false), meths = [];
diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs
index 72e0879e4bf..da592b6d3c1 100644
--- a/src/comp/syntax/print/pprust.rs
+++ b/src/comp/syntax/print/pprust.rs
@@ -558,11 +558,39 @@ fn print_attribute(s: ps, attr: ast::attribute) {
     word(s.s, "]");
 }
 
+// An expression that begins with a dual-form statement/expression like `{
+// ... }-10` would be parsed as `{ ... };-10` unless parentheses are used (ie,
+// `({...}-10)`).  These parentheses are not, however, preserved by the
+// parser. This function specifies whether parentheses must be inserted.
+fn stmt_expr_requires_parens(ex: @ast::expr) -> bool {
+    fn helper(ex: @ast::expr, inner: bool) -> bool {
+        alt ex.node {
+          ast::expr_call(subex, _, _) | ast::expr_binary(_, subex, _) {
+            be helper(subex, true);
+          }
+          _ when !inner { ret false; }
+          _ { ret !parse::parser::expr_requires_semi_to_be_stmt(ex); }
+        }
+    }
+    ret helper(ex, false);
+}
+
 fn print_stmt(s: ps, st: ast::stmt) {
     maybe_print_comment(s, st.span.lo);
     alt st.node {
-      ast::stmt_decl(decl, _) { print_decl(s, decl); }
-      ast::stmt_expr(expr, _) { space_if_not_bol(s); print_expr(s, expr); }
+      ast::stmt_decl(decl, _) {
+        print_decl(s, decl);
+      }
+      ast::stmt_expr(expr, _) {
+        space_if_not_bol(s);
+        if stmt_expr_requires_parens(expr) {
+            popen(s);
+            print_expr(s, expr);
+            pclose(s);
+        } else {
+            print_expr(s, expr);
+        }
+      }
     }
     if parse::parser::stmt_ends_with_semi(st) { word(s.s, ";"); }
     maybe_print_trailing_comment(s, st.span, none::<uint>);
diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs
index e2767160302..1e5b88e1aa6 100644
--- a/src/comp/syntax/visit.rs
+++ b/src/comp/syntax/visit.rs
@@ -52,6 +52,7 @@ type visitor<E> =
       visit_decl: fn@(@decl, E, vt<E>),
       visit_expr: fn@(@expr, E, vt<E>),
       visit_ty: fn@(@ty, E, vt<E>),
+      visit_ty_params: fn@([ty_param], E, vt<E>),
       visit_constr: fn@(@path, span, node_id, E, vt<E>),
       visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id, E, vt<E>)};
 
@@ -68,6 +69,7 @@ fn default_visitor<E>() -> visitor<E> {
           visit_decl: bind visit_decl::<E>(_, _, _),
           visit_expr: bind visit_expr::<E>(_, _, _),
           visit_ty: bind skip_ty::<E>(_, _, _),
+          visit_ty_params: bind visit_ty_params::<E>(_, _, _),
           visit_constr: bind visit_constr::<E>(_, _, _, _, _),
           visit_fn: bind visit_fn::<E>(_, _, _, _, _, _, _)};
 }
@@ -113,19 +115,19 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
         for vi: @view_item in nm.view_items { v.visit_view_item(vi, e, v); }
         for ni: @native_item in nm.items { v.visit_native_item(ni, e, v); }
       }
-      item_ty(t, tps) { v.visit_ty(t, e, v); visit_ty_params(tps, e, v); }
+      item_ty(t, tps) { v.visit_ty(t, e, v); v.visit_ty_params(tps, e, v); }
       item_res(decl, tps, body, dtor_id, _) {
         v.visit_fn(fk_res(i.ident, tps), decl, body, i.span,
                    dtor_id, e, v);
       }
       item_tag(variants, tps) {
-        visit_ty_params(tps, e, v);
+        v.visit_ty_params(tps, e, v);
         for vr: variant in variants {
             for va: variant_arg in vr.node.args { v.visit_ty(va.ty, e, v); }
         }
       }
       item_obj(ob, tps, _) {
-        visit_ty_params(tps, e, v);
+        v.visit_ty_params(tps, e, v);
         for f: obj_field in ob.fields { v.visit_ty(f.ty, e, v); }
         for m: @method in ob.methods {
             v.visit_fn(fk_method(m.ident, m.tps), m.decl, m.body, m.span,
@@ -133,7 +135,7 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
         }
       }
       item_impl(tps, ifce, ty, methods) {
-        visit_ty_params(tps, e, v);
+        v.visit_ty_params(tps, e, v);
         alt ifce { some(ty) { v.visit_ty(ty, e, v); } _ {} }
         v.visit_ty(ty, e, v);
         for m in methods {
@@ -142,7 +144,7 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
         }
       }
       item_iface(tps, methods) {
-        visit_ty_params(tps, e, v);
+        v.visit_ty_params(tps, e, v);
         for m in methods {
             for a in m.decl.inputs { v.visit_ty(a.ty, e, v); }
             v.visit_ty(m.decl.output, e, v);
@@ -217,7 +219,7 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
 fn visit_native_item<E>(ni: @native_item, e: E, v: vt<E>) {
     alt ni.node {
       native_item_fn(fd, tps) {
-        visit_ty_params(tps, e, v);
+        v.visit_ty_params(tps, e, v);
         visit_fn_decl(fd, e, v);
       }
       native_item_ty. { }
@@ -246,7 +248,7 @@ fn visit_fn_decl<E>(fd: fn_decl, e: E, v: vt<E>) {
 fn visit_fn<E>(fk: fn_kind, decl: fn_decl, body: blk, _sp: span,
                _id: node_id, e: E, v: vt<E>) {
     visit_fn_decl(decl, e, v);
-    visit_ty_params(tps_of_fn(fk), e, v);
+    v.visit_ty_params(tps_of_fn(fk), e, v);
     v.visit_block(body, e, v);
 }
 
@@ -414,6 +416,7 @@ type simple_visitor =
       visit_decl: fn@(@decl),
       visit_expr: fn@(@expr),
       visit_ty: fn@(@ty),
+      visit_ty_params: fn@([ty_param]),
       visit_constr: fn@(@path, span, node_id),
       visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id)};
 
@@ -432,6 +435,7 @@ fn default_simple_visitor() -> simple_visitor {
           visit_decl: fn(_d: @decl) { },
           visit_expr: fn(_e: @expr) { },
           visit_ty: simple_ignore_ty,
+          visit_ty_params: fn(_ps: [ty_param]) {},
           visit_constr: fn(_p: @path, _sp: span, _id: node_id) { },
           visit_fn: fn(_fk: fn_kind, _d: fn_decl, _b: blk, _sp: span,
                        _id: node_id) { }
@@ -488,6 +492,10 @@ fn mk_simple_visitor(v: simple_visitor) -> vt<()> {
         f(ty);
         visit_ty(ty, e, v);
     }
+    fn v_ty_params(f: fn@([ty_param]), ps: [ty_param], &&e: (), v: vt<()>) {
+        f(ps);
+        visit_ty_params(ps, e, v);
+    }
     fn v_constr(f: fn@(@path, span, node_id), pt: @path, sp: span,
                 id: node_id, &&e: (), v: vt<()>) {
         f(pt, sp, id);
@@ -517,6 +525,7 @@ fn mk_simple_visitor(v: simple_visitor) -> vt<()> {
                 visit_decl: bind v_decl(v.visit_decl, _, _, _),
                 visit_expr: bind v_expr(v.visit_expr, _, _, _),
                 visit_ty: visit_ty,
+                visit_ty_params: bind v_ty_params(v.visit_ty_params, _, _, _),
                 visit_constr: bind v_constr(v.visit_constr, _, _, _, _, _),
                 visit_fn: bind v_fn(v.visit_fn, _, _, _, _, _, _, _)
                });
diff --git a/src/comp/util/ppaux.rs b/src/comp/util/ppaux.rs
index ec1b90572c0..d4c01edbb15 100644
--- a/src/comp/util/ppaux.rs
+++ b/src/comp/util/ppaux.rs
@@ -19,10 +19,6 @@ fn mode_str(m: ty::mode) -> str {
     }
 }
 
-fn mode_str_1(m: ty::mode) -> str {
-    alt m { ast::by_ref. { "ref" } _ { mode_str(m) } }
-}
-
 fn ty_to_str(cx: ctxt, typ: t) -> str {
     fn fn_input_to_str(cx: ctxt, input: {mode: middle::ty::mode, ty: t}) ->
        str {
@@ -95,6 +91,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
       ty_str. { "str" }
       ty_box(tm) { "@" + mt_to_str(cx, tm) }
       ty_uniq(tm) { "~" + mt_to_str(cx, tm) }
+      ty_ptr(tm) { "*" + mt_to_str(cx, tm) }
       ty_vec(tm) { "[" + mt_to_str(cx, tm) + "]" }
       ty_type. { "type" }
       ty_rec(elems) {
diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs
index 17f2c739062..39ecb909a85 100644
--- a/src/compiletest/procsrv.rs
+++ b/src/compiletest/procsrv.rs
@@ -163,6 +163,7 @@ fn maybe_with_lib_path<T>(path: str, f: fn@() -> T) -> T {
 
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
 fn maybe_with_lib_path<T>(_path: str, f: fn@() -> T) -> T {
     f()
 }
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index a97253119fb..fefa9d7e617 100644
--- a/src/compiletest/runtest.rs
+++ b/src/compiletest/runtest.rs
@@ -309,6 +309,7 @@ fn program_output(cx: cx, testfile: str, lib_path: str, prog: str,
 // Linux and mac don't require adjusting the library search path
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
 fn make_cmdline(_libpath: str, prog: str, args: [str]) -> str {
     #fmt["%s %s", prog, str::connect(args, " ")]
 }
diff --git a/src/compiletest/util.rs b/src/compiletest/util.rs
index 6ffe1bda6b8..eb846037482 100644
--- a/src/compiletest/util.rs
+++ b/src/compiletest/util.rs
@@ -17,6 +17,7 @@ fn make_new_path(path: str) -> str {
 }
 
 #[cfg(target_os = "linux")]
+#[cfg(target_os = "freebsd")]
 fn lib_path_env_var() -> str { "LD_LIBRARY_PATH" }
 
 #[cfg(target_os = "macos")]
@@ -27,6 +28,7 @@ fn lib_path_env_var() -> str { "PATH" }
 
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
 fn path_div() -> str { ":" }
 
 #[cfg(target_os = "win32")]
diff --git a/src/etc/snapshot.py b/src/etc/snapshot.py
index 130304fdab8..673facfe70a 100644
--- a/src/etc/snapshot.py
+++ b/src/etc/snapshot.py
@@ -33,7 +33,13 @@ snapshot_files = {
               "lib/std-*.dll",
               "lib/rustc-*.dll",
               "lib/rustrt.dll",
-              "lib/rustllvm.dll"]
+              "lib/rustllvm.dll"],
+    "freebsd": ["bin/rustc",
+                "lib/libcore-*.so",
+                "lib/libstd-*.so",
+                "lib/librustc-*.so",
+                "lib/librustrt.so",
+                "lib/librustllvm.so"]
     }
 
 def parse_line(n, line):
@@ -73,6 +79,8 @@ def get_kernel(triple):
         return "winnt"
     if os_name == "darwin":
         return "macos"
+    if os_name == "freebsd":
+        return "freebsd"
     return "linux"
 
 def get_cpu(triple):
diff --git a/src/libcore/char.rs b/src/libcore/char.rs
index 0bdda6f0afb..5475e695a79 100644
--- a/src/libcore/char.rs
+++ b/src/libcore/char.rs
@@ -41,7 +41,7 @@ export is_alphabetic,
        is_XID_start, is_XID_continue,
        is_lowercase, is_uppercase,
        is_whitespace, is_alphanumeric,
-       to_digit, maybe_digit, cmp;
+       to_digit, to_lower, to_upper, maybe_digit, cmp;
 
 import is_alphabetic = unicode::derived_property::Alphabetic;
 import is_XID_start = unicode::derived_property::XID_Start;
@@ -122,7 +122,7 @@ pure fn to_digit(c: char) -> u8 unsafe {
 }
 
 /*
- Function: to_digit
+ Function: maybe_digit
 
  Convert a char to the corresponding digit. Returns none when the
  character is not a valid hexadecimal digit.
@@ -137,6 +137,34 @@ pure fn maybe_digit(c: char) -> option::t<u8> {
 }
 
 /*
+ Function: to_lower
+
+ Convert a char to the corresponding lower case.
+
+ FIXME: works only on ASCII
+*/
+pure fn to_lower(c: char) -> char {
+    alt c {
+      'A' to 'Z' { ((c as u8) + 32u8) as char }
+      _ { c }
+    }
+}
+
+/*
+ Function: to_upper
+
+ Convert a char to the corresponding upper case.
+
+ FIXME: works only on ASCII
+*/
+pure fn to_upper(c: char) -> char {
+    alt c {
+      'a' to 'z' { ((c as u8) - 32u8) as char }
+      _ { c }
+    }
+}
+
+/*
  Function: cmp
 
  Compare two chars.
diff --git a/src/libcore/cmath.rs b/src/libcore/cmath.rs
index dd922ddf1bc..4f3c5a8d68b 100644
--- a/src/libcore/cmath.rs
+++ b/src/libcore/cmath.rs
@@ -5,6 +5,9 @@ import ctypes::c_int;
 import ctypes::c_float;
 import ctypes::c_double;
 
+// function names are almost identical to C's libmath, a few have been
+// renamed, grep for "rename:"
+
 #[link_name = "m"]
 #[abi = "cdecl"]
 native mod c_double {
@@ -26,8 +29,10 @@ native mod c_double {
     pure fn expm1(n: c_double) -> c_double;
     pure fn exp2(n: c_double) -> c_double;
     #[link_name="fabs"] pure fn abs(n: c_double) -> c_double;
-    #[link_name="fdim"] pure fn sub_pos(a: c_double, b: c_double) -> c_double;
+    // rename: for clarity and consistency with add/sub/mul/div
+    #[link_name="fdim"] pure fn abs_sub(a: c_double, b: c_double) -> c_double;
     pure fn floor(n: c_double) -> c_double;
+    // rename: for clarity and consistency with add/sub/mul/div
     #[link_name="fma"] pure fn mul_add(a: c_double, b: c_double,
                                        c: c_double) -> c_double;
     #[link_name="fmax"] pure fn fmax(a: c_double, b: c_double) -> c_double;
@@ -38,17 +43,26 @@ native mod c_double {
     pure fn ldexp(x: c_double, n: c_int) -> c_double;
     #[link_name="lgamma_r"] pure fn lgamma(n: c_double,
                                            &sign: c_int) -> c_double;
+    // renamed: log is a reserved keyword; ln seems more natural, too
     #[link_name="log"] pure fn ln(n: c_double) -> c_double;
-    pure fn logb(n: c_double) -> c_double;
+    // renamed: "logb" /often/ is confused for log2 by beginners
+    #[link_name="logb"] pure fn log_radix(n: c_double) -> c_double;
+    // renamed: to be consitent with log as ln
     #[link_name="log1p"] pure fn ln1p(n: c_double) -> c_double;
     pure fn log10(n: c_double) -> c_double;
+    #[cfg(target_os="linux")]
+    #[cfg(target_os="macos")]
+    #[cfg(target_os="win32")]
     pure fn log2(n: c_double) -> c_double;
-    pure fn ilogb(n: c_double) -> c_int;
+    #[link_name="ilogb"] pure fn ilogradix(n: c_double) -> c_int;
     pure fn modf(n: c_double, &iptr: c_double) -> c_double;
     pure fn pow(n: c_double, e: c_double) -> c_double;
-    pure fn rint(n: c_double) -> c_double;
+// FIXME enable when rounding modes become available
+//    pure fn rint(n: c_double) -> c_double;
     pure fn round(n: c_double) -> c_double;
-    pure fn scalbn(n: c_double, i: c_int) -> c_double;
+    // rename: for consistency with logradix
+    #[link_name="scalbn"] pure fn ldexp_radix(n: c_double, i: c_int) ->
+        c_double;
     pure fn sin(n: c_double) -> c_double;
     pure fn sinh(n: c_double) -> c_double;
     pure fn sqrt(n: c_double) -> c_double;
@@ -90,7 +104,7 @@ native mod c_float {
     #[link_name="expm1f"]pure fn expm1(n: c_float) -> c_float;
     #[link_name="exp2f"] pure fn exp2(n: c_float) -> c_float;
     #[link_name="fabsf"] pure fn abs(n: c_float) -> c_float;
-    #[link_name="fdimf"] pure fn sub_pos(a: c_float, b: c_float) -> c_float;
+    #[link_name="fdimf"] pure fn abs_sub(a: c_float, b: c_float) -> c_float;
     #[link_name="floorf"] pure fn floor(n: c_float) -> c_float;
     #[link_name="frexpf"] pure fn frexp(n: c_float,
                                         &value: c_int) -> c_float;
@@ -105,17 +119,21 @@ native mod c_float {
     #[link_name="lgammaf_r"] pure fn lgamma(n: c_float,
                                             &sign: c_int) -> c_float;
     #[link_name="logf"] pure fn ln(n: c_float) -> c_float;
-    #[link_name="logbf"] pure fn logb(n: c_float) -> c_float;
+    #[link_name="logbf"] pure fn log_radix(n: c_float) -> c_float;
     #[link_name="log1pf"] pure fn ln1p(n: c_float) -> c_float;
+    #[cfg(target_os="linux")]
+    #[cfg(target_os="macos")]
+    #[cfg(target_os="win32")]
     #[link_name="log2f"] pure fn log2(n: c_float) -> c_float;
     #[link_name="log10f"] pure fn log10(n: c_float) -> c_float;
-    #[link_name="ilogbf"] pure fn ilogb(n: c_float) -> c_int;
+    #[link_name="ilogbf"] pure fn ilog_radix(n: c_float) -> c_int;
     #[link_name="modff"] pure fn modf(n: c_float,
                                       &iptr: c_float) -> c_float;
     #[link_name="powf"] pure fn pow(n: c_float, e: c_float) -> c_float;
-    #[link_name="rintf"] pure fn rint(n: c_float) -> c_float;
+// FIXME enable when rounding modes become available
+//    #[link_name="rintf"] pure fn rint(n: c_float) -> c_float;
     #[link_name="roundf"] pure fn round(n: c_float) -> c_float;
-    #[link_name="scalbnf"] pure fn scalbn(n: c_float, i: c_int) -> c_float;
+    #[link_name="scalbnf"] pure fn ldexp_radix(n: c_float, i: c_int) -> c_float;
     #[link_name="sinf"] pure fn sin(n: c_float) -> c_float;
     #[link_name="sinhf"] pure fn sinh(n: c_float) -> c_float;
     #[link_name="sqrtf"] pure fn sqrt(n: c_float) -> c_float;
diff --git a/src/libcore/either.rs b/src/libcore/either.rs
index 60ad12d2e02..f3571ea5bcf 100644
--- a/src/libcore/either.rs
+++ b/src/libcore/either.rs
@@ -103,6 +103,24 @@ pure fn to_result<copy T, copy U>(eith: t<T, U>) -> result::t<U, T> {
     }
 }
 
+/*
+Function: is_left
+
+Checks whether the given value is a left
+*/
+pure fn is_left<T, U>(eith: t<T, U>) -> bool {
+    alt eith { left(_) { true } _ { false } }
+}
+
+/*
+Function: is_left
+
+Checks whether the given value is a right
+*/
+pure fn is_right<T, U>(eith: t<T, U>) -> bool {
+    alt eith { right(_) { true } _ { false } }
+}
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/libcore/f32.rs b/src/libcore/f32.rs
index 26ec20a2ed9..7c7d45694e5 100644
--- a/src/libcore/f32.rs
+++ b/src/libcore/f32.rs
@@ -16,6 +16,8 @@ type t = f32;
 
 // PORT check per architecture
 
+// FIXME obtain these in a different way
+
 const radix: uint = 2u;
 
 const mantissa_digits: uint = 24u;
@@ -42,7 +44,7 @@ const infinity: f32 = 1.0_f32/0.0_f32;
 const neg_infinity: f32 = -1.0_f32/0.0_f32;
 
 /* Predicate: isNaN */
-pure fn isNaN(f: f32) -> bool { f != f }
+pure fn is_NaN(f: f32) -> bool { f != f }
 
 /* Function: add */
 pure fn add(x: f32, y: f32) -> f32 { ret x + y; }
@@ -77,29 +79,32 @@ pure fn ge(x: f32, y: f32) -> bool { ret x >= y; }
 /* Predicate: gt */
 pure fn gt(x: f32, y: f32) -> bool { ret x > y; }
 
+// FIXME replace the predicates below with llvm intrinsics or calls
+// to the libmath macros in the rust runtime for performance
+
 /*
-Predicate: positive
+Predicate: is_positive
 
 Returns true if `x` is a positive number, including +0.0f320 and +Infinity.
  */
-pure fn positive(x: f32) -> bool
+pure fn is_positive(x: f32) -> bool
     { ret x > 0.0f32 || (1.0f32/x) == infinity; }
 
 /*
-Predicate: negative
+Predicate: is_negative
 
 Returns true if `x` is a negative number, including -0.0f320 and -Infinity.
  */
-pure fn negative(x: f32) -> bool
+pure fn is_negative(x: f32) -> bool
     { ret x < 0.0f32 || (1.0f32/x) == neg_infinity; }
 
 /*
-Predicate: nonpositive
+Predicate: is_nonpositive
 
 Returns true if `x` is a negative number, including -0.0f320 and -Infinity.
 (This is the same as `f32::negative`.)
 */
-pure fn nonpositive(x: f32) -> bool {
+pure fn is_nonpositive(x: f32) -> bool {
   ret x < 0.0f32 || (1.0f32/x) == neg_infinity;
 }
 
@@ -109,10 +114,39 @@ Predicate: nonnegative
 Returns true if `x` is a positive number, including +0.0f320 and +Infinity.
 (This is the same as `f32::positive`.)
 */
-pure fn nonnegative(x: f32) -> bool {
+pure fn is_nonnegative(x: f32) -> bool {
   ret x > 0.0f32 || (1.0f32/x) == infinity;
 }
 
+/*
+Predicate: is_zero
+
+Returns true if `x` is a zero number (positive or negative zero)
+*/
+pure fn is_zero(x: f32) -> bool {
+    ret x == 0.0f32 || x == -0.0f32;
+}
+
+/*
+Predicate: is_infinite
+
+Returns true if `x`is an infinite numer
+*/
+pure fn is_infinite(x: f32) -> bool {
+    ret x == infinity || x == neg_infinity;
+}
+
+/*
+Predicate: is_finite
+
+Returns true if `x`is a finite numer
+*/
+pure fn is_finite(x: f32) -> bool {
+    ret !(is_nan(x) || is_infinite(x));
+}
+
+// FIXME add is_normal, is_subnormal, and fpclassify
+
 /* Module: consts */
 mod consts {
 
@@ -208,6 +242,15 @@ mod consts {
     const ln_10: f32 = 2.30258509299404568401799145468436421_f32;
 }
 
+pure fn logarithm(n: f32, b: f32) -> f32 {
+    ret ln(n) / ln(b);
+}
+
+#[cfg(target_os="freebsd")]
+pure fn log2(n: f32) -> f32 {
+    ret ln(n) / consts::ln_2;
+}
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/libcore/f64.rs b/src/libcore/f64.rs
index c7ee7e1a5db..058d6620106 100644
--- a/src/libcore/f64.rs
+++ b/src/libcore/f64.rs
@@ -16,6 +16,8 @@ type t = f64;
 
 // PORT check per architecture
 
+// FIXME obtain these in a different way
+
 const radix: uint = 2u;
 
 const mantissa_digits: uint = 53u;
@@ -42,7 +44,7 @@ const infinity: f64 = 1.0_f64/0.0_f64;
 const neg_infinity: f64 = -1.0_f64/0.0_f64;
 
 /* Predicate: isNaN */
-pure fn isNaN(f: f64) -> bool { f != f }
+pure fn is_NaN(f: f64) -> bool { f != f }
 
 /* Function: add */
 pure fn add(x: f64, y: f64) -> f64 { ret x + y; }
@@ -78,41 +80,70 @@ pure fn ge(x: f64, y: f64) -> bool { ret x >= y; }
 pure fn gt(x: f64, y: f64) -> bool { ret x > y; }
 
 /*
-Predicate: positive
+Predicate: is_positive
 
 Returns true if `x` is a positive number, including +0.0f640 and +Infinity.
  */
-pure fn positive(x: f64) -> bool
+pure fn is_positive(x: f64) -> bool
     { ret x > 0.0f64 || (1.0f64/x) == infinity; }
 
 /*
-Predicate: negative
+Predicate: is_negative
 
 Returns true if `x` is a negative number, including -0.0f640 and -Infinity.
  */
-pure fn negative(x: f64) -> bool
+pure fn is_negative(x: f64) -> bool
     { ret x < 0.0f64 || (1.0f64/x) == neg_infinity; }
 
 /*
-Predicate: nonpositive
+Predicate: is_nonpositive
 
 Returns true if `x` is a negative number, including -0.0f640 and -Infinity.
 (This is the same as `f64::negative`.)
 */
-pure fn nonpositive(x: f64) -> bool {
+pure fn is_nonpositive(x: f64) -> bool {
   ret x < 0.0f64 || (1.0f64/x) == neg_infinity;
 }
 
 /*
-Predicate: nonnegative
+Predicate: is_nonnegative
 
 Returns true if `x` is a positive number, including +0.0f640 and +Infinity.
 (This is the same as `f64::positive`.)
 */
-pure fn nonnegative(x: f64) -> bool {
+pure fn is_nonnegative(x: f64) -> bool {
   ret x > 0.0f64 || (1.0f64/x) == infinity;
 }
 
+/*
+Predicate: is_zero
+
+Returns true if `x` is a zero number (positive or negative zero)
+*/
+pure fn is_zero(x: f64) -> bool {
+    ret x == 0.0f64 || x == -0.0f64;
+}
+
+/*
+Predicate: is_infinite
+
+Returns true if `x`is an infinite numer
+*/
+pure fn is_infinite(x: f64) -> bool {
+    ret x == infinity || x == neg_infinity;
+}
+
+/*
+Predicate: is_finite
+
+Returns true if `x`is a finite numer
+*/
+pure fn is_finite(x: f64) -> bool {
+    ret !(is_nan(x) || is_infinite(x));
+}
+
+// FIXME add is_normal, is_subnormal, and fpclassify
+
 /* Module: consts */
 mod consts {
 
@@ -208,6 +239,15 @@ mod consts {
     const ln_10: f64 = 2.30258509299404568401799145468436421_f64;
 }
 
+pure fn logarithm(n: f64, b: f64) -> f64 {
+    ret ln(n) / ln(b);
+}
+
+#[cfg(target_os="freebsd")]
+pure fn log2(n: f64) -> f64 {
+    ret ln(n) / consts::ln_2;
+}
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/libcore/option.rs b/src/libcore/option.rs
index aaaf82eb7cd..e0a137ca9e8 100644
--- a/src/libcore/option.rs
+++ b/src/libcore/option.rs
@@ -36,7 +36,7 @@ pure fn get<copy T>(opt: t<T>) -> T {
 
 /*
 */
-fn map<T, U>(opt: t<T>, f: block(T) -> U) -> t<U> {
+fn map<T, copy U>(opt: t<T>, f: block(T) -> U) -> t<U> {
     alt opt { some(x) { some(f(x)) } none. { none } }
 }
 
@@ -61,7 +61,7 @@ Function: from_maybe
 
 Returns the contained value or a default
 */
-pure fn from_maybe<T>(def: T, opt: t<T>) -> T {
+pure fn from_maybe<copy T>(def: T, opt: t<T>) -> T {
     alt opt { some(x) { x } none. { def } }
 }
 
@@ -70,7 +70,7 @@ Function: maybe
 
 Applies a function to the contained value or returns a default
 */
-fn maybe<T, U>(def: U, opt: t<T>, f: block(T) -> U) -> U {
+fn maybe<T, copy U>(def: U, opt: t<T>, f: block(T) -> U) -> U {
     alt opt { none. { def } some(t) { f(t) } }
 }
 
diff --git a/src/libcore/result.rs b/src/libcore/result.rs
index 84fbc3b7f90..91038474ead 100644
--- a/src/libcore/result.rs
+++ b/src/libcore/result.rs
@@ -37,7 +37,7 @@ Failure:
 
 If the result is an error
 */
-fn get<T, U>(res: t<T, U>) -> T {
+fn get<copy T, U>(res: t<T, U>) -> T {
     alt res {
       ok(t) { t }
       err(_) {
@@ -57,7 +57,7 @@ Failure:
 
 If the result is not an error
 */
-fn get_err<T, U>(res: t<T, U>) -> U {
+fn get_err<T, copy U>(res: t<T, U>) -> U {
     alt res {
       err(u) { u }
       ok(_) {
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 6cb3ada6a88..cba9b11cb3d 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -7,12 +7,12 @@ String manipulation.
 export eq, lteq, hash, is_empty, is_not_empty, is_whitespace, byte_len,
        byte_len_range, index,
        rindex, find, starts_with, ends_with, substr, slice, split, splitn,
-       split_str, concat, connect, to_upper, replace, char_slice, trim_left,
-       trim_right, trim, unshift_char, shift_char, pop_char, push_char,
-       is_utf8, from_chars, to_chars, char_len, char_len_range, char_at,
-       bytes, is_ascii, shift_byte, pop_byte,
+       split_str, concat, connect, to_lower, to_upper, replace, char_slice,
+       trim_left, trim_right, trim, unshift_char, shift_char, pop_char,
+       push_char, is_utf8, from_chars, to_chars, char_len, char_len_range,
+       char_at, bytes, is_ascii, shift_byte, pop_byte,
        unsafe_from_byte, unsafe_from_bytes, from_char, char_range_at,
-       str_from_cstr, sbuf, as_buf, push_byte, utf8_char_width, safe_slice,
+       from_cstr, sbuf, as_buf, push_byte, utf8_char_width, safe_slice,
        contains, iter_chars, loop_chars, loop_chars_sub,
        escape;
 
@@ -116,14 +116,7 @@ Function: is_whitespace
 Returns true if the string contains only whitespace
 */
 fn is_whitespace(s: str) -> bool {
-    let i = 0u;
-    let len = char_len(s);
-    while i < len {
-        // FIXME: This is not how char_at works
-        if !char::is_whitespace(char_at(s, i)) { ret false; }
-        i += 1u;
-    }
-    ret true;
+    ret loop_chars(s, char::is_whitespace);
 }
 
 /*
@@ -832,7 +825,18 @@ fn connect(v: [str], sep: str) -> str {
     ret s;
 }
 
-// FIXME: This only handles ASCII
+/*
+Function: to_lower
+
+Convert a string to lowercase
+*/
+fn to_lower(s: str) -> str {
+    let outstr = "";
+    iter_chars(s) { |c|
+        push_char(outstr, char::to_lower(c));
+    }
+    ret outstr;
+}
 /*
 Function: to_upper
 
@@ -840,15 +844,8 @@ Convert a string to uppercase
 */
 fn to_upper(s: str) -> str {
     let outstr = "";
-    let ascii_a = 'a' as u8;
-    let ascii_z = 'z' as u8;
-    let diff = 32u8;
-    for byte: u8 in s {
-        let next;
-        if ascii_a <= byte && byte <= ascii_z {
-            next = byte - diff;
-        } else { next = byte; }
-        push_byte(outstr, next);
+    iter_chars(s) { |c|
+        push_char(outstr, char::to_upper(c));
     }
     ret outstr;
 }
@@ -976,11 +973,11 @@ fn as_buf<T>(s: str, f: block(sbuf) -> T) -> T unsafe {
 }
 
 /*
-Function: str_from_cstr
+Function: from_cstr
 
 Create a Rust string from a null-terminated C string
 */
-unsafe fn str_from_cstr(cstr: sbuf) -> str {
+unsafe fn from_cstr(cstr: sbuf) -> str {
     let res = "";
     let start = cstr;
     let curr = start;
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 90e1ff37864..0c1db7119af 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -743,6 +743,17 @@ fn iter<T>(v: [const T], f: block(T)) {
 }
 
 /*
+Function: iter2
+
+Iterates over two vectors in parallel
+
+*/
+fn iter2<U, T>(v: [U], v2: [T], f: block(U, T)) {
+    let i = 0;
+    for elt in v { f(elt, v2[i]); i += 1; }
+}
+
+/*
 Function: iteri
 
 Iterates over a vector's elements and indexes
diff --git a/src/libstd/deque.rs b/src/libstd/deque.rs
index 7c0a13bb5f8..8da26c55b10 100644
--- a/src/libstd/deque.rs
+++ b/src/libstd/deque.rs
@@ -57,7 +57,7 @@ fn create<copy T>() -> t<T> {
 
         ret rv;
     }
-    fn get<T>(elts: [mutable cell<T>], i: uint) -> T {
+    fn get<copy T>(elts: [mutable cell<T>], i: uint) -> T {
         ret alt elts[i] { option::some(t) { t } _ { fail } };
     }
     obj deque<copy T>(mutable nelts: uint,
diff --git a/src/libstd/freebsd_os.rs b/src/libstd/freebsd_os.rs
new file mode 100644
index 00000000000..4fecf418213
--- /dev/null
+++ b/src/libstd/freebsd_os.rs
@@ -0,0 +1,155 @@
+/*
+Module: os
+
+TODO: Restructure and document
+*/
+
+import core::option;
+import core::ctypes::*;
+
+export libc;
+export libc_constants;
+export pipe;
+export fd_FILE;
+export close;
+export fclose;
+export waitpid;
+export getcwd;
+export exec_suffix;
+export target_os;
+export dylib_filename;
+export get_exe_path;
+export fsync_fd;
+
+// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult
+// by https://github.com/graydon/rust/issues#issue/268
+
+#[link_name = ""]               // FIXME remove after #[nolink] is snapshotted
+#[nolink]
+#[abi = "cdecl"]
+native mod libc {
+    fn read(fd: fd_t, buf: *u8, count: size_t) -> ssize_t;
+    fn write(fd: fd_t, buf: *u8, count: size_t) -> ssize_t;
+    fn fread(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t;
+    fn fwrite(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t;
+    fn open(s: str::sbuf, flags: c_int, mode: unsigned) -> fd_t;
+    fn close(fd: fd_t) -> c_int;
+    type FILE;
+    fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE;
+    fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE;
+    fn fclose(f: FILE);
+    fn fflush(f: FILE) -> c_int;
+    fn fsync(fd: fd_t) -> c_int;
+    fn fileno(f: FILE) -> fd_t;
+    fn fgetc(f: FILE) -> c_int;
+    fn ungetc(c: c_int, f: FILE);
+    fn feof(f: FILE) -> c_int;
+    fn fseek(f: FILE, offset: long, whence: c_int) -> c_int;
+    fn ftell(f: FILE) -> long;
+    type dir;
+    fn opendir(d: str::sbuf) -> dir;
+    fn closedir(d: dir) -> c_int;
+    type dirent;
+    fn readdir(d: dir) -> dirent;
+    fn getenv(n: str::sbuf) -> str::sbuf;
+    fn setenv(n: str::sbuf, v: str::sbuf, overwrite: c_int) -> c_int;
+    fn unsetenv(n: str::sbuf) -> c_int;
+    fn pipe(buf: *mutable fd_t) -> c_int;
+    fn waitpid(pid: pid_t, &status: c_int, options: c_int) -> pid_t;
+    fn readlink(path: str::sbuf, buf: str::sbuf, bufsize: size_t) -> ssize_t;
+    fn mkdir(path: str::sbuf, mode: c_int) -> c_int;
+    fn rmdir(path: str::sbuf) -> c_int;
+    fn chdir(path: str::sbuf) -> c_int;
+
+    fn sysctl(name: *c_int, namelen: c_uint,
+              oldp: *u8, &oldlenp: size_t,
+              newp: *u8, newlen: size_t) -> c_int;
+}
+
+mod libc_constants {
+    const O_RDONLY: c_int = 0i32;
+    const O_WRONLY: c_int = 1i32;
+    const O_RDWR: c_int   = 2i32;
+    const O_APPEND: c_int = 8i32;
+    const O_CREAT: c_int  = 512i32;
+    const O_EXCL: c_int   = 2048i32;
+    const O_TRUNC: c_int  = 1024i32;
+    const O_TEXT: c_int   = 0i32;     // nonexistent in FreeBSD libc
+    const O_BINARY: c_int = 0i32;     // nonexistent in FreeBSD libc
+
+    const S_IRUSR: unsigned = 256u32;
+    const S_IWUSR: unsigned = 128u32;
+
+    const CTL_KERN: c_int = 1i32;
+    const KERN_PROC: c_int = 14i32;
+    const KERN_PROC_PATHNAME: c_int = 12i32;
+}
+
+fn pipe() -> {in: fd_t, out: fd_t} {
+    let fds = {mutable in: 0i32, mutable out: 0i32};
+    assert (os::libc::pipe(ptr::mut_addr_of(fds.in)) == 0i32);
+    ret {in: fds.in, out: fds.out};
+}
+
+fn fd_FILE(fd: fd_t) -> libc::FILE {
+    ret str::as_buf("r", {|modebuf| libc::fdopen(fd, modebuf) });
+}
+
+fn close(fd: fd_t) -> c_int {
+    libc::close(fd)
+}
+
+fn fclose(file: libc::FILE) {
+    libc::fclose(file)
+}
+
+fn fsync_fd(fd: fd_t, _l: io::fsync::level) -> c_int {
+    ret libc::fsync(fd);
+}
+
+fn waitpid(pid: pid_t) -> i32 {
+    let status = 0i32;
+    assert (os::libc::waitpid(pid, status, 0i32) != -1i32);
+    ret status;
+}
+
+#[abi = "cdecl"]
+native mod rustrt {
+    fn rust_getcwd() -> str;
+}
+
+fn getcwd() -> str { ret rustrt::rust_getcwd(); }
+
+fn exec_suffix() -> str { ret ""; }
+
+fn target_os() -> str { ret "freebsd"; }
+
+fn dylib_filename(base: str) -> str { ret "lib" + base + ".so"; }
+
+/// Returns the directory containing the running program
+/// followed by a path separator
+fn get_exe_path() -> option::t<fs::path> unsafe {
+    let bufsize = 1023u;
+    let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize));
+    let mib = [libc_constants::CTL_KERN,
+               libc_constants::KERN_PROC,
+               libc_constants::KERN_PROC_PATHNAME, -1i32];
+    ret str::as_buf(path, { |path_buf|
+        if libc::sysctl(vec::unsafe::to_ptr(mib),
+                        vec::len(mib) as c_uint,
+                        path_buf, bufsize,
+                        ptr::null(), 0u) == 0i32 {
+            option::some(fs::dirname(path) + fs::path_sep())
+        } else {
+            option::none
+        }
+    });
+}
+
+// Local Variables:
+// mode: rust;
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 6c8c614ab55..33030565143 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -148,6 +148,7 @@ fn make_dir(p: path, mode: ctypes::c_int) -> bool {
 
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
     fn mkdir(_p: path, _mode: ctypes::c_int) -> bool {
         ret str::as_buf(_p, {|buf| os::libc::mkdir(buf, _mode) == 0i32 });
     }
@@ -186,6 +187,7 @@ fn remove_dir(p: path) -> bool {
 
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
     fn rmdir(_p: path) -> bool {
         ret str::as_buf(_p, {|buf| os::libc::rmdir(buf) == 0i32 });
     }
@@ -201,6 +203,7 @@ fn change_dir(p: path) -> bool {
 
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
     fn chdir(_p: path) -> bool {
         ret str::as_buf(_p, {|buf| os::libc::chdir(buf) == 0i32 });
     }
@@ -367,6 +370,7 @@ fn normalize(p: path) -> path {
 
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
     fn reabsolute(orig: path, new: path) -> path {
         if path_is_absolute(orig) {
             path_sep() + new
diff --git a/src/libstd/generic_os.rs b/src/libstd/generic_os.rs
index babfd0281ba..c1312ad4608 100644
--- a/src/libstd/generic_os.rs
+++ b/src/libstd/generic_os.rs
@@ -28,18 +28,20 @@ fn setenv(n: str, v: str) { }
 
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
 fn getenv(n: str) -> option::t<str> unsafe {
     let s = str::as_buf(n, {|buf| os::libc::getenv(buf) });
     ret if unsafe::reinterpret_cast(s) == 0 {
             option::none::<str>
         } else {
             let s = unsafe::reinterpret_cast(s);
-            option::some::<str>(str::str_from_cstr(s))
+            option::some::<str>(str::from_cstr(s))
         };
 }
 
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
 fn setenv(n: str, v: str) {
     // FIXME (868)
     str::as_buf(
diff --git a/src/libstd/list.rs b/src/libstd/list.rs
index c6a24d275eb..f6b46ed7d2e 100644
--- a/src/libstd/list.rs
+++ b/src/libstd/list.rs
@@ -98,6 +98,27 @@ fn has<copy T>(ls: list<T>, elt: T) -> bool {
 }
 
 /*
+Function: is_empty
+
+Returns true if the list is empty.
+*/
+pure fn is_empty<copy T>(ls: list<T>) -> bool {
+    alt ls {
+        nil. { true }
+        _ { false }
+    }
+}
+
+/*
+Function: is_not_empty
+
+Returns true if the list is not empty.
+*/
+pure fn is_not_empty<copy T>(ls: list<T>) -> bool {
+    ret !is_empty(ls);
+}
+
+/*
 Function: len
 
 Returns the length of a list
@@ -112,8 +133,11 @@ Function: tail
 
 Returns all but the first element of a list
 */
-pure fn tail<copy T>(ls: list<T>) -> list<T> {
-    alt ls { cons(_, tl) { ret *tl; } nil. { fail "list empty" } }
+pure fn tail<copy T>(ls: list<T>) : is_not_empty(ls) -> list<T> {
+    alt ls {
+        cons(_, tl) { ret *tl; }
+        nil. { fail "list empty" }
+    }
 }
 
 /*
@@ -121,8 +145,11 @@ Function: head
 
 Returns the first element of a list
 */
-pure fn head<copy T>(ls: list<T>) -> T {
-    alt ls { cons(hd, _) { ret hd; } nil. { fail "list empty" } }
+pure fn head<copy T>(ls: list<T>) : is_not_empty(ls) -> T {
+    alt ls {
+        cons(hd, _) { ret hd; }
+        nil. { fail "list empty" }
+    }
 }
 
 /*
diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs
index dea9d187110..6b821dc6223 100644
--- a/src/libstd/rope.rs
+++ b/src/libstd/rope.rs
@@ -443,7 +443,7 @@ fn iter_chars(rope: rope, it: block(char)) {
     loop_chars(rope) {|x|
         it(x);
         ret true
-    }
+    };
 }
 
 /*
diff --git a/src/libstd/run_program.rs b/src/libstd/run_program.rs
index 55163696579..11bc15d578f 100644
--- a/src/libstd/run_program.rs
+++ b/src/libstd/run_program.rs
@@ -266,6 +266,7 @@ fn waitpid(pid: pid_t) -> int {
 
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
     fn waitpid_os(pid: pid_t) -> int {
         #[cfg(target_os = "linux")]
         fn WIFEXITED(status: i32) -> bool {
@@ -273,6 +274,7 @@ fn waitpid(pid: pid_t) -> int {
         }
 
         #[cfg(target_os = "macos")]
+        #[cfg(target_os = "freebsd")]
         fn WIFEXITED(status: i32) -> bool {
             (status & 0x7fi32) == 0i32
         }
@@ -283,6 +285,7 @@ fn waitpid(pid: pid_t) -> int {
         }
 
         #[cfg(target_os = "macos")]
+        #[cfg(target_os = "freebsd")]
         fn WEXITSTATUS(status: i32) -> i32 {
             status >> 8i32
         }
diff --git a/src/libstd/std.rc b/src/libstd/std.rc
index bcd2b82cf1a..8acb1ac9ef9 100644
--- a/src/libstd/std.rc
+++ b/src/libstd/std.rc
@@ -98,6 +98,13 @@ mod os;
 #[path = "posix_fs.rs"]
 mod os_fs;
 
+#[cfg(target_os = "freebsd")]
+#[path = "freebsd_os.rs"]
+mod os;
+#[cfg(target_os = "freebsd")]
+#[path = "posix_fs.rs"]
+mod os_fs;
+
 // Local Variables:
 // mode: rust;
 // fill-column: 78;
diff --git a/src/libstd/util.rs b/src/libstd/util.rs
index a15b5291546..f4d984a8937 100644
--- a/src/libstd/util.rs
+++ b/src/libstd/util.rs
@@ -7,7 +7,7 @@ Function: id
 
 The identity function
 */
-pure fn id<T>(x: T) -> T { x }
+pure fn id<copy T>(x: T) -> T { x }
 
 /*
 Function: unreachable
diff --git a/src/libstd/uv.rs b/src/libstd/uv.rs
index a12fdc8cecc..b24008448bb 100644
--- a/src/libstd/uv.rs
+++ b/src/libstd/uv.rs
@@ -5,6 +5,7 @@ the C libuv API. Does very little right now pending scheduler improvements.
 
 #[cfg(target_os = "linux")];
 #[cfg(target_os = "macos")];
+#[cfg(target_os = "freebsd")];
 
 export sanity_check;
 export loop_t, idle_t;
@@ -39,6 +40,7 @@ type idle_cb = opaque_cb;
 
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
 type handle_private_fields = {
     a00: ctypes::c_int,
     a01: ctypes::c_int,
@@ -121,6 +123,7 @@ fn sanity_check() {
 
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
 fn handle_fields_new() -> handle_fields {
     {
         loop: ptr::null(),
@@ -149,4 +152,4 @@ fn idle_new() -> idle_t {
     {
         fields: handle_fields_new()
     }
-}
\ No newline at end of file
+}
diff --git a/src/llvm b/src/llvm
-Subproject a320b2aa41fbe3f944bad33780626d65d1b11e6
+Subproject 4fb132c803512f06f7cbc38baa6e86280912f80
diff --git a/src/rt/arch/i386/ccall.S b/src/rt/arch/i386/ccall.S
index a8b89dc6b0f..c04c3e01c7e 100644
--- a/src/rt/arch/i386/ccall.S
+++ b/src/rt/arch/i386/ccall.S
@@ -16,20 +16,20 @@ ___morestack:
 __morestack:
 #endif
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_startproc
 #endif
 
 	pushl %ebp
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_def_cfa_offset 8
 	.cfi_offset %ebp, -8
 #endif
 
 	movl %esp,%ebp          // save esp
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_def_cfa_register %ebp
 #endif
 
@@ -42,6 +42,6 @@ __morestack:
 
 	ret
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_endproc
-#endif
\ No newline at end of file
+#endif
diff --git a/src/rt/arch/i386/morestack.S b/src/rt/arch/i386/morestack.S
index 6b2e55022c6..d1433213b2d 100644
--- a/src/rt/arch/i386/morestack.S
+++ b/src/rt/arch/i386/morestack.S
@@ -72,7 +72,7 @@
 #define UPCALL_DEL_STACK        L_upcall_del_stack$stub
 #define MORESTACK               ___morestack
 #else
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
 #define UPCALL_NEW_STACK        upcall_new_stack
 #define UPCALL_DEL_STACK        upcall_del_stack
 #define RUST_GET_TASK           rust_get_task
@@ -93,7 +93,7 @@
 .globl MORESTACK
 
 // FIXME: What about _WIN32?	
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
 	.hidden MORESTACK
 #else
 #if defined(__APPLE__)
@@ -106,7 +106,7 @@
 #endif
 
 MORESTACK:
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_startproc
 #endif
 
@@ -125,7 +125,7 @@ MORESTACK:
 	// __morestack, and an extra return address.
 
 	pushl %ebp
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	// The CFA is 20 bytes above the register that it is
 	// associated with for this frame (which will be %ebp)
 	.cfi_def_cfa_offset 20
@@ -133,7 +133,7 @@ MORESTACK:
 	.cfi_offset %ebp, -20
 #endif
 	movl %esp, %ebp
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	// Calculate the CFA as an offset from %ebp
 	.cfi_def_cfa_register %ebp
 #endif
@@ -216,7 +216,7 @@ MORESTACK:
 	// FIXME: I don't think these rules are necessary
 	// since the unwinder should never encounter an instruction
 	// pointer pointing here.
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	// Restore the rule for how to find %ebp
 	.cfi_restore %ebp
 	// Tell the unwinder how to find the CFA in terms of %esp
@@ -234,7 +234,7 @@ MORESTACK:
 	
 	jmpl *%eax
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_endproc
 #endif
 
diff --git a/src/rt/arch/i386/record_sp.S b/src/rt/arch/i386/record_sp.S
index b9c42a650d8..1b698ed74bf 100644
--- a/src/rt/arch/i386/record_sp.S
+++ b/src/rt/arch/i386/record_sp.S
@@ -14,7 +14,7 @@
 .globl GET_SP
 .globl CHECK_STACK
 
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
 RECORD_SP:
 	movl 4(%esp), %eax
 	movl %eax, %gs:48
diff --git a/src/rt/arch/x86_64/ccall.S b/src/rt/arch/x86_64/ccall.S
index c208c7ae643..42415e84a52 100644
--- a/src/rt/arch/x86_64/ccall.S
+++ b/src/rt/arch/x86_64/ccall.S
@@ -23,19 +23,19 @@ ___morestack:
 __morestack:
 #endif
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_startproc
 #endif
 
 	push %rbp
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_def_cfa_offset 16
 	.cfi_offset %rbp, -16
 #endif
 
 	mov %rsp,%rbp          // save rsp
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_def_cfa_register %rbp
 #endif
 
@@ -46,6 +46,6 @@ __morestack:
 
 	ret
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 	.cfi_endproc
 #endif
diff --git a/src/rt/arch/x86_64/context.h b/src/rt/arch/x86_64/context.h
index 75902fd7954..5784a4b85fd 100644
--- a/src/rt/arch/x86_64/context.h
+++ b/src/rt/arch/x86_64/context.h
@@ -31,7 +31,11 @@ extern "C" void __morestack(void *args, void *fn_ptr, uintptr_t stack_ptr);
 
 class context {
 public:
+#ifdef __FreeBSD__
+    registers_t regs __attribute__((aligned(16)));
+#else
     registers_t regs;
+#endif
     
     context();
     
diff --git a/src/rt/arch/x86_64/morestack.S b/src/rt/arch/x86_64/morestack.S
index 3f25f786b6c..89ca9d21452 100644
--- a/src/rt/arch/x86_64/morestack.S
+++ b/src/rt/arch/x86_64/morestack.S
@@ -20,7 +20,7 @@
 .globl UPCALL_DEL_STACK
 .globl MORESTACK
 
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
 	.hidden MORESTACK
 #else
 #if defined(__APPLE__)
@@ -33,7 +33,7 @@
 #endif
 
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
 MORESTACK:
 	.cfi_startproc
 
@@ -92,6 +92,9 @@ MORESTACK:
 #ifdef __linux__
 	call UPCALL_NEW_STACK@PLT
 #endif
+#ifdef __FreeBSD__
+	call UPCALL_NEW_STACK@PLT
+#endif
 
 	// Pop the saved arguments
 	movdqa    (%rsp), %xmm0
@@ -135,6 +138,9 @@ MORESTACK:
 #ifdef __linux__
 	call UPCALL_DEL_STACK@PLT
 #endif
+#ifdef __FreeBSD__
+	call UPCALL_DEL_STACK@PLT
+#endif
 
 	popq %rax // Restore the return value
 	popq %rbp
diff --git a/src/rt/arch/x86_64/record_sp.S b/src/rt/arch/x86_64/record_sp.S
index af217d0f37f..3b238d33c77 100644
--- a/src/rt/arch/x86_64/record_sp.S
+++ b/src/rt/arch/x86_64/record_sp.S
@@ -18,17 +18,19 @@
 RECORD_SP:
 	movq %rdi, %fs:112
 	ret
-#else
-#if defined(__APPLE__)
+#elif defined(__APPLE__)
 RECORD_SP:
 	movq $0x60+90*8, %rsi
 	movq %rdi, %gs:(%rsi)
 	ret
+#elif defined(__FreeBSD__)
+RECORD_SP:
+	movq %rdi, %fs:24
+	ret
 #else
 RECORD_SP:
 	ret
 #endif
-#endif
 
 GET_SP:
 	movq %rsp, %rax
diff --git a/src/rt/rust_abi.cpp b/src/rt/rust_abi.cpp
index e17abde7bd7..b533ab1f673 100644
--- a/src/rt/rust_abi.cpp
+++ b/src/rt/rust_abi.cpp
@@ -7,7 +7,7 @@
 #include <stdint.h>
 #include "rust_abi.h"
 
-#if defined(__APPLE__) || defined(__linux__)
+#if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__)
 #define HAVE_DLFCN_H
 #include <dlfcn.h>
 #elif defined(_WIN32)
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index 2e6c41a8e79..92f24c8c7fa 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -23,6 +23,8 @@
 #define RZ_MAC_32   (1024*20)
 #define RZ_MAC_64   (1024*20)
 #define RZ_WIN_32   (1024*20)
+#define RZ_BSD_32   (1024*20)
+#define RZ_BSD_64   (1024*20)
 
 #ifdef __linux__
 #ifdef __i386__
@@ -48,6 +50,14 @@
 #define RED_ZONE_SIZE RZ_WIN_64
 #endif
 #endif
+#ifdef __FreeBSD__
+#ifdef __i386__
+#define RED_ZONE_SIZE RZ_BSD_32
+#endif
+#ifdef __x86_64__
+#define RED_ZONE_SIZE RZ_BSD_64
+#endif
+#endif
 
 // A value that goes at the end of the stack and must not be touched
 const uint8_t stack_canary[] = {0xAB, 0xCD, 0xAB, 0xCD,
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 4eafb8fcbba..7d64842994e 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -20,7 +20,7 @@
 // the rust stack and happen frequently enough to catch most stack changes,
 // including at the beginning of all landing pads.
 // FIXME: Enable this for windows
-#if defined __linux__ || defined __APPLE__
+#if defined __linux__ || defined __APPLE__ || defined __FreeBSD__
 extern "C" void
 check_stack_alignment() __attribute__ ((aligned (16)));
 #else
diff --git a/src/test/compile-fail/binop-add-ptr.rs b/src/test/compile-fail/binop-add-ptr.rs
index e21f6a97fc2..83d73da94d6 100644
--- a/src/test/compile-fail/binop-add-ptr.rs
+++ b/src/test/compile-fail/binop-add-ptr.rs
@@ -1,4 +1,4 @@
-// error-pattern:binary operation + cannot be applied to type `*i`
+// error-pattern:binary operation + cannot be applied to type `*int`
 
 fn die() -> *int { (0 as *int) + (0 as *int) }
 fn main() { }
\ No newline at end of file
diff --git a/src/test/compile-fail/block-must-not-have-result-do.rs b/src/test/compile-fail/block-must-not-have-result-do.rs
index 7a1a384e64b..f842b0f02b4 100644
--- a/src/test/compile-fail/block-must-not-have-result-do.rs
+++ b/src/test/compile-fail/block-must-not-have-result-do.rs
@@ -1,4 +1,4 @@
-// error-pattern:this block must not have a result
+// error-pattern:mismatched types: expected `()` but found `bool`
 
 fn main() {
     do {
diff --git a/src/test/compile-fail/block-must-not-have-result-for.rs b/src/test/compile-fail/block-must-not-have-result-for.rs
index 5b5e1fafca2..d2e7edbfc0f 100644
--- a/src/test/compile-fail/block-must-not-have-result-for.rs
+++ b/src/test/compile-fail/block-must-not-have-result-for.rs
@@ -1,4 +1,4 @@
-// error-pattern:this block must not have a result
+// error-pattern:mismatched types: expected `()` but found `bool`
 
 fn main() {
     for i in [0] {
diff --git a/src/test/compile-fail/block-must-not-have-result-res.rs b/src/test/compile-fail/block-must-not-have-result-res.rs
index 4e0a1a54232..d617aba2fde 100644
--- a/src/test/compile-fail/block-must-not-have-result-res.rs
+++ b/src/test/compile-fail/block-must-not-have-result-res.rs
@@ -1,4 +1,4 @@
-// error-pattern:this block must not have a result
+// error-pattern:mismatched types: expected `()` but found `bool`
 
 resource r(i: int) {
     true
diff --git a/src/test/compile-fail/block-must-not-have-result-while.rs b/src/test/compile-fail/block-must-not-have-result-while.rs
index d0417fc27ec..7f172998c28 100644
--- a/src/test/compile-fail/block-must-not-have-result-while.rs
+++ b/src/test/compile-fail/block-must-not-have-result-while.rs
@@ -1,4 +1,4 @@
-// error-pattern:this block must not have a result
+// error-pattern:mismatched types: expected `()` but found `bool`
 
 fn main() {
     while true {
diff --git a/src/test/compile-fail/forgot-ret.rs b/src/test/compile-fail/forgot-ret.rs
index 0f780f1b335..4e422c970c6 100644
--- a/src/test/compile-fail/forgot-ret.rs
+++ b/src/test/compile-fail/forgot-ret.rs
@@ -3,6 +3,6 @@
 
 fn god_exists(a: int) -> bool { be god_exists(a); }
 
-fn f(a: int) -> int { if god_exists(a) { ret 5; } }
+fn f(a: int) -> int { if god_exists(a) { ret 5; }; }
 
 fn main() { f(12); }
diff --git a/src/test/compile-fail/if-without-else-result.rs b/src/test/compile-fail/if-without-else-result.rs
index 0ff469a871c..2454f4f37bf 100644
--- a/src/test/compile-fail/if-without-else-result.rs
+++ b/src/test/compile-fail/if-without-else-result.rs
@@ -1,4 +1,4 @@
-// error-pattern:`if` without `else` can not produce a result
+// error-pattern:mismatched types: expected `()` but found `bool`
 
 fn main() {
     let a = if true { true };
diff --git a/src/test/compile-fail/missing-return2.rs b/src/test/compile-fail/missing-return2.rs
index a73db075196..26a9febd3c7 100644
--- a/src/test/compile-fail/missing-return2.rs
+++ b/src/test/compile-fail/missing-return2.rs
@@ -3,7 +3,7 @@
 fn f() -> int {
     // Make sure typestate doesn't interpret this alt expression
     // as the function result
-    alt true { true { } }
+    alt true { true { } };
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/native-type-mismatch.rs b/src/test/compile-fail/native-type-mismatch.rs
index 381c89c3aa3..d4b8e44cad0 100644
--- a/src/test/compile-fail/native-type-mismatch.rs
+++ b/src/test/compile-fail/native-type-mismatch.rs
@@ -1,4 +1,4 @@
-// error-pattern:expected `*Mb` but found `native`
+// error-pattern:expected `*u8` but found `native`
 use std;
 
 fn main() unsafe {
diff --git a/src/test/pretty/disamb-stmt-expr.rs b/src/test/pretty/disamb-stmt-expr.rs
new file mode 100644
index 00000000000..5f9d57f94a7
--- /dev/null
+++ b/src/test/pretty/disamb-stmt-expr.rs
@@ -0,0 +1,8 @@
+// pp-exact
+
+// Here we check that the parentheses around the body of `wsucc()` are
+// preserved.  They are needed to disambiguate `{ret n+1}; - 0` from
+// `({ret n+1}-0)`.
+
+fn wsucc(n: int) -> int { ({ ret n + 1 } - 0); }
+fn main() { }
diff --git a/src/test/run-pass/block-arg-as-stmt.rs b/src/test/run-pass/block-arg-as-stmt.rs
new file mode 100644
index 00000000000..53187408af1
--- /dev/null
+++ b/src/test/run-pass/block-arg-as-stmt.rs
@@ -0,0 +1,25 @@
+
+fn compute1() -> float {
+    let v = [0f, 1f, 2f, 3f];
+
+    // Here the "-10f" parses as a second
+    // statement in tail position:
+    vec::foldl(0f, v) { |x, y| x + y } - 10f
+}
+
+fn compute2() -> float {
+    let v = [0f, 1f, 2f, 3f];
+
+    // Here the ret makes this explicit:
+    ret vec::foldl(0f, v) { |x, y| x + y } - 10f;
+}
+
+fn main() {
+    let x = compute1();
+    log(debug, x);
+    assert(x == -10f);
+
+    let y = compute2();
+    log(debug, y);
+    assert(y == -4f);
+}
diff --git a/src/test/run-pass/block-arg-can-be-followed-by-binop.rs b/src/test/run-pass/block-arg-can-be-followed-by-binop.rs
new file mode 100644
index 00000000000..a8e763e96a6
--- /dev/null
+++ b/src/test/run-pass/block-arg-can-be-followed-by-binop.rs
@@ -0,0 +1,8 @@
+fn main() {
+    let v = [-1f, 0f, 1f, 2f, 3f];
+
+    // Trailing expressions require parentheses:
+    let y = vec::foldl(0f, v) { |x, y| x + y } + 10f;
+
+    assert y == 15f;
+}
diff --git a/src/test/run-pass/block-arg-can-be-followed-by-block-arg.rs b/src/test/run-pass/block-arg-can-be-followed-by-block-arg.rs
new file mode 100644
index 00000000000..8fd79740512
--- /dev/null
+++ b/src/test/run-pass/block-arg-can-be-followed-by-block-arg.rs
@@ -0,0 +1,6 @@
+fn main() {
+    fn f(i: block() -> uint) -> uint { i() }
+    let v = [-1f, 0f, 1f, 2f, 3f];
+    let z = vec::foldl(f, v) { |x, _y| x } { || 22u };
+    assert z == 22u;
+}
diff --git a/src/test/run-pass/block-arg-can-be-followed-by-call.rs b/src/test/run-pass/block-arg-can-be-followed-by-call.rs
new file mode 100644
index 00000000000..b570a8bdd82
--- /dev/null
+++ b/src/test/run-pass/block-arg-can-be-followed-by-call.rs
@@ -0,0 +1,6 @@
+fn main() {
+    fn f(i: uint) -> uint { i }
+    let v = [-1f, 0f, 1f, 2f, 3f];
+    let z = vec::foldl(f, v) { |x, _y| x } (22u);
+    assert z == 22u;
+}
diff --git a/src/test/run-pass/block-arg-in-parentheses.rs b/src/test/run-pass/block-arg-in-parentheses.rs
new file mode 100644
index 00000000000..205c9144833
--- /dev/null
+++ b/src/test/run-pass/block-arg-in-parentheses.rs
@@ -0,0 +1,23 @@
+// xfail-test
+
+// FIXME: Parser doesn't distinguish expression in parentheses (as in
+// this example) from one that is not!  It is somewhat of a pain to
+// fix this though there are no theoretical difficulties.  We could
+// either add paren to the AST (better for pretty-print, I suppose) or
+// modify the parser to track whether the expression in question is
+// parenthesized.  I did the latter once and it was a bit of pain but
+// not terribly difficult.  We could also the decision as to whether
+// something is an "expression with a value" down into the
+// parse_expr() codepath, where we *know* if there are parentheses or
+// not, but we'd probably have to be a bit more careful then with
+// clearing the top-level restrction flag (which we ought to do
+// anyhow!)
+
+fn main() {
+    let v = [1f, 2f, 3f];
+    let w =
+        if true { (vec::any(v) { |e| float::nonnegative(e) }) }
+        else { false };
+    assert w;
+}
+
diff --git a/src/test/run-pass/block-arg-in-ternary.rs b/src/test/run-pass/block-arg-in-ternary.rs
new file mode 100644
index 00000000000..d48331b3c71
--- /dev/null
+++ b/src/test/run-pass/block-arg-in-ternary.rs
@@ -0,0 +1,6 @@
+// Allow block arguments with ternary... why not, no chance of ambig.
+fn main() {
+    let v = [-1f, 1f];
+    let foo = vec::any(v) { |e| float::negative(e) } ? true : false;
+    assert foo;
+}
diff --git a/src/test/run-pass/block-arg.rs b/src/test/run-pass/block-arg.rs
new file mode 100644
index 00000000000..6fa2ed5b0f3
--- /dev/null
+++ b/src/test/run-pass/block-arg.rs
@@ -0,0 +1,52 @@
+// Check usage and precedence of block arguments in expressions:
+fn main() {
+    let v = [-1f, 0f, 1f, 2f, 3f];
+
+    // Statement form does not require parentheses:
+    vec::iter(v) { |i|
+        log(info, i);
+    }
+
+    // Usable at all:
+    let any_negative = vec::any(v) { |e| float::negative(e) };
+    assert any_negative;
+
+    // Higher precedence than assignments:
+    any_negative = vec::any(v) { |e| float::negative(e) };
+    assert any_negative;
+
+    // Higher precedence than unary operations:
+    let abs_v = vec::map(v) { |e| float::abs(e) };
+    assert vec::all(abs_v) { |e| float::nonnegative(e) };
+    assert !vec::any(abs_v) { |e| float::negative(e) };
+
+    // Usable in funny statement-like forms:
+    if !vec::any(v) { |e| float::positive(e) } {
+        assert false;
+    }
+    alt vec::all(v) { |e| float::negative(e) } {
+        true { fail "incorrect answer."; }
+        false { }
+    }
+    alt 3 {
+      _ when vec::any(v) { |e| float::negative(e) } {
+      }
+      _ {
+        fail "wrong answer.";
+      }
+    }
+
+
+    // Lower precedence than binary operations:
+    let w = vec::foldl(0f, v, { |x, y| x + y }) + 10f;
+    let y = vec::foldl(0f, v) { |x, y| x + y } + 10f;
+    let z = 10f + vec::foldl(0f, v) { |x, y| x + y };
+    assert w == y;
+    assert y == z;
+
+    // They are not allowed as the tail of a block without parentheses:
+    let w =
+      if true { vec::any(abs_v, { |e| float::nonnegative(e) }) }
+      else { false };
+    assert w;
+}
diff --git a/src/test/run-pass/dupe-first-attr.rc b/src/test/run-pass/dupe-first-attr.rc
index 65798b048f9..663bc50fd02 100644
--- a/src/test/run-pass/dupe-first-attr.rc
+++ b/src/test/run-pass/dupe-first-attr.rc
@@ -8,4 +8,7 @@ mod hello;
 mod hello;
 
 #[cfg(target_os = "win32")]
-mod hello;
\ No newline at end of file
+mod hello;
+
+#[cfg(target_os = "freebsd")]
+mod hello;
diff --git a/src/test/run-pass/early-ret-binop-add.rs b/src/test/run-pass/early-ret-binop-add.rs
index f7719afc591..21eca8fefac 100644
--- a/src/test/run-pass/early-ret-binop-add.rs
+++ b/src/test/run-pass/early-ret-binop-add.rs
@@ -1,2 +1,2 @@
-fn wsucc(n: int) -> int { { ret n + 1 } + 0; }
+fn wsucc(n: int) -> int { ({ ret n + 1 } + 0); }
 fn main() { }
diff --git a/src/test/run-pass/early-ret-binop.rs b/src/test/run-pass/early-ret-binop.rs
index ffa6efb4e80..118bec708b8 100644
--- a/src/test/run-pass/early-ret-binop.rs
+++ b/src/test/run-pass/early-ret-binop.rs
@@ -1,2 +1,2 @@
-fn wsucc(n: int) -> int { { ret n + 1 } == 0; }
+fn wsucc(n: int) -> int { ({ ret n + 1 } == 0); }
 fn main() { }
diff --git a/src/test/run-pass/expr-alt-generic-box2.rs b/src/test/run-pass/expr-alt-generic-box2.rs
index 53cab4546e2..bc19200e323 100644
--- a/src/test/run-pass/expr-alt-generic-box2.rs
+++ b/src/test/run-pass/expr-alt-generic-box2.rs
@@ -4,7 +4,7 @@
 // -*- rust -*-
 type compare<T> = fn@(T, T) -> bool;
 
-fn test_generic<T>(expected: T, eq: compare<T>) {
+fn test_generic<copy T>(expected: T, eq: compare<T>) {
     let actual: T = alt true { true { expected } };
     assert (eq(expected, actual));
 }
diff --git a/src/test/run-pass/expr-alt-generic-unique1.rs b/src/test/run-pass/expr-alt-generic-unique1.rs
index e8b949e9410..301d56389b1 100644
--- a/src/test/run-pass/expr-alt-generic-unique1.rs
+++ b/src/test/run-pass/expr-alt-generic-unique1.rs
@@ -3,7 +3,7 @@
 // -*- rust -*-
 type compare<T> = fn@(~T, ~T) -> bool;
 
-fn test_generic<T>(expected: ~T, eq: compare<T>) {
+fn test_generic<copy T>(expected: ~T, eq: compare<T>) {
     let actual: ~T = alt true { true { expected } };
     assert (eq(expected, actual));
 }
diff --git a/src/test/run-pass/expr-alt-generic-unique2.rs b/src/test/run-pass/expr-alt-generic-unique2.rs
index ee0be17ffa2..5640dca0b4e 100644
--- a/src/test/run-pass/expr-alt-generic-unique2.rs
+++ b/src/test/run-pass/expr-alt-generic-unique2.rs
@@ -4,7 +4,7 @@
 // -*- rust -*-
 type compare<T> = fn@(T, T) -> bool;
 
-fn test_generic<T>(expected: T, eq: compare<T>) {
+fn test_generic<copy T>(expected: T, eq: compare<T>) {
     let actual: T = alt true { true { expected } };
     assert (eq(expected, actual));
 }
diff --git a/src/test/run-pass/expr-alt-generic.rs b/src/test/run-pass/expr-alt-generic.rs
index c3df90f7fdc..5a2f7d8b71c 100644
--- a/src/test/run-pass/expr-alt-generic.rs
+++ b/src/test/run-pass/expr-alt-generic.rs
@@ -4,7 +4,7 @@
 // -*- rust -*-
 type compare<T> = fn@(T, T) -> bool;
 
-fn test_generic<T>(expected: T, eq: compare<T>) {
+fn test_generic<copy T>(expected: T, eq: compare<T>) {
     let actual: T = alt true { true { expected } };
     assert (eq(expected, actual));
 }
diff --git a/src/test/run-pass/expr-fn.rs b/src/test/run-pass/expr-fn.rs
index 30ba5fa79e4..5cd3f2a178b 100644
--- a/src/test/run-pass/expr-fn.rs
+++ b/src/test/run-pass/expr-fn.rs
@@ -9,7 +9,7 @@ fn test_vec() {
 }
 
 fn test_generic() {
-    fn f<T>(t: T) -> T { t }
+    fn f<copy T>(t: T) -> T { t }
     assert (f(10) == 10);
 }
 
diff --git a/src/test/run-pass/iface-generic.rs b/src/test/run-pass/iface-generic.rs
new file mode 100644
index 00000000000..07727035a28
--- /dev/null
+++ b/src/test/run-pass/iface-generic.rs
@@ -0,0 +1,35 @@
+// xfail-pretty
+
+iface to_str {
+    fn to_str() -> str;
+}
+impl of to_str for int {
+    fn to_str() -> str { int::str(self) }
+}
+impl of to_str for str {
+    fn to_str() -> str { self }
+}
+
+iface map<T> {
+    fn map<U>(f: block(T) -> U) -> [U];
+}
+impl <T> of map<T> for [T] {
+    fn map<U>(f: block(T) -> U) -> [U] {
+        let r = [];
+        for x in self { r += [f(x)]; }
+        r
+    }
+}
+
+fn foo<U, T: map<U>>(x: T) -> [str] {
+    x.map({|_e| "hi" })
+}
+fn bar<U: to_str, T: map<U>>(x: T) -> [str] {
+    x.map({|_e| _e.to_str() })
+}
+
+fn main() {
+    assert foo([1]) == ["hi"];
+    assert bar::<int, [int]>([4, 5]) == ["4", "5"];
+    assert bar::<str, [str]>(["x", "y"]) == ["x", "y"];
+}
diff --git a/src/test/run-pass/iface-to-str.rs b/src/test/run-pass/iface-to-str.rs
new file mode 100644
index 00000000000..cf30cefbd33
--- /dev/null
+++ b/src/test/run-pass/iface-to-str.rs
@@ -0,0 +1,28 @@
+iface to_str {
+    fn to_str() -> str;
+}
+
+impl of to_str for int {
+    fn to_str() -> str { int::str(self) }
+}
+
+impl <T: to_str> of to_str for [T] {
+    fn to_str() -> str {
+        "[" + str::connect(vec::map(self, {|e| e.to_str()}), ", ") + "]"
+    }
+}
+
+fn main() {
+    assert 1.to_str() == "1";
+    assert [2, 3, 4].to_str() == "[2, 3, 4]";
+
+    fn indirect<T: to_str>(x: T) -> str {
+        x.to_str() + "!"
+    }
+    assert indirect([10, 20]) == "[10, 20]!";
+
+    fn indirect2<T: to_str>(x: T) -> str {
+        indirect(x)
+    }
+    assert indirect2([1]) == "[1]!";
+}
diff --git a/src/test/run-pass/issue-970.rs b/src/test/run-pass/issue-970.rs
new file mode 100644
index 00000000000..a19b6ccbc7b
--- /dev/null
+++ b/src/test/run-pass/issue-970.rs
@@ -0,0 +1,6 @@
+tag maybe_ordered_pair {
+    yes({low: int, high: int} : less_than(*.low, *.high));
+    no;
+}
+pure fn less_than(x: int, y: int) -> bool { ret x < y; }
+fn main() { }
diff --git a/src/test/run-pass/last-use-is-capture.rs b/src/test/run-pass/last-use-is-capture.rs
new file mode 100644
index 00000000000..1399839a1ed
--- /dev/null
+++ b/src/test/run-pass/last-use-is-capture.rs
@@ -0,0 +1,8 @@
+// Make sure #1399 stays fixed
+
+fn main() {
+    fn invoke(f: lambda()) { f(); }
+    let k = ~22;
+    let _u = {a: k};
+    invoke {||log(error, k);}
+}
diff --git a/src/test/run-pass/non-boolean-pure-fns.rs b/src/test/run-pass/non-boolean-pure-fns.rs
index 3a85cba6162..27f3b530154 100644
--- a/src/test/run-pass/non-boolean-pure-fns.rs
+++ b/src/test/run-pass/non-boolean-pure-fns.rs
@@ -14,7 +14,10 @@ pure fn nonempty_list<copy T>(ls: list<T>) -> bool { pure_length(ls) > 0u }
 // knowledge that ls is a cons node. Future work.
 // Also, this is pretty contrived since nonempty_list
 // could be a "tag refinement", if we implement those.
-fn safe_head<copy T>(ls: list<T>) : nonempty_list(ls) -> T { head(ls) }
+fn safe_head<copy T>(ls: list<T>) : nonempty_list(ls) -> T {
+    check is_not_empty(ls);
+    ret head(ls);
+}
 
 fn main() {
     let mylist = cons(@1u, @nil);
diff --git a/src/test/run-pass/unchecked-predicates.rs b/src/test/run-pass/unchecked-predicates.rs
index a4c80b57549..035b18f8051 100644
--- a/src/test/run-pass/unchecked-predicates.rs
+++ b/src/test/run-pass/unchecked-predicates.rs
@@ -1,4 +1,6 @@
 // Uses foldl to exhibit the unchecked block syntax.
+// TODO: since list's head/tail require the predicate "is_not_empty" now and
+// we have unit tests for list, this test might me not necessary anymore?
 use std;
 
 import std::list::*;
@@ -6,7 +8,10 @@ import std::list::*;
 // Can't easily be written as a "pure fn" because there's
 // no syntax for specifying that f is pure.
 fn pure_foldl<copy T, copy U>(ls: list<T>, u: U, f: block(T, U) -> U) -> U {
-    alt ls { nil. { u } cons(hd, tl) { f(hd, pure_foldl(*tl, f(hd, u), f)) } }
+    alt ls {
+        nil. { u }
+        cons(hd, tl) { f(hd, pure_foldl(*tl, f(hd, u), f)) }
+    }
 }
 
 // Shows how to use an "unchecked" block to call a general
@@ -22,7 +27,10 @@ pure fn nonempty_list<copy T>(ls: list<T>) -> bool { pure_length(ls) > 0u }
 // knowledge that ls is a cons node. Future work.
 // Also, this is pretty contrived since nonempty_list
 // could be a "tag refinement", if we implement those.
-fn safe_head<copy T>(ls: list<T>) : nonempty_list(ls) -> T { head(ls) }
+fn safe_head<copy T>(ls: list<T>) : nonempty_list(ls) -> T {
+    check is_not_empty(ls);
+    ret head(ls)
+}
 
 fn main() {
     let mylist = cons(@1u, @nil);
diff --git a/src/test/run-pass/x86stdcall.rs b/src/test/run-pass/x86stdcall.rs
index 765f87de7b2..b9d9b84a526 100644
--- a/src/test/run-pass/x86stdcall.rs
+++ b/src/test/run-pass/x86stdcall.rs
@@ -19,4 +19,5 @@ fn main() {
 
 #[cfg(target_os = "macos")]
 #[cfg(target_os = "linux")]
+#[cfg(target_os = "freebsd")]
 fn main() { }
diff --git a/src/test/run-pass/x86stdcall2.rs b/src/test/run-pass/x86stdcall2.rs
index 6c5093a1329..e58656a73a4 100644
--- a/src/test/run-pass/x86stdcall2.rs
+++ b/src/test/run-pass/x86stdcall2.rs
@@ -24,4 +24,5 @@ fn main() {
 
 #[cfg(target_os = "macos")]
 #[cfg(target_os = "linux")]
+#[cfg(target_os = "freebsd")]
 fn main() { }
diff --git a/src/test/stdtest/char.rs b/src/test/stdtest/char.rs
index 1ed61f5632f..7da2443d6c2 100644
--- a/src/test/stdtest/char.rs
+++ b/src/test/stdtest/char.rs
@@ -60,3 +60,19 @@ fn test_to_digit_fail_1() {
 fn test_to_digit_fail_2() {
     char::to_digit('$');
 }
+
+#[test]
+fn test_to_lower() {
+    assert (char::to_lower('H') == 'h');
+    assert (char::to_lower('e') == 'e');
+    //assert (char::to_lower('Ö') == 'ö');
+    assert (char::to_lower('ß') == 'ß');
+}
+
+#[test]
+fn test_to_upper() {
+    assert (char::to_upper('l') == 'L');
+    assert (char::to_upper('Q') == 'Q');
+    //assert (char::to_upper('ü') == 'Ü');
+    assert (char::to_upper('ß') == 'ß');
+}
diff --git a/src/test/stdtest/list.rs b/src/test/stdtest/list.rs
index 227e8ad299d..b96bc9cd16a 100644
--- a/src/test/stdtest/list.rs
+++ b/src/test/stdtest/list.rs
@@ -2,17 +2,38 @@ import core::*;
 
 use std;
 import std::list;
-import std::list::head;
-import std::list::tail;
-import std::list::from_vec;
+import std::list::{from_vec, head, is_empty, is_not_empty, tail};
 import option;
 
 #[test]
+fn test_is_empty() {
+    let empty : list::list<int> = from_vec([]);
+    let full1 = from_vec([1]);
+    let full2 = from_vec(['r', 'u']);
+
+    assert is_empty(empty);
+    assert !is_empty(full1);
+    assert !is_empty(full2);
+
+    assert !is_not_empty(empty);
+    assert is_not_empty(full1);
+    assert is_not_empty(full2);
+}
+
+#[test]
 fn test_from_vec() {
     let l = from_vec([0, 1, 2]);
+
+    check is_not_empty(l);
     assert (head(l) == 0);
-    assert (head(tail(l)) == 1);
-    assert (head(tail(tail(l))) == 2);
+
+    let tail_l = tail(l);
+    check is_not_empty(tail_l);
+    assert (head(tail_l) == 1);
+
+    let tail_tail_l = tail(tail_l);
+    check is_not_empty(tail_tail_l);
+    assert (head(tail_tail_l) == 2);
 }
 
 #[test]
@@ -24,9 +45,17 @@ fn test_from_vec_empty() {
 #[test]
 fn test_from_vec_mut() {
     let l = from_vec([mutable 0, 1, 2]);
+
+    check is_not_empty(l);
     assert (head(l) == 0);
-    assert (head(tail(l)) == 1);
-    assert (head(tail(tail(l))) == 2);
+
+    let tail_l = tail(l);
+    check is_not_empty(tail_l);
+    assert (head(tail_l) == 1);
+
+    let tail_tail_l = tail(tail_l);
+    check is_not_empty(tail_tail_l);
+    assert (head(tail_tail_l) == 2);
 }
 
 #[test]
diff --git a/src/test/stdtest/os.rs b/src/test/stdtest/os.rs
index 7f7b2c4f3b3..fd773063e8d 100644
--- a/src/test/stdtest/os.rs
+++ b/src/test/stdtest/os.rs
@@ -14,6 +14,7 @@ fn test_setenv() {
 }
 
 #[test]
+#[ignore(reason = "fails periodically on mac")]
 fn test_setenv_overwrite() {
     setenv("NAME2", "1");
     setenv("NAME2", "2");
@@ -23,12 +24,14 @@ fn test_setenv_overwrite() {
 // Windows GetEnvironmentVariable requires some extra work to make sure
 // the buffer the variable is copied into is the right size
 #[test]
+#[ignore(reason = "fails periodically on mac")]
 fn test_getenv_big() {
     let s = "";
     let i = 0;
     while i < 100 { s += "aaaaaaaaaa"; i += 1; }
-    setenv("NAME3", s);
-    assert (getenv("NAME3") == option::some(s));
+    setenv("test_getenv_big", s);
+    log(debug, s);
+    assert (getenv("test_getenv_big") == option::some(s));
 }
 
 #[test]
diff --git a/src/test/stdtest/str.rs b/src/test/stdtest/str.rs
index c3f0e53bac5..39217fb8a8b 100644
--- a/src/test/stdtest/str.rs
+++ b/src/test/stdtest/str.rs
@@ -287,10 +287,10 @@ fn unsafe_from_bytes() {
 }
 
 #[test]
-fn str_from_cstr() unsafe {
+fn from_cstr() unsafe {
     let a = [65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 0u8];
     let b = vec::to_ptr(a);
-    let c = str::str_from_cstr(b);
+    let c = str::from_cstr(b);
     assert (c == "AAAAAAA");
 }
 
@@ -312,7 +312,7 @@ fn as_buf_small() unsafe {
 fn as_buf2() unsafe {
     let s = "hello";
     let sb = str::as_buf(s, {|b| b });
-    let s_cstr = str::str_from_cstr(sb);
+    let s_cstr = str::from_cstr(sb);
     assert (str::eq(s_cstr, s));
 }
 
diff --git a/src/test/stdtest/uv.rs b/src/test/stdtest/uv.rs
index 5529ee7a2bb..a7f6e0a8f15 100644
--- a/src/test/stdtest/uv.rs
+++ b/src/test/stdtest/uv.rs
@@ -1,6 +1,7 @@
 
 #[cfg(target_os = "linux")];
 #[cfg(target_os = "macos")];
+#[cfg(target_os = "freebsd")];
 
 import core::*;
 
@@ -44,4 +45,4 @@ mod test_ref {
         uv::loop_delete(loop);
         */
     }
-}
\ No newline at end of file
+}