aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/comp/middle/trans.rs529
-rw-r--r--src/comp/middle/ty.rs7
-rw-r--r--src/test/run-pass/seq-compare.rs2
-rw-r--r--src/test/run-pass/structured-compare-recursive.rs9
4 files changed, 350 insertions, 197 deletions
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index bb8ecd9c..d9b58c11 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -374,7 +374,7 @@ fn T_tydesc(type_names tn) -> TypeRef {
T_ptr(T_nil()),
tydescpp,
pvoid), T_void()));
- auto cmp_glue_fn_ty = T_ptr(T_fn(vec(T_ptr(T_i8()),
+ auto cmp_glue_fn_ty = T_ptr(T_fn(vec(T_ptr(T_i1()),
T_taskptr(tn),
T_ptr(T_nil()),
tydescpp,
@@ -887,6 +887,10 @@ fn C_int(int i) -> ValueRef {
ret C_integral(i, T_int());
}
+fn C_i8(uint i) -> ValueRef {
+ ret C_integral(i as int, T_i8());
+}
+
// This is a 'c-like' raw string, which differs from
// our boxed-and-length-annotated strings.
fn C_cstr(@crate_ctxt cx, str s) -> ValueRef {
@@ -1730,7 +1734,7 @@ fn make_generic_glue(@local_ctxt cx,
}
case (mgghf_cmp) {
auto llrawptr1 = llvm.LLVMGetParam(llfn, 5u);
- auto llval1 = bcx.build.BitCast(llrawptr0, llty);
+ auto llval1 = bcx.build.BitCast(llrawptr1, llty);
auto llcmpval = llvm.LLVMGetParam(llfn, 6u);
@@ -1980,9 +1984,257 @@ fn decr_refcnt_and_if_zero(@block_ctxt cx,
ret res(next_cx, phi);
}
-fn make_cmp_glue(@block_ctxt cx, ValueRef v0, ValueRef v1, @ty.t t,
- ValueRef llop) {
- cx.build.RetVoid(); // TODO
+// Structural comparison: a rather involved form of glue.
+
+fn make_cmp_glue(@block_ctxt cx,
+ ValueRef lhs0,
+ ValueRef rhs0,
+ @ty.t t,
+ ValueRef llop) {
+ auto lhs = load_if_immediate(cx, lhs0, t);
+ auto rhs = load_if_immediate(cx, rhs0, t);
+
+ if (ty.type_is_scalar(t)) {
+ make_scalar_cmp_glue(cx, lhs, rhs, t, llop);
+
+ } else if (ty.type_is_box(t)) {
+ lhs = cx.build.GEP(lhs, vec(C_int(0), C_int(abi.box_rc_field_body)));
+ rhs = cx.build.GEP(rhs, vec(C_int(0), C_int(abi.box_rc_field_body)));
+ auto rslt = call_cmp_glue(cx, lhs, rhs, t, llop);
+
+ rslt.bcx.build.Store(rslt.val, cx.fcx.llretptr);
+ rslt.bcx.build.RetVoid();
+
+ } else if (ty.type_is_structural(t)
+ || ty.type_is_sequence(t)) {
+
+ auto scx = new_sub_block_ctxt(cx, "structural compare start");
+ auto next = new_sub_block_ctxt(cx, "structural compare end");
+ cx.build.Br(scx.llbb);
+
+ /*
+ * We're doing lexicographic comparison here. We start with the
+ * assumption that the two input elements are equal. Depending on
+ * operator, this means that the result is either true or false;
+ * equality produces 'true' for ==, <= and >=. It produces 'false' for
+ * !=, < and >.
+ *
+ * We then move one element at a time through the structure checking
+ * for pairwise element equality. If we have equality, our assumption
+ * about overall sequence equality is not modified, so we have to move
+ * to the next element.
+ *
+ * If we do not have pairwise element equality, we have reached an
+ * element that 'decides' the lexicographic comparison. So we exit the
+ * loop with a flag that indicates the true/false sense of that
+ * decision, by testing the element again with the operator we're
+ * interested in.
+ *
+ * When we're lucky, LLVM should be able to fold some of these two
+ * tests together (as they're applied to the same operands and in some
+ * cases are sometimes redundant). But we don't bother trying to
+ * optimize combinations like that, at this level.
+ */
+
+ auto flag = alloca(scx, T_i1());
+ llvm.LLVMSetValueName(flag, _str.buf("flag"));
+
+ auto r;
+ if (ty.type_is_sequence(t)) {
+
+ // If we hit == all the way through the minimum-shared-length
+ // section, default to judging the relative sequence lengths.
+ r = compare_integral_values(scx,
+ vec_fill(scx, lhs),
+ vec_fill(scx, rhs),
+ false,
+ llop);
+ r.bcx.build.Store(r.val, flag);
+
+ } else {
+ // == and <= default to true if they find == all the way. <
+ // defaults to false if it finds == all the way.
+ auto result_if_equal = scx.build.ICmp(lib.llvm.LLVMIntNE, llop,
+ C_i8(abi.cmp_glue_op_lt));
+ scx.build.Store(result_if_equal, flag);
+ r = res(scx, C_nil());
+ }
+
+ fn inner(@block_ctxt last_cx,
+ bool load_inner,
+ ValueRef flag,
+ ValueRef llop,
+ @block_ctxt cx,
+ ValueRef av0,
+ ValueRef bv0,
+ @ty.t t) -> result {
+
+ auto cnt_cx = new_sub_block_ctxt(cx, "continue_comparison");
+ auto stop_cx = new_sub_block_ctxt(cx, "stop_comparison");
+
+ auto av = av0;
+ auto bv = bv0;
+ if (load_inner) {
+ // If `load_inner` is true, then the pointer type will always
+ // be i8, because the data part of a vector always has type
+ // i8[]. So we need to cast it to the proper type.
+
+ if (!ty.type_has_dynamic_size(t)) {
+ auto llelemty = T_ptr(type_of(last_cx.fcx.lcx.ccx, t));
+ av = cx.build.PointerCast(av, llelemty);
+ bv = cx.build.PointerCast(bv, llelemty);
+ }
+
+ av = load_if_immediate(cx, av, t);
+ bv = load_if_immediate(cx, bv, t);
+ }
+
+ // First 'eq' comparison: if so, continue to next elts.
+ auto eq_r = call_cmp_glue(cx, av, bv, t,
+ C_i8(abi.cmp_glue_op_eq));
+ eq_r.bcx.build.CondBr(eq_r.val, cnt_cx.llbb, stop_cx.llbb);
+
+ // Second 'op' comparison: find out how this elt-pair decides.
+ auto stop_r = call_cmp_glue(stop_cx, av, bv, t, llop);
+ stop_r.bcx.build.Store(stop_r.val, flag);
+ stop_r.bcx.build.Br(last_cx.llbb);
+ ret res(cnt_cx, C_nil());
+ }
+
+ if (ty.type_is_structural(t)) {
+ r = iter_structural_ty_full(r.bcx, lhs, rhs, t,
+ bind inner(next, false, flag, llop,
+ _, _, _, _));
+ } else {
+ auto lhs_p0 = vec_p0(r.bcx, lhs);
+ auto rhs_p0 = vec_p0(r.bcx, rhs);
+ auto min_len = umin(r.bcx, vec_fill(r.bcx, lhs),
+ vec_fill(r.bcx, rhs));
+ auto rhs_lim = r.bcx.build.GEP(rhs_p0, vec(min_len));
+ auto elt_ty = ty.sequence_element_type(t);
+ r = size_of(r.bcx, elt_ty);
+ r = iter_sequence_raw(r.bcx, lhs_p0, rhs_p0, rhs_lim, r.val,
+ bind inner(next, true, flag, llop,
+ _, _, _, elt_ty));
+ }
+
+ r.bcx.build.Br(next.llbb);
+ auto v = next.build.Load(flag);
+
+ next.build.Store(v, cx.fcx.llretptr);
+ next.build.RetVoid();
+
+
+ } else {
+ // FIXME: compare obj, fn by pointer?
+ trans_fail(cx, none[common.span],
+ "attempt to compare values of type " + ty.ty_to_str(t));
+ }
+}
+
+// A helper function to create scalar comparison glue.
+fn make_scalar_cmp_glue(@block_ctxt cx, ValueRef lhs, ValueRef rhs, @ty.t t,
+ ValueRef llop) {
+ if (ty.type_is_fp(t)) {
+ make_fp_cmp_glue(cx, lhs, rhs, t, llop);
+ ret;
+ }
+
+ if (ty.type_is_integral(t) || ty.type_is_bool(t)) {
+ make_integral_cmp_glue(cx, lhs, rhs, t, llop);
+ ret;
+ }
+
+ if (ty.type_is_nil(t)) {
+ cx.build.Store(C_bool(true), cx.fcx.llretptr);
+ cx.build.RetVoid();
+ ret;
+ }
+
+ trans_fail(cx, none[common.span],
+ "attempt to compare values of type " + ty.ty_to_str(t));
+}
+
+// A helper function to create floating point comparison glue.
+fn make_fp_cmp_glue(@block_ctxt cx, ValueRef lhs, ValueRef rhs, @ty.t fptype,
+ ValueRef llop) {
+ auto last_cx = new_sub_block_ctxt(cx, "last");
+
+ auto eq_cx = new_sub_block_ctxt(cx, "eq");
+ auto eq_result = eq_cx.build.FCmp(lib.llvm.LLVMRealUEQ, lhs, rhs);
+ eq_cx.build.Br(last_cx.llbb);
+
+ auto lt_cx = new_sub_block_ctxt(cx, "lt");
+ auto lt_result = lt_cx.build.FCmp(lib.llvm.LLVMRealULT, lhs, rhs);
+ lt_cx.build.Br(last_cx.llbb);
+
+ auto le_cx = new_sub_block_ctxt(cx, "le");
+ auto le_result = le_cx.build.FCmp(lib.llvm.LLVMRealULE, lhs, rhs);
+ le_cx.build.Br(last_cx.llbb);
+
+ auto unreach_cx = new_sub_block_ctxt(cx, "unreach");
+ unreach_cx.build.Unreachable();
+
+ auto llswitch = cx.build.Switch(llop, unreach_cx.llbb, 3u);
+ llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_eq), eq_cx.llbb);
+ llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_lt), lt_cx.llbb);
+ llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_le), le_cx.llbb);
+
+ auto last_result =
+ last_cx.build.Phi(T_i1(), vec(eq_result, lt_result, le_result),
+ vec(eq_cx.llbb, lt_cx.llbb, le_cx.llbb));
+ last_cx.build.Store(last_result, cx.fcx.llretptr);
+ last_cx.build.RetVoid();
+}
+
+// A helper function to compare integral values. This is used by both
+// `make_integral_cmp_glue` and `make_cmp_glue`.
+fn compare_integral_values(@block_ctxt cx, ValueRef lhs, ValueRef rhs,
+ bool signed, ValueRef llop) -> result {
+ auto lt_cmp; auto le_cmp;
+ if (signed) {
+ lt_cmp = lib.llvm.LLVMIntSLT;
+ le_cmp = lib.llvm.LLVMIntSLE;
+ } else {
+ lt_cmp = lib.llvm.LLVMIntULT;
+ le_cmp = lib.llvm.LLVMIntULE;
+ }
+
+ auto last_cx = new_sub_block_ctxt(cx, "last");
+
+ auto eq_cx = new_sub_block_ctxt(cx, "eq");
+ auto eq_result = eq_cx.build.ICmp(lib.llvm.LLVMIntEQ, lhs, rhs);
+ eq_cx.build.Br(last_cx.llbb);
+
+ auto lt_cx = new_sub_block_ctxt(cx, "lt");
+ auto lt_result = lt_cx.build.ICmp(lt_cmp, lhs, rhs);
+ lt_cx.build.Br(last_cx.llbb);
+
+ auto le_cx = new_sub_block_ctxt(cx, "le");
+ auto le_result = le_cx.build.ICmp(le_cmp, lhs, rhs);
+ le_cx.build.Br(last_cx.llbb);
+
+ auto unreach_cx = new_sub_block_ctxt(cx, "unreach");
+ unreach_cx.build.Unreachable();
+
+ auto llswitch = cx.build.Switch(llop, unreach_cx.llbb, 3u);
+ llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_eq), eq_cx.llbb);
+ llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_lt), lt_cx.llbb);
+ llvm.LLVMAddCase(llswitch, C_i8(abi.cmp_glue_op_le), le_cx.llbb);
+
+ auto last_result =
+ last_cx.build.Phi(T_i1(), vec(eq_result, lt_result, le_result),
+ vec(eq_cx.llbb, lt_cx.llbb, le_cx.llbb));
+ ret res(last_cx, last_result);
+}
+
+// A helper function to create integral comparison glue.
+fn make_integral_cmp_glue(@block_ctxt cx, ValueRef lhs, ValueRef rhs,
+ @ty.t intype, ValueRef llop) {
+ auto r = compare_integral_values(cx, lhs, rhs, ty.type_is_signed(intype),
+ llop);
+ r.bcx.build.Store(r.val, r.bcx.fcx.llretptr);
+ r.bcx.build.RetVoid();
}
@@ -2403,6 +2655,42 @@ fn call_tydesc_glue(@block_ctxt cx, ValueRef v, @ty.t t, int field) {
td.val, field);
}
+fn call_cmp_glue(@block_ctxt cx, ValueRef lhs, ValueRef rhs, @ty.t t,
+ ValueRef llop) -> result {
+ // We can't use call_tydesc_glue_full() and friends here because compare
+ // glue has a special signature.
+
+ auto lllhs = spill_if_immediate(cx, lhs, t);
+ auto llrhs = spill_if_immediate(cx, rhs, t);
+
+ auto llrawlhsptr = cx.build.BitCast(lllhs, T_ptr(T_i8()));
+ auto llrawrhsptr = cx.build.BitCast(llrhs, T_ptr(T_i8()));
+
+ auto r = get_tydesc(cx, t);
+ auto lltydescs =
+ r.bcx.build.GEP(r.val, vec(C_int(0),
+ C_int(abi.tydesc_field_first_param)));
+ lltydescs = r.bcx.build.Load(lltydescs);
+ auto llfnptr =
+ r.bcx.build.GEP(r.val, vec(C_int(0),
+ C_int(abi.tydesc_field_cmp_glue)));
+ auto llfn = r.bcx.build.Load(llfnptr);
+
+ auto llcmpresultptr = r.bcx.build.Alloca(T_i1());
+
+ let vec[ValueRef] llargs = vec(llcmpresultptr,
+ r.bcx.fcx.lltaskptr,
+ C_null(T_ptr(T_nil())),
+ lltydescs,
+ llrawlhsptr,
+ llrawrhsptr,
+ llop);
+
+ r.bcx.build.FastCall(llfn, llargs);
+
+ ret res(r.bcx, r.bcx.build.Load(llcmpresultptr));
+}
+
fn take_ty(@block_ctxt cx, ValueRef v, @ty.t t) -> result {
if (!ty.type_is_scalar(t)) {
call_tydesc_glue(cx, v, t, abi.tydesc_field_take_glue);
@@ -2661,7 +2949,7 @@ fn trans_unary(@block_ctxt cx, ast.unop op,
fn trans_compare(@block_ctxt cx0, ast.binop op, @ty.t t0,
ValueRef lhs0, ValueRef rhs0) -> result {
-
+ // Autoderef both sides.
auto cx = cx0;
auto lhs_r = autoderef(cx, lhs0, t0);
@@ -2674,194 +2962,30 @@ fn trans_compare(@block_ctxt cx0, ast.binop op, @ty.t t0,
auto t = autoderefed_ty(t0);
- if (ty.type_is_scalar(t)) {
- ret res(cx, trans_scalar_compare(cx, op, t, lhs, rhs));
-
- } else if (ty.type_is_structural(t)
- || ty.type_is_sequence(t)) {
-
- auto scx = new_sub_block_ctxt(cx, "structural compare start");
- auto next = new_sub_block_ctxt(cx, "structural compare end");
- cx.build.Br(scx.llbb);
-
- /*
- * We're doing lexicographic comparison here. We start with the
- * assumption that the two input elements are equal. Depending on
- * operator, this means that the result is either true or false;
- * equality produces 'true' for ==, <= and >=. It produces 'false' for
- * !=, < and >.
- *
- * We then move one element at a time through the structure checking
- * for pairwise element equality. If we have equality, our assumption
- * about overall sequence equality is not modified, so we have to move
- * to the next element.
- *
- * If we do not have pairwise element equality, we have reached an
- * element that 'decides' the lexicographic comparison. So we exit the
- * loop with a flag that indicates the true/false sense of that
- * decision, by testing the element again with the operator we're
- * interested in.
- *
- * When we're lucky, LLVM should be able to fold some of these two
- * tests together (as they're applied to the same operands and in some
- * cases are sometimes redundant). But we don't bother trying to
- * optimize combinations like that, at this level.
- */
-
- auto flag = alloca(scx, T_i1());
-
- if (ty.type_is_sequence(t)) {
-
- // If we hit == all the way through the minimum-shared-length
- // section, default to judging the relative sequence lengths.
- auto len_cmp =
- trans_integral_compare(scx, op, plain_ty(ty.ty_uint),
- vec_fill(scx, lhs),
- vec_fill(scx, rhs));
- scx.build.Store(len_cmp, flag);
-
- } else {
- auto T = C_integral(1, T_i1());
- auto F = C_integral(0, T_i1());
-
- alt (op) {
- // ==, <= and >= default to true if they find == all the way.
- case (ast.eq) { scx.build.Store(T, flag); }
- case (ast.le) { scx.build.Store(T, flag); }
- case (ast.ge) { scx.build.Store(T, flag); }
- case (_) {
- // < > default to false if they find == all the way.
- scx.build.Store(F, flag);
- }
-
- }
- }
-
- fn inner(@block_ctxt last_cx,
- bool load_inner,
- ValueRef flag,
- ast.binop op,
- @block_ctxt cx,
- ValueRef av0,
- ValueRef bv0,
- @ty.t t) -> result {
-
- auto cnt_cx = new_sub_block_ctxt(cx, "continue comparison");
- auto stop_cx = new_sub_block_ctxt(cx, "stop comparison");
-
- auto av = av0;
- auto bv = bv0;
- if (load_inner) {
- av = load_if_immediate(cx, av, t);
- bv = load_if_immediate(cx, bv, t);
- }
-
- // First 'eq' comparison: if so, continue to next elts.
- auto eq_r = trans_compare(cx, ast.eq, t, av, bv);
- eq_r.bcx.build.CondBr(eq_r.val, cnt_cx.llbb, stop_cx.llbb);
-
- // Second 'op' comparison: find out how this elt-pair decides.
- auto stop_r = trans_compare(stop_cx, op, t, av, bv);
- stop_r.bcx.build.Store(stop_r.val, flag);
- stop_r.bcx.build.Br(last_cx.llbb);
- ret res(cnt_cx, C_nil());
- }
-
- auto r;
- if (ty.type_is_structural(t)) {
- r = iter_structural_ty_full(scx, lhs, rhs, t,
- bind inner(next, false, flag, op,
- _, _, _, _));
- } else {
- auto lhs_p0 = vec_p0(scx, lhs);
- auto rhs_p0 = vec_p0(scx, rhs);
- auto min_len = umin(scx, vec_fill(scx, lhs), vec_fill(scx, rhs));
- auto rhs_lim = scx.build.GEP(rhs_p0, vec(min_len));
- auto elt_ty = ty.sequence_element_type(t);
- auto elt_llsz_r = size_of(scx, elt_ty);
- scx = elt_llsz_r.bcx;
- r = iter_sequence_raw(scx, lhs_p0, rhs_p0, rhs_lim,
- elt_llsz_r.val,
- bind inner(next, true, flag, op,
- _, _, _, elt_ty));
- }
-
- r.bcx.build.Br(next.llbb);
- auto v = next.build.Load(flag);
- ret res(next, v);
-
-
- } else {
- // FIXME: compare obj, fn by pointer?
- cx.fcx.lcx.ccx.sess.unimpl("type in trans_compare");
- ret res(cx, C_bool(false));
- }
-}
-
-fn trans_scalar_compare(@block_ctxt cx, ast.binop op, @ty.t t,
- ValueRef lhs, ValueRef rhs) -> ValueRef {
- if (ty.type_is_fp(t)) {
- ret trans_fp_compare(cx, op, t, lhs, rhs);
- } else {
- ret trans_integral_compare(cx, op, t, lhs, rhs);
- }
-}
-
-fn trans_fp_compare(@block_ctxt cx, ast.binop op, @ty.t fptype,
- ValueRef lhs, ValueRef rhs) -> ValueRef {
-
- auto cmp = lib.llvm.LLVMIntEQ;
+ // Determine the operation we need.
+ // FIXME: Use or-patterns when we have them.
+ auto llop;
alt (op) {
- // FIXME: possibly use the unordered-or-< predicates here,
- // for now we're only going with ordered-and-< style (no NaNs).
- case (ast.eq) { cmp = lib.llvm.LLVMRealOEQ; }
- case (ast.ne) { cmp = lib.llvm.LLVMRealONE; }
- case (ast.lt) { cmp = lib.llvm.LLVMRealOLT; }
- case (ast.gt) { cmp = lib.llvm.LLVMRealOGT; }
- case (ast.le) { cmp = lib.llvm.LLVMRealOLE; }
- case (ast.ge) { cmp = lib.llvm.LLVMRealOGE; }
+ case (ast.eq) { llop = C_i8(abi.cmp_glue_op_eq); }
+ case (ast.lt) { llop = C_i8(abi.cmp_glue_op_lt); }
+ case (ast.le) { llop = C_i8(abi.cmp_glue_op_le); }
+ case (ast.ne) { llop = C_i8(abi.cmp_glue_op_eq); }
+ case (ast.ge) { llop = C_i8(abi.cmp_glue_op_lt); }
+ case (ast.gt) { llop = C_i8(abi.cmp_glue_op_le); }
}
- ret cx.build.FCmp(cmp, lhs, rhs);
-}
+ auto rslt = call_cmp_glue(cx, lhs, rhs, t, llop);
-fn trans_integral_compare(@block_ctxt cx, ast.binop op, @ty.t intype,
- ValueRef lhs, ValueRef rhs) -> ValueRef {
- auto cmp = lib.llvm.LLVMIntEQ;
+ // Invert the result if necessary.
+ // FIXME: Use or-patterns when we have them.
alt (op) {
- case (ast.eq) { cmp = lib.llvm.LLVMIntEQ; }
- case (ast.ne) { cmp = lib.llvm.LLVMIntNE; }
-
- case (ast.lt) {
- if (ty.type_is_signed(intype)) {
- cmp = lib.llvm.LLVMIntSLT;
- } else {
- cmp = lib.llvm.LLVMIntULT;
- }
- }
- case (ast.le) {
- if (ty.type_is_signed(intype)) {
- cmp = lib.llvm.LLVMIntSLE;
- } else {
- cmp = lib.llvm.LLVMIntULE;
- }
- }
- case (ast.gt) {
- if (ty.type_is_signed(intype)) {
- cmp = lib.llvm.LLVMIntSGT;
- } else {
- cmp = lib.llvm.LLVMIntUGT;
- }
- }
- case (ast.ge) {
- if (ty.type_is_signed(intype)) {
- cmp = lib.llvm.LLVMIntSGE;
- } else {
- cmp = lib.llvm.LLVMIntUGE;
- }
- }
+ case (ast.eq) { ret res(rslt.bcx, rslt.val); }
+ case (ast.lt) { ret res(rslt.bcx, rslt.val); }
+ case (ast.le) { ret res(rslt.bcx, rslt.val); }
+ case (ast.ne) { ret res(rslt.bcx, rslt.bcx.build.Not(rslt.val)); }
+ case (ast.ge) { ret res(rslt.bcx, rslt.bcx.build.Not(rslt.val)); }
+ case (ast.gt) { ret res(rslt.bcx, rslt.bcx.build.Not(rslt.val)); }
}
- ret cx.build.ICmp(cmp, lhs, rhs);
}
fn trans_vec_append(@block_ctxt cx, @ty.t t,
@@ -3678,7 +3802,7 @@ fn trans_alt(@block_ctxt cx, @ast.expr expr,
}
auto default_cx = this_cx;
- auto default_res = trans_fail(default_cx, expr.span,
+ auto default_res = trans_fail(default_cx, some[common.span](expr.span),
"non-exhaustive match failure");
// FIXME: This isn't quite right, particularly re: dynamic types
@@ -3976,7 +4100,8 @@ fn trans_index(@block_ctxt cx, &ast.span sp, @ast.expr base,
bcx.build.CondBr(bounds_check, next_cx.llbb, fail_cx.llbb);
// fail: bad bounds check.
- auto fail_res = trans_fail(fail_cx, sp, "bounds check");
+ auto fail_res = trans_fail(fail_cx, some[common.span](sp),
+ "bounds check");
auto body = next_cx.build.GEP(v, vec(C_int(0), C_int(abi.vec_elt_data)));
auto elt;
@@ -4893,7 +5018,7 @@ fn trans_expr(@block_ctxt cx, @ast.expr e) -> result {
}
case (ast.expr_fail(_)) {
- ret trans_fail(cx, e.span, "explicit failure");
+ ret trans_fail(cx, some[common.span](e.span), "explicit failure");
}
case (ast.expr_log(?lvl, ?a, _)) {
@@ -5062,7 +5187,7 @@ fn trans_check_expr(@block_ctxt cx, @ast.expr e) -> result {
auto expr_str = pretty.pprust.expr_to_str(e);
auto fail_cx = new_sub_block_ctxt(cx, "fail");
- auto fail_res = trans_fail(fail_cx, e.span, expr_str);
+ auto fail_res = trans_fail(fail_cx, some[common.span](e.span), expr_str);
auto next_cx = new_sub_block_ctxt(cx, "next");
cond_res.bcx.build.CondBr(cond_res.val,
@@ -5071,11 +5196,23 @@ fn trans_check_expr(@block_ctxt cx, @ast.expr e) -> result {
ret res(next_cx, C_nil());
}
-fn trans_fail(@block_ctxt cx, common.span sp, str fail_str) -> result {
+fn trans_fail(@block_ctxt cx, option.t[common.span] sp_opt, str fail_str)
+ -> result {
auto V_fail_str = p2i(C_cstr(cx.fcx.lcx.ccx, fail_str));
- auto loc = cx.fcx.lcx.ccx.sess.lookup_pos(sp.lo);
- auto V_filename = p2i(C_cstr(cx.fcx.lcx.ccx, loc.filename));
- auto V_line = loc.line as int;
+
+ auto V_filename; auto V_line;
+ alt (sp_opt) {
+ case (some[common.span](?sp)) {
+ auto loc = cx.fcx.lcx.ccx.sess.lookup_pos(sp.lo);
+ V_filename = p2i(C_cstr(cx.fcx.lcx.ccx, loc.filename));
+ V_line = loc.line as int;
+ }
+ case (none[common.span]) {
+ V_filename = p2i(C_str(cx.fcx.lcx.ccx, "<runtime>"));
+ V_line = 0;
+ }
+ }
+
auto args = vec(V_fail_str, V_filename, C_int(V_line));
auto sub = trans_upcall(cx, "upcall_fail", args);
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 06fbf76e..a25c0057 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -437,6 +437,13 @@ fn type_is_nil(@t ty) -> bool {
fail;
}
+fn type_is_bool(@t ty) -> bool {
+ alt (ty.struct) {
+ case (ty_bool) { ret true; }
+ case (_) { ret false; }
+ }
+}
+
fn type_is_structural(@t ty) -> bool {
alt (ty.struct) {
diff --git a/src/test/run-pass/seq-compare.rs b/src/test/run-pass/seq-compare.rs
index 36db2b46..e8bc4788 100644
--- a/src/test/run-pass/seq-compare.rs
+++ b/src/test/run-pass/seq-compare.rs
@@ -13,4 +13,4 @@ fn main() {
check (vec(1,2,3,4) > vec(1,2,3));
check (vec(1,2,3) == vec(1,2,3));
check (vec(1,2,3) != vec(1,1,3));
-} \ No newline at end of file
+}
diff --git a/src/test/run-pass/structured-compare-recursive.rs b/src/test/run-pass/structured-compare-recursive.rs
new file mode 100644
index 00000000..3049faec
--- /dev/null
+++ b/src/test/run-pass/structured-compare-recursive.rs
@@ -0,0 +1,9 @@
+tag taggy {
+ foo(@taggy);
+ bar;
+}
+
+fn main() {
+ check (bar <= bar);
+}
+