authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-17 14:34:10+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-09-17 14:34:10+01:00
log41330c96aebddbf26d8fdc0725e7483476175601
tree15b108f8983affc4933d60d6b0eaf6e2c2db35ee
parent812557bfde3c577b5f00cb556201c71ad5ed6fa4
parent4650e5b9fcaa74b724a51458f5cf8952f3c734de
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21428 from mlugg/compare-to-undef

Sema: return undefined on comparison of runtime value against undefined

5 files changed, 192 insertions(+), 181 deletions(-)

src/Sema.zig+120-170
......@@ -17911,12 +17911,15 @@ fn cmpSelf(
1791117911 const pt = sema.pt;
1791217912 const zcu = pt.zcu;
1791317913 const resolved_type = sema.typeOf(casted_lhs);
17914 const runtime_src: LazySrcLoc = src: {
17915 if (try sema.resolveValue(casted_lhs)) |lhs_val| {
17916 if (lhs_val.isUndef(zcu)) return pt.undefRef(Type.bool);
17917 if (try sema.resolveValue(casted_rhs)) |rhs_val| {
17918 if (rhs_val.isUndef(zcu)) return pt.undefRef(Type.bool);
1791917914
17915 const maybe_lhs_val = try sema.resolveValue(casted_lhs);
17916 const maybe_rhs_val = try sema.resolveValue(casted_rhs);
17917 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(Type.bool);
17918 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(Type.bool);
17919
17920 const runtime_src: LazySrcLoc = src: {
17921 if (maybe_lhs_val) |lhs_val| {
17922 if (maybe_rhs_val) |rhs_val| {
1792017923 if (resolved_type.zigTypeTag(zcu) == .vector) {
1792117924 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
1792217925 return Air.internedToRef(cmp_val.toIntern());
......@@ -17937,8 +17940,7 @@ fn cmpSelf(
1793717940 // For bools, we still check the other operand, because we can lower
1793817941 // bool eq/neq more efficiently.
1793917942 if (resolved_type.zigTypeTag(zcu) == .bool) {
17940 if (try sema.resolveValue(casted_rhs)) |rhs_val| {
17941 if (rhs_val.isUndef(zcu)) return pt.undefRef(Type.bool);
17943 if (maybe_rhs_val) |rhs_val| {
1794217944 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
1794317945 }
1794417946 }
......@@ -33837,51 +33839,61 @@ fn cmpNumeric(
3383733839 else
3383833840 uncasted_rhs;
3383933841
33840 const runtime_src: LazySrcLoc = src: {
33841 if (try sema.resolveValue(lhs)) |lhs_val| {
33842 if (try sema.resolveValue(rhs)) |rhs_val| {
33843 // Compare ints: const vs. undefined (or vice versa)
33844 if (!lhs_val.isUndef(zcu) and (lhs_ty.isInt(zcu) or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu) and rhs_val.isUndef(zcu)) {
33845 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
33846 return if (res) .bool_true else .bool_false;
33847 }
33848 } else if (!rhs_val.isUndef(zcu) and (rhs_ty.isInt(zcu) or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu) and lhs_val.isUndef(zcu)) {
33849 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
33850 return if (res) .bool_true else .bool_false;
33851 }
33852 }
33842 const maybe_lhs_val = try sema.resolveValue(lhs);
33843 const maybe_rhs_val = try sema.resolveValue(rhs);
3385333844
33854 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
33855 return pt.undefRef(Type.bool);
33856 }
33857 if (lhs_val.isNan(zcu) or rhs_val.isNan(zcu)) {
33858 return if (op == std.math.CompareOperator.neq) .bool_true else .bool_false;
33859 }
33860 return if (try Value.compareHeteroSema(lhs_val, op, rhs_val, pt))
33861 .bool_true
33862 else
33863 .bool_false;
33864 } else {
33865 if (!lhs_val.isUndef(zcu) and (lhs_ty.isInt(zcu) or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {
33866 // Compare ints: const vs. var
33867 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
33868 return if (res) .bool_true else .bool_false;
33869 }
33870 }
33871 break :src rhs_src;
33872 }
33873 } else {
33874 if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| {
33875 if (!rhs_val.isUndef(zcu) and (rhs_ty.isInt(zcu) or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {
33876 // Compare ints: var vs. const
33877 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
33878 return if (res) .bool_true else .bool_false;
33879 }
33880 }
33881 }
33882 break :src lhs_src;
33883 }
33884 };
33845 // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value.
33846 if (maybe_lhs_val) |lhs_val| {
33847 // Result based on comparison exceeding type bounds
33848 if (!lhs_val.isUndef(zcu) and (lhs_ty_tag == .int or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {
33849 if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| {
33850 return if (res) .bool_true else .bool_false;
33851 }
33852 }
33853 // Result based on NaN comparison
33854 if (lhs_val.isNan(zcu)) {
33855 return if (op == .neq) .bool_true else .bool_false;
33856 }
33857 // Result based on inf comparison to int
33858 if (lhs_val.isInf(zcu) and rhs_ty_tag == .int) return switch (op) {
33859 .neq => .bool_true,
33860 .eq => .bool_false,
33861 .gt, .gte => if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
33862 .lt, .lte => if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
33863 };
33864 }
33865
33866 // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS value.
33867 if (maybe_rhs_val) |rhs_val| {
33868 // Result based on comparison exceeding type bounds
33869 if (!rhs_val.isUndef(zcu) and (rhs_ty_tag == .int or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {
33870 if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| {
33871 return if (res) .bool_true else .bool_false;
33872 }
33873 }
33874 // Result based on NaN comparison
33875 if (rhs_val.isNan(zcu)) {
33876 return if (op == .neq) .bool_true else .bool_false;
33877 }
33878 // Result based on inf comparison to int
33879 if (rhs_val.isInf(zcu) and lhs_ty_tag == .int) return switch (op) {
33880 .neq => .bool_true,
33881 .eq => .bool_false,
33882 .gt, .gte => if (rhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
33883 .lt, .lte => if (rhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
33884 };
33885 }
33886
33887 // Any other comparison depends on both values, so the result is undef if either is undef.
33888 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(Type.bool);
33889 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(Type.bool);
33890
33891 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: {
33892 if (maybe_rhs_val) |rhs_val| {
33893 const res = try Value.compareHeteroSema(lhs_val, op, rhs_val, pt);
33894 return if (res) .bool_true else .bool_false;
33895 } else break :rs rhs_src;
33896 } else lhs_src;
3388533897
3388633898 // TODO handle comparisons against lazy zero values
3388733899 // Some values can be compared against zero without being runtime-known or without forcing
......@@ -33919,17 +33931,18 @@ fn cmpNumeric(
3391933931 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
3392033932 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);
3392133933 }
33934
3392233935 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
3392333936 // For mixed signed and unsigned integers, implicit cast both operands to a signed
3392433937 // integer with + 1 bit.
3392533938 // For mixed floats and integers, extract the integer part from the float, cast that to
3392633939 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
3392733940 // add/subtract 1.
33928 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
33941 const lhs_is_signed = if (maybe_lhs_val) |lhs_val|
3392933942 !(try lhs_val.compareAllWithZeroSema(.gte, pt))
3393033943 else
3393133944 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu));
33932 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
33945 const rhs_is_signed = if (maybe_rhs_val) |rhs_val|
3393333946 !(try rhs_val.compareAllWithZeroSema(.gte, pt))
3393433947 else
3393533948 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu));
......@@ -33938,19 +33951,8 @@ fn cmpNumeric(
3393833951 var dest_float_type: ?Type = null;
3393933952
3394033953 var lhs_bits: usize = undefined;
33941 if (try sema.resolveValueResolveLazy(lhs)) |lhs_val| {
33942 if (lhs_val.isUndef(zcu))
33943 return pt.undefRef(Type.bool);
33944 if (lhs_val.isNan(zcu)) switch (op) {
33945 .neq => return .bool_true,
33946 else => return .bool_false,
33947 };
33948 if (lhs_val.isInf(zcu)) switch (op) {
33949 .neq => return .bool_true,
33950 .eq => return .bool_false,
33951 .gt, .gte => return if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
33952 .lt, .lte => return if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
33953 };
33954 if (maybe_lhs_val) |unresolved_lhs_val| {
33955 const lhs_val = try sema.resolveLazyValue(unresolved_lhs_val);
3395433956 if (!rhs_is_signed) {
3395533957 switch (lhs_val.orderAgainstZero(zcu)) {
3395633958 .gt => {},
......@@ -33996,19 +33998,8 @@ fn cmpNumeric(
3399633998 }
3399733999
3399834000 var rhs_bits: usize = undefined;
33999 if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| {
34000 if (rhs_val.isUndef(zcu))
34001 return pt.undefRef(Type.bool);
34002 if (rhs_val.isNan(zcu)) switch (op) {
34003 .neq => return .bool_true,
34004 else => return .bool_false,
34005 };
34006 if (rhs_val.isInf(zcu)) switch (op) {
34007 .neq => return .bool_true,
34008 .eq => return .bool_false,
34009 .gt, .gte => return if (rhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
34010 .lt, .lte => return if (rhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
34011 };
34001 if (maybe_rhs_val) |unresolved_rhs_val| {
34002 const rhs_val = try sema.resolveLazyValue(unresolved_rhs_val);
3401234003 if (!lhs_is_signed) {
3401334004 switch (rhs_val.orderAgainstZero(zcu)) {
3401434005 .gt => {},
......@@ -34075,90 +34066,49 @@ fn compareIntsOnlyPossibleResult(
3407534066 lhs_val: Value,
3407634067 op: std.math.CompareOperator,
3407734068 rhs_ty: Type,
34078) Allocator.Error!?bool {
34069) SemaError!?bool {
3407934070 const pt = sema.pt;
3408034071 const zcu = pt.zcu;
34081 const rhs_info = rhs_ty.intInfo(zcu);
34082 const vs_zero = lhs_val.orderAgainstZeroSema(pt) catch unreachable;
34083 const is_zero = vs_zero == .eq;
34084 const is_negative = vs_zero == .lt;
34085 const is_positive = vs_zero == .gt;
3408634072
34087 // Anything vs. zero-sized type has guaranteed outcome.
34088 if (rhs_info.bits == 0) return switch (op) {
34089 .eq, .lte, .gte => is_zero,
34090 .neq, .lt, .gt => !is_zero,
34091 };
34073 const min_rhs = try rhs_ty.minInt(pt, rhs_ty);
34074 const max_rhs = try rhs_ty.maxInt(pt, rhs_ty);
3409234075
34093 // Special case for i1, which can only be 0 or -1.
34094 // Zero and positive ints have guaranteed outcome.
34095 if (rhs_info.bits == 1 and rhs_info.signedness == .signed) {
34096 if (is_positive) return switch (op) {
34097 .gt, .gte, .neq => true,
34098 .lt, .lte, .eq => false,
34099 };
34100 if (is_zero) return switch (op) {
34101 .gte => true,
34102 .lt => false,
34103 .gt, .lte, .eq, .neq => null,
34104 };
34076 if (min_rhs.toIntern() == max_rhs.toIntern()) {
34077 // RHS is effectively comptime-known.
34078 return try Value.compareHeteroSema(lhs_val, op, min_rhs, pt);
3410534079 }
3410634080
34107 // Negative vs. unsigned has guaranteed outcome.
34108 if (rhs_info.signedness == .unsigned and is_negative) return switch (op) {
34109 .eq, .gt, .gte => false,
34110 .neq, .lt, .lte => true,
34111 };
34112
34113 const sign_adj = @intFromBool(!is_negative and rhs_info.signedness == .signed);
34114 const req_bits = lhs_val.intBitCountTwosComp(zcu) + sign_adj;
34115
34116 // No sized type can have more than 65535 bits.
34117 // The RHS type operand is either a runtime value or sized (but undefined) constant.
34118 if (req_bits > 65535) return switch (op) {
34119 .lt, .lte => is_negative,
34120 .gt, .gte => is_positive,
34121 .eq => false,
34122 .neq => true,
34123 };
34124 const fits = req_bits <= rhs_info.bits;
34081 const against_min = try lhs_val.orderAdvanced(min_rhs, .sema, zcu, pt.tid);
34082 const against_max = try lhs_val.orderAdvanced(max_rhs, .sema, zcu, pt.tid);
3412534083
34126 // Oversized int has guaranteed outcome.
3412734084 switch (op) {
34128 .eq => return if (!fits) false else null,
34129 .neq => return if (!fits) true else null,
34130 .lt, .lte => if (!fits) return is_negative,
34131 .gt, .gte => if (!fits) return !is_negative,
34085 .eq => {
34086 if (against_min.compare(.lt)) return false;
34087 if (against_max.compare(.gt)) return false;
34088 },
34089 .neq => {
34090 if (against_min.compare(.lt)) return true;
34091 if (against_max.compare(.gt)) return true;
34092 },
34093 .lt => {
34094 if (against_min.compare(.lt)) return true;
34095 if (against_max.compare(.gte)) return false;
34096 },
34097 .gt => {
34098 if (against_max.compare(.gt)) return true;
34099 if (against_min.compare(.lte)) return false;
34100 },
34101 .lte => {
34102 if (against_min.compare(.lte)) return true;
34103 if (against_max.compare(.gt)) return false;
34104 },
34105 .gte => {
34106 if (against_max.compare(.gte)) return true;
34107 if (against_min.compare(.lt)) return false;
34108 },
3413234109 }
3413334110
34134 // For any other comparison, we need to know if the LHS value is
34135 // equal to the maximum or minimum possible value of the RHS type.
34136 const is_min, const is_max = edge: {
34137 if (is_zero and rhs_info.signedness == .unsigned) break :edge .{ true, false };
34138
34139 if (req_bits != rhs_info.bits) break :edge .{ false, false };
34140
34141 const ty = try pt.intType(
34142 if (is_negative) .signed else .unsigned,
34143 @intCast(req_bits),
34144 );
34145 const pop_count = lhs_val.popCount(ty, zcu);
34146
34147 if (is_negative) {
34148 break :edge .{ pop_count == 1, false };
34149 } else {
34150 break :edge .{ false, pop_count == req_bits - sign_adj };
34151 }
34152 };
34153
34154 assert(fits);
34155 return switch (op) {
34156 .lt => if (is_max) false else null,
34157 .lte => if (is_min) true else null,
34158 .gt => if (is_min) false else null,
34159 .gte => if (is_max) true else null,
34160 .eq, .neq => unreachable,
34161 };
34111 return null;
3416234112}
3416334113
3416434114/// Asserts that lhs and rhs types are both vectors.
......@@ -34189,21 +34139,17 @@ fn cmpVector(
3418934139 .child = .bool_type,
3419034140 });
3419134141
34192 const runtime_src: LazySrcLoc = src: {
34193 if (try sema.resolveValue(casted_lhs)) |lhs_val| {
34194 if (try sema.resolveValue(casted_rhs)) |rhs_val| {
34195 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
34196 return pt.undefRef(result_ty);
34197 }
34198 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
34199 return Air.internedToRef(cmp_val.toIntern());
34200 } else {
34201 break :src rhs_src;
34202 }
34203 } else {
34204 break :src lhs_src;
34205 }
34206 };
34142 const maybe_lhs_val = try sema.resolveValue(casted_lhs);
34143 const maybe_rhs_val = try sema.resolveValue(casted_rhs);
34144 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
34145 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
34146
34147 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: {
34148 if (maybe_rhs_val) |rhs_val| {
34149 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
34150 return Air.internedToRef(cmp_val.toIntern());
34151 } else break :src rhs_src;
34152 } else lhs_src;
3420734153
3420834154 try sema.requireRuntimeBlock(block, src, runtime_src);
3420934155 return block.addCmpVector(casted_lhs, casted_rhs, op);
......@@ -38691,8 +38637,12 @@ fn compareVector(
3869138637 for (result_data, 0..) |*scalar, i| {
3869238638 const lhs_elem = try lhs.elemValue(pt, i);
3869338639 const rhs_elem = try rhs.elemValue(pt, i);
38694 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));
38695 scalar.* = Value.makeBool(res_bool).toIntern();
38640 if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) {
38641 scalar.* = try pt.intern(.{ .undef = .bool_type });
38642 } else {
38643 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));
38644 scalar.* = Value.makeBool(res_bool).toIntern();
38645 }
3869638646 }
3869738647 return Value.fromInterned(try pt.intern(.{ .aggregate = .{
3869838648 .ty = (try pt.vectorType(.{ .len = ty.vectorLen(zcu), .child = .bool_type })).toIntern(),
src/Type.zig+2-6
......@@ -3040,8 +3040,7 @@ pub fn minInt(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
30403040pub fn minIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
30413041 const zcu = pt.zcu;
30423042 const info = ty.intInfo(zcu);
3043 if (info.signedness == .unsigned) return pt.intValue(dest_ty, 0);
3044 if (info.bits == 0) return pt.intValue(dest_ty, -1);
3043 if (info.signedness == .unsigned or info.bits == 0) return pt.intValue(dest_ty, 0);
30453044
30463045 if (std.math.cast(u6, info.bits - 1)) |shift| {
30473046 const n = @as(i64, std.math.minInt(i64)) >> (63 - shift);
......@@ -3072,10 +3071,7 @@ pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
30723071 const info = ty.intInfo(pt.zcu);
30733072
30743073 switch (info.bits) {
3075 0 => return switch (info.signedness) {
3076 .signed => try pt.intValue(dest_ty, -1),
3077 .unsigned => try pt.intValue(dest_ty, 0),
3078 },
3074 0 => return pt.intValue(dest_ty, 0),
30793075 1 => return switch (info.signedness) {
30803076 .signed => try pt.intValue(dest_ty, 0),
30813077 .unsigned => try pt.intValue(dest_ty, 1),
src/Value.zig+2-2
......@@ -191,7 +191,7 @@ pub fn toBigIntAdvanced(
191191 comptime strat: ResolveStrat,
192192 zcu: *Zcu,
193193 tid: strat.Tid(),
194) Zcu.CompileError!BigIntConst {
194) Zcu.SemaError!BigIntConst {
195195 const ip = &zcu.intern_pool;
196196 return switch (val.toIntern()) {
197197 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
......@@ -1038,7 +1038,7 @@ pub fn orderAgainstZeroInner(
10381038 comptime strat: ResolveStrat,
10391039 zcu: *Zcu,
10401040 tid: strat.Tid(),
1041) Zcu.CompileError!std.math.Order {
1041) Zcu.SemaError!std.math.Order {
10421042 return switch (lhs.toIntern()) {
10431043 .bool_false => .eq,
10441044 .bool_true => .gt,
test/behavior/math.zig+62
......@@ -1729,3 +1729,65 @@ test "@clz works on both vector and scalar inputs" {
17291729 try std.testing.expectEqual(@as(u6, 31), a);
17301730 try std.testing.expectEqual([_]u6{ 31, 31, 31, 31 }, b);
17311731}
1732
1733test "runtime comparison to NaN is comptime-known" {
1734 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1735 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1736 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1737 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1738 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1739 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1740 if (builtin.cpu.arch.isArmOrThumb() and builtin.target.floatAbi() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234
1741
1742 const S = struct {
1743 fn doTheTest(comptime F: type, x: F) void {
1744 const nan = math.nan(F);
1745 if (!(nan != x)) comptime unreachable;
1746 if (nan == x) comptime unreachable;
1747 if (nan > x) comptime unreachable;
1748 if (nan < x) comptime unreachable;
1749 if (nan >= x) comptime unreachable;
1750 if (nan <= x) comptime unreachable;
1751 }
1752 };
1753
1754 S.doTheTest(f16, 123.0);
1755 S.doTheTest(f32, 123.0);
1756 S.doTheTest(f64, 123.0);
1757 S.doTheTest(f128, 123.0);
1758 comptime S.doTheTest(f16, 123.0);
1759 comptime S.doTheTest(f32, 123.0);
1760 comptime S.doTheTest(f64, 123.0);
1761 comptime S.doTheTest(f128, 123.0);
1762}
1763
1764test "runtime int comparison to inf is comptime-known" {
1765 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1766 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1767 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1768 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1769 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1770 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1771 if (builtin.cpu.arch.isArmOrThumb() and builtin.target.floatAbi() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234
1772
1773 const S = struct {
1774 fn doTheTest(comptime F: type, x: u32) void {
1775 const inf = math.inf(F);
1776 if (!(inf != x)) comptime unreachable;
1777 if (inf == x) comptime unreachable;
1778 if (x > inf) comptime unreachable;
1779 if (x >= inf) comptime unreachable;
1780 if (!(x < inf)) comptime unreachable;
1781 if (!(x <= inf)) comptime unreachable;
1782 }
1783 };
1784
1785 S.doTheTest(f16, 123);
1786 S.doTheTest(f32, 123);
1787 S.doTheTest(f64, 123);
1788 S.doTheTest(f128, 123);
1789 comptime S.doTheTest(f16, 123);
1790 comptime S.doTheTest(f32, 123);
1791 comptime S.doTheTest(f64, 123);
1792 comptime S.doTheTest(f128, 123);
1793}
test/cases/compile_errors/comparing_against_undefined_produces_undefined_value.zig+6-3
......@@ -1,9 +1,12 @@
1export fn entry() void {
1export fn foo() void {
22 if (2 == undefined) {}
33}
44
5export fn bar(x: u32) void {
6 if (x == undefined) {}
7}
8
59// error
6// backend=stage2
7// target=native
810//
911// :2:11: error: use of undefined value here causes undefined behavior
12// :6:11: error: use of undefined value here causes undefined behavior