about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Holk <eholk@mozilla.com>2011-08-04 16:17:13 -0700
committerEric Holk <eholk@mozilla.com>2011-08-04 16:17:13 -0700
commitbdb84e76c5798731eee3261e2c6169064a022043 (patch)
tree6f19db4fa5bb6b6f813f83d4f09ad61d27558161
parent0e70332b295d72e9b70e5db59382e507a416b8e8 (diff)
parentae3312002a87d673ae97a9e75455060fcd111069 (diff)
downloadrust-bdb84e76c5798731eee3261e2c6169064a022043.tar.gz
rust-bdb84e76c5798731eee3261e2c6169064a022043.zip
Merge branch 'master' of github.com:graydon/rust
-rw-r--r--src/comp/back/abi.rs17
-rw-r--r--src/comp/middle/trans.rs79
-rw-r--r--src/comp/middle/trans_alt.rs2
-rw-r--r--src/comp/middle/trans_common.rs28
-rw-r--r--src/comp/middle/ty.rs76
-rw-r--r--src/comp/middle/typeck.rs20
-rw-r--r--src/test/compile-fail/occurs-check-2.rs5
-rw-r--r--src/test/compile-fail/occurs-check.rs4
-rw-r--r--src/test/run-pass/alt-bot-2.rs3
9 files changed, 163 insertions, 71 deletions
diff --git a/src/comp/back/abi.rs b/src/comp/back/abi.rs
index d65a9049786..6a53767491f 100644
--- a/src/comp/back/abi.rs
+++ b/src/comp/back/abi.rs
@@ -47,30 +47,21 @@ const vec_elt_pad: int = 3;
 const vec_elt_data: int = 4;
 
 const tydesc_field_first_param: int = 0;
-
 const tydesc_field_size: int = 1;
-
 const tydesc_field_align: int = 2;
-
 const tydesc_field_copy_glue: int = 3;
-
 const tydesc_field_drop_glue: int = 4;
-
 const tydesc_field_free_glue: int = 5;
-
 const tydesc_field_sever_glue: int = 6;
-
 const tydesc_field_mark_glue: int = 7;
-
-
 // FIXME no longer used in rustc, drop when rustboot is gone
 const tydesc_field_obj_drop_glue: int = 8;
-
 const tydesc_field_is_stateful: int = 9;
-
 const tydesc_field_cmp_glue: int = 10;
-
-const n_tydesc_fields: int = 11;
+const tydesc_field_shape: int = 11;
+const tydesc_field_shape_tables: int = 12;
+const tydesc_field_n_params: int = 13;
+const n_tydesc_fields: int = 14;
 
 const cmp_glue_op_eq: uint = 0u;
 
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 817e82718ec..0793d390d57 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -895,7 +895,8 @@ fn linearize_ty_params(cx: &@block_ctxt, t: &ty::t) ->
 
 fn trans_stack_local_derived_tydesc(cx: &@block_ctxt, llsz: ValueRef,
                                     llalign: ValueRef, llroottydesc: ValueRef,
-                                    llparamtydescs: ValueRef) -> ValueRef {
+                                    llparamtydescs: ValueRef,
+                                    n_params: uint) -> ValueRef {
     let llmyroottydesc = alloca(cx, bcx_ccx(cx).tydesc_type);
     // By convention, desc 0 is the root descriptor.
 
@@ -904,11 +905,14 @@ fn trans_stack_local_derived_tydesc(cx: &@block_ctxt, llsz: ValueRef,
     // Store a pointer to the rest of the descriptors.
 
     let llfirstparam = cx.build.GEP(llparamtydescs, ~[C_int(0), C_int(0)]);
-    cx.build.Store(llfirstparam,
-                   cx.build.GEP(llmyroottydesc, ~[C_int(0), C_int(0)]));
-    cx.build.Store(llsz, cx.build.GEP(llmyroottydesc, ~[C_int(0), C_int(1)]));
-    cx.build.Store(llalign,
-                   cx.build.GEP(llmyroottydesc, ~[C_int(0), C_int(2)]));
+    store_inbounds(cx, llfirstparam, llmyroottydesc,
+                   ~[C_int(0), C_int(abi::tydesc_field_first_param)]);
+    store_inbounds(cx, C_uint(n_params), llmyroottydesc,
+                   ~[C_int(0), C_int(abi::tydesc_field_n_params)]);
+    store_inbounds(cx, llsz, llmyroottydesc,
+                   ~[C_int(0), C_int(abi::tydesc_field_size)]);
+    store_inbounds(cx, llalign, llmyroottydesc,
+                   ~[C_int(0), C_int(abi::tydesc_field_align)]);
     ret llmyroottydesc;
 }
 
@@ -964,7 +968,8 @@ fn get_derived_tydesc(cx: &@block_ctxt, t: &ty::t, escapes: bool,
         v = td_val;
     } else {
         let llparamtydescs =
-            alloca(bcx, T_array(T_ptr(bcx_ccx(bcx).tydesc_type), n_params));
+            alloca(bcx, T_array(T_ptr(bcx_ccx(bcx).tydesc_type),
+                                n_params + 1u));
         let i = 0;
         for td: ValueRef  in tys.descs {
             let tdp = bcx.build.GEP(llparamtydescs, ~[C_int(0), C_int(i)]);
@@ -973,7 +978,7 @@ fn get_derived_tydesc(cx: &@block_ctxt, t: &ty::t, escapes: bool,
         }
         v =
             trans_stack_local_derived_tydesc(bcx, sz.val, align.val, root,
-                                             llparamtydescs);
+                                             llparamtydescs, n_params);
     }
     bcx.fcx.derived_tydescs.insert(t, {lltydesc: v, escapes: escapes});
     ret rslt(cx, v);
@@ -1191,20 +1196,28 @@ fn emit_tydescs(ccx: &@crate_ctxt) {
               none. { ccx.stats.n_null_glues += 1u; C_null(cmp_fn_ty) }
               some(v) { ccx.stats.n_real_glues += 1u; v }
             };
-        let  // copy_glue
-             // drop_glue
-             // free_glue
-             // sever_glue
-             // mark_glue
-             // obj_drop_glue
-             // is_stateful
-            tydesc =
+
+        let shape = shape::shape_of(ccx, pair.key);
+        let shape_tables =
+            llvm::LLVMConstPointerCast(ccx.shape_cx.llshapetables,
+                                       T_ptr(T_i8()));
+
+        let tydesc =
             C_named_struct(ccx.tydesc_type,
-                           ~[C_null(T_ptr(T_ptr(ccx.tydesc_type))), ti.size,
-                             ti.align, copy_glue, drop_glue, free_glue,
-                             C_null(glue_fn_ty), C_null(glue_fn_ty),
-                             C_null(glue_fn_ty), C_null(glue_fn_ty),
-                             cmp_glue]); // cmp_glue
+                           ~[C_null(T_ptr(T_ptr(ccx.tydesc_type))),
+                             ti.size,               // size
+                             ti.align,              // align
+                             copy_glue,             // copy_glue
+                             drop_glue,             // drop_glue
+                             free_glue,             // free_glue
+                             C_null(glue_fn_ty),    // sever_glue
+                             C_null(glue_fn_ty),    // mark_glue
+                             C_null(glue_fn_ty),    // obj_drop_glue
+                             C_null(glue_fn_ty),    // is_stateful
+                             cmp_glue,              // cmp_glue
+                             C_shape(ccx, shape),   // shape
+                             shape_tables,          // shape_tables
+                             C_int(0)]);            // n_params
 
         let gvar = ti.tydesc;
         llvm::LLVMSetInitializer(gvar, tydesc);
@@ -2288,8 +2301,9 @@ fn call_cmp_glue(cx: &@block_ctxt, lhs: ValueRef, rhs: ValueRef, t: &ty::t,
     let ti = none[@tydesc_info];
     let r = get_tydesc(cx, t, false, ti);
     lazily_emit_tydesc_glue(cx, abi::tydesc_field_cmp_glue, ti);
+    let lltydesc = r.val;
     let lltydescs =
-        r.bcx.build.GEP(r.val,
+        r.bcx.build.GEP(lltydesc,
                         ~[C_int(0), C_int(abi::tydesc_field_first_param)]);
     lltydescs = r.bcx.build.Load(lltydescs);
 
@@ -2297,7 +2311,7 @@ fn call_cmp_glue(cx: &@block_ctxt, lhs: ValueRef, rhs: ValueRef, t: &ty::t,
     alt ti {
       none. {
         let llfnptr =
-            r.bcx.build.GEP(r.val,
+            r.bcx.build.GEP(lltydesc,
                             ~[C_int(0), C_int(abi::tydesc_field_cmp_glue)]);
         llfn = r.bcx.build.Load(llfnptr);
       }
@@ -2306,7 +2320,7 @@ fn call_cmp_glue(cx: &@block_ctxt, lhs: ValueRef, rhs: ValueRef, t: &ty::t,
 
     let llcmpresultptr = alloca(r.bcx, T_i1());
     let llargs: ValueRef[] =
-        ~[llcmpresultptr, r.bcx.fcx.lltaskptr, C_null(T_ptr(T_nil())),
+        ~[llcmpresultptr, r.bcx.fcx.lltaskptr, lltydesc,
           lltydescs, llrawlhsptr, llrawrhsptr, llop];
     r.bcx.build.Call(llfn, llargs);
     ret rslt(r.bcx, r.bcx.build.Load(llcmpresultptr));
@@ -2366,7 +2380,7 @@ fn call_memmove(cx: &@block_ctxt, dst: ValueRef, src: ValueRef,
     let src_ptr = cx.build.PointerCast(src, T_ptr(T_i8()));
     let dst_ptr = cx.build.PointerCast(dst, T_ptr(T_i8()));
     let size = cx.build.IntCast(n_bytes, T_i32());
-    let align = C_int(0);
+    let align = C_int(1);
     let volatile = C_bool(false);
     ret rslt(cx,
              cx.build.Call(memmove,
@@ -2397,7 +2411,12 @@ fn memmove_ty(cx: &@block_ctxt, dst: ValueRef, src: ValueRef, t: &ty::t) ->
     if ty::type_has_dynamic_size(bcx_tcx(cx), t) {
         let llsz = size_of(cx, t);
         ret call_memmove(llsz.bcx, dst, src, llsz.val);
-    } else { ret rslt(cx, cx.build.Store(cx.build.Load(src), dst)); }
+    } else if ty::type_is_structural(bcx_tcx(cx), t) {
+        let llsz = llsize_of(type_of(bcx_ccx(cx), cx.sp, t));
+        ret call_memmove(cx, dst, src, llsz);
+    } else {
+        ret rslt(cx, cx.build.Store(cx.build.Load(src), dst));
+    }
 }
 
 // Duplicates any heap-owned memory owned by a value of the given type.
@@ -4696,7 +4715,7 @@ fn trans_arg_expr(cx: &@block_ctxt, arg: &ty::arg, lldestty0: TypeRef,
 //  - create_llargs_for_fn_args.
 //  - new_fn_ctxt
 //  - trans_args
-fn trans_args(cx: &@block_ctxt, llenv: ValueRef, llobj: &option::t[ValueRef],
+fn trans_args(cx: &@block_ctxt, llenv: ValueRef,
               gen: &option::t[generic_info], lliterbody: &option::t[ValueRef],
               es: &(@ast::expr)[], fn_ty: &ty::t) ->
    {bcx: @block_ctxt, args: ValueRef[], retslot: ValueRef} {
@@ -4815,8 +4834,7 @@ fn trans_call(cx: &@block_ctxt, f: &@ast::expr,
 
     let ret_ty = ty::node_id_to_type(bcx_tcx(cx), id);
     let args_res =
-        trans_args(bcx, llenv, f_res.llobj, f_res.generic, lliterbody, args,
-                   fn_ty);
+        trans_args(bcx, llenv, f_res.generic, lliterbody, args, fn_ty);
     bcx = args_res.bcx;
     let llargs = args_res.args;
     let llretslot = args_res.retslot;
@@ -8000,8 +8018,9 @@ fn trans_crate(sess: &session::session, crate: &@ast::crate, tcx: &ty::ctxt,
     trans_mod(cx, crate.node.module);
     create_crate_map(ccx);
     emit_tydescs(ccx);
-    // Translate the metadata:
+    shape::gen_shape_tables(ccx);
 
+    // Translate the metadata.
     write_metadata(cx.ccx, crate);
     if ccx.sess.get_opts().stats {
         log_err "--- trans stats ---";
diff --git a/src/comp/middle/trans_alt.rs b/src/comp/middle/trans_alt.rs
index 4a1734d3bc1..cdfff0ffe9a 100644
--- a/src/comp/middle/trans_alt.rs
+++ b/src/comp/middle/trans_alt.rs
@@ -433,7 +433,7 @@ fn trans_alt(cx: &@block_ctxt, expr: &@ast::expr, arms: &ast::arm[],
             ret rslt(cx, cx.build.Unreachable());
         }
         else {
-            ret rslt(cx, C_nil());
+            ret er;
         }
     }
 
diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs
index 32b25a8a062..e0b26a97c9f 100644
--- a/src/comp/middle/trans_common.rs
+++ b/src/comp/middle/trans_common.rs
@@ -597,22 +597,13 @@ fn T_tydesc(taskptr_type: TypeRef) -> TypeRef {
         T_ptr(T_fn(~[T_ptr(T_nil()), taskptr_type, T_ptr(T_nil()), tydescpp,
                      pvoid], T_void()));
     let cmp_glue_fn_ty =
-        T_ptr(T_fn(~[T_ptr(T_i1()), taskptr_type, T_ptr(T_nil()), tydescpp,
+        T_ptr(T_fn(~[T_ptr(T_i1()), taskptr_type, T_ptr(tydesc), tydescpp,
                      pvoid, pvoid, T_i8()], T_void()));
 
-    let  // first_param
-         // size
-         // align
-         // copy_glue
-         // drop_glue
-         // free_glue
-         // sever_glue
-         // mark_glue
-         // obj_drop_glue
-         // is_stateful
-        elems =
+    let elems =
         ~[tydescpp, T_int(), T_int(), glue_fn_ty, glue_fn_ty, glue_fn_ty,
-          glue_fn_ty, glue_fn_ty, glue_fn_ty, glue_fn_ty, cmp_glue_fn_ty];
+          glue_fn_ty, glue_fn_ty, glue_fn_ty, glue_fn_ty, cmp_glue_fn_ty,
+          T_ptr(T_i8()), T_ptr(T_i8()), T_int()];
     set_struct_body(tydesc, elems);
     ret tydesc;
 }
@@ -874,3 +865,14 @@ fn C_bytes(bytes : &u8[]) -> ValueRef {
                               ivec::len(bytes), False);
 }
 
+fn C_shape(ccx : &@crate_ctxt, bytes : &u8[]) -> ValueRef {
+    let llshape = C_bytes(bytes);
+    let llglobal = llvm::LLVMAddGlobal(ccx.llmod, val_ty(llshape),
+                                       str::buf(ccx.names.next("shape")));
+    llvm::LLVMSetInitializer(llglobal, llshape);
+    llvm::LLVMSetGlobalConstant(llglobal, True);
+    llvm::LLVMSetLinkage(llglobal,
+                         lib::llvm::LLVMInternalLinkage as llvm::Linkage);
+    ret llvm::LLVMConstPointerCast(llglobal, T_ptr(T_i8()));
+}
+
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 556c9e36bea..00f2e6a1b86 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -184,6 +184,7 @@ export type_param;
 export unify;
 export variant_info;
 export walk_ty;
+export occurs_check_fails;
 
 // Data types
 tag mode { mo_val; mo_alias(bool); }
@@ -655,6 +656,7 @@ fn walk_ty(cx: &ctxt, walker: ty_walk, ty: t) {
       ty_str. {/* no-op */ }
       ty_istr. {/* no-op */ }
       ty_type. {/* no-op */ }
+      ty_task. {/* no-op */ }
       ty_native(_) {/* no-op */ }
       ty_box(tm) { walk_ty(cx, walker, tm.ty); }
       ty_vec(tm) { walk_ty(cx, walker, tm.ty); }
@@ -686,6 +688,9 @@ fn walk_ty(cx: &ctxt, walker: ty_walk, ty: t) {
         walk_ty(cx, walker, sub);
         for tp: t  in tps { walk_ty(cx, walker, tp); }
       }
+      ty_constr(sub, _) {
+        walk_ty(cx, walker, sub);
+      }
       ty_var(_) {/* no-op */ }
       ty_param(_,_) {/* no-op */ }
     }
@@ -1393,6 +1398,24 @@ fn type_param(cx: &ctxt, ty: &t) -> option::t[uint] {
     ret none;
 }
 
+// Returns an ivec of all the type variables
+// occurring in t. It may contain duplicates.
+fn vars_in_type(cx:&ctxt, ty: &t) -> int[] {
+    fn collect_var(cx:&ctxt, vars: &@mutable int[], ty: t) {
+        alt struct(cx, ty) {
+          ty_var(v) {
+            *vars += ~[v];
+          }
+          _ {}
+        }
+    }
+    let rslt: @mutable int[] = @mutable (~[]);
+    walk_ty(cx, bind collect_var(cx, rslt, _), ty);
+    // Works because of a "convenient" bug that lets us
+    // return a mutable ivec as if it's immutable
+    ret *rslt;
+}
+
 fn type_autoderef(cx: &ctxt, t: &ty::t) -> ty::t {
     let t1: ty::t = t;
     while true {
@@ -1990,6 +2013,32 @@ fn is_lval(expr: &@ast::expr) -> bool {
     }
 }
 
+fn occurs_check_fails(tcx: &ctxt, sp: &option::t[span], vid: int, rt: &t)
+    -> bool {
+    if (!type_contains_vars(tcx, rt)) {
+        // Fast path
+        ret false;
+    }
+    // Occurs check!
+    if ivec::member(vid, vars_in_type(tcx, rt)) {
+        alt sp {
+          some (s) {
+            // Maybe this should be span_err -- however, there's an
+            // assertion later on that the type doesn't contain
+            // variables, so in this case we have to be sure to die.
+            tcx.sess.span_fatal(s,
+                                "Type inference failed because I \
+               could not find a type\n that's both of the form " +
+               ty_to_str(tcx, ty::mk_var(tcx, (vid)))
+              + " and of the form " + ty_to_str(tcx, rt)
+              + ". Such a type would have to be infinitely \
+               large.");
+          }
+          _ { ret true; }
+        }
+    }
+    else { ret false; }
+}
 
 // Type unification via Robinson's algorithm (Robinson 1965). Implemented as
 // described in Hoder and Voronkov:
@@ -2318,9 +2367,6 @@ mod unify {
         // TODO: rewrite this using tuple pattern matching when available, to
         // avoid all this rightward drift and spikiness.
 
-        // TODO: occurs check, to make sure we don't loop forever when
-        // unifying e.g. 'a and option['a]
-
         // Fast path.
 
         if eq_ty(expected, actual) { ret ures_ok(expected); }
@@ -2694,9 +2740,15 @@ mod unify {
     }
 
     // Fixups and substitutions
-    fn fixup_vars(tcx: ty_ctxt, vb: @var_bindings, typ: t) -> fixup_result {
-        fn subst_vars(tcx: ty_ctxt, vb: @var_bindings,
+    //    Takes an optional span - complain about occurs check violations
+    //    iff the span is present (so that if we already know we're going
+    //    to error anyway, we don't complain)
+    fn fixup_vars(tcx: ty_ctxt, sp: &option::t[span],
+                  vb: @var_bindings, typ: t) -> fixup_result {
+        fn subst_vars(tcx: ty_ctxt, sp: &option::t[span], vb: @var_bindings,
                       unresolved: @mutable option::t[int], vid: int) -> t {
+            // Should really return a fixup_result instead of a t, but fold_ty
+            // doesn't allow returning anything but a t.
             if vid as uint >= ufindivec::set_count(vb.sets) {
                 *unresolved = some(vid);
                 ret ty::mk_var(tcx, vid);
@@ -2705,15 +2757,18 @@ mod unify {
             alt smallintmap::find[t](vb.types, root_id) {
               none. { *unresolved = some(vid); ret ty::mk_var(tcx, vid); }
               some(rt) {
+                if occurs_check_fails(tcx, sp, vid, rt) {
+      // Return the type unchanged, so we can error out downstream
+                    ret rt;
+                }
                 ret fold_ty(tcx,
-                            fm_var(bind subst_vars(tcx, vb, unresolved, _)),
-                            rt);
+                  fm_var(bind subst_vars(tcx, sp, vb, unresolved, _)), rt);
               }
             }
         }
         let unresolved = @mutable none[int];
         let rty =
-            fold_ty(tcx, fm_var(bind subst_vars(tcx, vb, unresolved, _)),
+            fold_ty(tcx, fm_var(bind subst_vars(tcx, sp, vb, unresolved, _)),
                     typ);
         let ur = *unresolved;
         alt ur {
@@ -2721,13 +2776,14 @@ mod unify {
           some(var_id) { ret fix_err(var_id); }
         }
     }
-    fn resolve_type_var(tcx: &ty_ctxt, vb: &@var_bindings, vid: int) ->
+    fn resolve_type_var(tcx: &ty_ctxt, sp: &option::t[span],
+                        vb: &@var_bindings, vid: int) ->
        fixup_result {
         if vid as uint >= ufindivec::set_count(vb.sets) { ret fix_err(vid); }
         let root_id = ufindivec::find(vb.sets, vid as uint);
         alt smallintmap::find[t](vb.types, root_id) {
           none. { ret fix_err(vid); }
-          some(rt) { ret fixup_vars(tcx, vb, rt); }
+          some(rt) { ret fixup_vars(tcx, sp, vb, rt); }
         }
     }
 }
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index d5bc8b0cbe3..e5a8f7eba8a 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -879,7 +879,18 @@ fn do_autoderef(fcx: &@fn_ctxt, sp: &span, t: &ty::t) -> ty::t {
     let t1 = t;
     while true {
         alt structure_of(fcx, sp, t1) {
-          ty::ty_box(inner) { t1 = inner.ty; }
+          ty::ty_box(inner) {
+            alt ty::struct(fcx.ccx.tcx, t1) {
+              ty::ty_var(v1) {
+                if ty::occurs_check_fails(fcx.ccx.tcx, some(sp), v1,
+                                          ty::mk_box(fcx.ccx.tcx, inner)) {
+                    break;
+                }
+              }
+              _ {}
+            }
+            t1 = inner.ty;
+          }
           ty::ty_res(_, inner, tps) {
             t1 = ty::substitute_type_params(fcx.ccx.tcx, tps, inner);
           }
@@ -942,7 +953,7 @@ fn do_fn_block_coerce(fcx: &@fn_ctxt, sp: &span, actual: &ty::t,
 
 
 fn resolve_type_vars_if_possible(fcx: &@fn_ctxt, typ: ty::t) -> ty::t {
-    alt ty::unify::fixup_vars(fcx.ccx.tcx, fcx.var_bindings, typ) {
+    alt ty::unify::fixup_vars(fcx.ccx.tcx, none, fcx.var_bindings, typ) {
       fix_ok(new_type) { ret new_type; }
       fix_err(_) { ret typ; }
     }
@@ -1073,7 +1084,8 @@ mod writeback {
     fn resolve_type_vars_in_type(fcx: &@fn_ctxt, sp: &span, typ: ty::t) ->
        option::t[ty::t] {
         if !ty::type_contains_vars(fcx.ccx.tcx, typ) { ret some(typ); }
-        alt ty::unify::fixup_vars(fcx.ccx.tcx, fcx.var_bindings, typ) {
+        alt ty::unify::fixup_vars(fcx.ccx.tcx, some(sp),
+                                  fcx.var_bindings, typ) {
           fix_ok(new_type) { ret some(new_type); }
           fix_err(vid) {
             fcx.ccx.tcx.sess.span_err(sp,
@@ -1139,7 +1151,7 @@ mod writeback {
         if !wbcx.success { ret; }
         let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
         let fix_rslt =
-            ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx,
+            ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, some(l.span),
                                         wbcx.fcx.var_bindings, var_id);
         alt fix_rslt {
           fix_ok(lty) { write::ty_only(wbcx.fcx.ccx.tcx, l.node.id, lty); }
diff --git a/src/test/compile-fail/occurs-check-2.rs b/src/test/compile-fail/occurs-check-2.rs
new file mode 100644
index 00000000000..9b5536e57c3
--- /dev/null
+++ b/src/test/compile-fail/occurs-check-2.rs
@@ -0,0 +1,5 @@
+// error-pattern: Type inference failed because I could not find
+fn main() {
+    let f = @f;
+    f();
+}
\ No newline at end of file
diff --git a/src/test/compile-fail/occurs-check.rs b/src/test/compile-fail/occurs-check.rs
new file mode 100644
index 00000000000..a2bd12fcc35
--- /dev/null
+++ b/src/test/compile-fail/occurs-check.rs
@@ -0,0 +1,4 @@
+// error-pattern: Type inference failed because I could not find
+fn main() {
+    let f = @f;
+}
diff --git a/src/test/run-pass/alt-bot-2.rs b/src/test/run-pass/alt-bot-2.rs
new file mode 100644
index 00000000000..3183b580fc6
--- /dev/null
+++ b/src/test/run-pass/alt-bot-2.rs
@@ -0,0 +1,3 @@
+// n.b. This was only ever failing with optimization disabled.
+fn a() -> int { alt ret 1 { 2 { 3 } } }
+fn main() { a(); }