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(...@@ -17911,12 +17911,15 @@ fn cmpSelf(
17911 const pt = sema.pt;17911 const pt = sema.pt;
17912 const zcu = pt.zcu;17912 const zcu = pt.zcu;
17913 const resolved_type = sema.typeOf(casted_lhs);17913 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| {
17920 if (resolved_type.zigTypeTag(zcu) == .vector) {17923 if (resolved_type.zigTypeTag(zcu) == .vector) {
17921 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);17924 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
17922 return Air.internedToRef(cmp_val.toIntern());17925 return Air.internedToRef(cmp_val.toIntern());
...@@ -17937,8 +17940,7 @@ fn cmpSelf(...@@ -17937,8 +17940,7 @@ fn cmpSelf(
17937 // For bools, we still check the other operand, because we can lower17940 // For bools, we still check the other operand, because we can lower
17938 // bool eq/neq more efficiently.17941 // bool eq/neq more efficiently.
17939 if (resolved_type.zigTypeTag(zcu) == .bool) {17942 if (resolved_type.zigTypeTag(zcu) == .bool) {
17940 if (try sema.resolveValue(casted_rhs)) |rhs_val| {17943 if (maybe_rhs_val) |rhs_val| {
17941 if (rhs_val.isUndef(zcu)) return pt.undefRef(Type.bool);
17942 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);17944 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
17943 }17945 }
17944 }17946 }
...@@ -33837,51 +33839,61 @@ fn cmpNumeric(...@@ -33837,51 +33839,61 @@ fn cmpNumeric(
33837 else33839 else
33838 uncasted_rhs;33840 uncasted_rhs;
3383933841
33840 const runtime_src: LazySrcLoc = src: {33842 const maybe_lhs_val = try sema.resolveValue(lhs);
33841 if (try sema.resolveValue(lhs)) |lhs_val| {33843 const maybe_rhs_val = try sema.resolveValue(rhs);
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 }
3385333844
33854 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {33845 // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value.
33855 return pt.undefRef(Type.bool);33846 if (maybe_lhs_val) |lhs_val| {
33856 }33847 // Result based on comparison exceeding type bounds
33857 if (lhs_val.isNan(zcu) or rhs_val.isNan(zcu)) {33848 if (!lhs_val.isUndef(zcu) and (lhs_ty_tag == .int or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {
33858 return if (op == std.math.CompareOperator.neq) .bool_true else .bool_false;33849 if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| {
33859 }33850 return if (res) .bool_true else .bool_false;
33860 return if (try Value.compareHeteroSema(lhs_val, op, rhs_val, pt))33851 }
33861 .bool_true33852 }
33862 else33853 // Result based on NaN comparison
33863 .bool_false;33854 if (lhs_val.isNan(zcu)) {
33864 } else {33855 return if (op == .neq) .bool_true else .bool_false;
33865 if (!lhs_val.isUndef(zcu) and (lhs_ty.isInt(zcu) or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {33856 }
33866 // Compare ints: const vs. var33857 // Result based on inf comparison to int
33867 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {33858 if (lhs_val.isInf(zcu) and rhs_ty_tag == .int) return switch (op) {
33868 return if (res) .bool_true else .bool_false;33859 .neq => .bool_true,
33869 }33860 .eq => .bool_false,
33870 }33861 .gt, .gte => if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
33871 break :src rhs_src;33862 .lt, .lte => if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
33872 }33863 };
33873 } else {33864 }
33874 if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| {33865
33875 if (!rhs_val.isUndef(zcu) and (rhs_ty.isInt(zcu) or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {33866 // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS value.
33876 // Compare ints: var vs. const33867 if (maybe_rhs_val) |rhs_val| {
33877 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {33868 // Result based on comparison exceeding type bounds
33878 return if (res) .bool_true else .bool_false;33869 if (!rhs_val.isUndef(zcu) and (rhs_ty_tag == .int or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {
33879 }33870 if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| {
33880 }33871 return if (res) .bool_true else .bool_false;
33881 }33872 }
33882 break :src lhs_src;33873 }
33883 }33874 // Result based on NaN comparison
33884 };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
33886 // TODO handle comparisons against lazy zero values33898 // TODO handle comparisons against lazy zero values
33887 // Some values can be compared against zero without being runtime-known or without forcing33899 // Some values can be compared against zero without being runtime-known or without forcing
...@@ -33919,17 +33931,18 @@ fn cmpNumeric(...@@ -33919,17 +33931,18 @@ fn cmpNumeric(
33919 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);33931 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
33920 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);33932 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);
33921 }33933 }
33934
33922 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.33935 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
33923 // For mixed signed and unsigned integers, implicit cast both operands to a signed33936 // For mixed signed and unsigned integers, implicit cast both operands to a signed
33924 // integer with + 1 bit.33937 // integer with + 1 bit.
33925 // For mixed floats and integers, extract the integer part from the float, cast that to33938 // For mixed floats and integers, extract the integer part from the float, cast that to
33926 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,33939 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
33927 // add/subtract 1.33940 // 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|
33929 !(try lhs_val.compareAllWithZeroSema(.gte, pt))33942 !(try lhs_val.compareAllWithZeroSema(.gte, pt))
33930 else33943 else
33931 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu));33944 (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|
33933 !(try rhs_val.compareAllWithZeroSema(.gte, pt))33946 !(try rhs_val.compareAllWithZeroSema(.gte, pt))
33934 else33947 else
33935 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu));33948 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu));
...@@ -33938,19 +33951,8 @@ fn cmpNumeric(...@@ -33938,19 +33951,8 @@ fn cmpNumeric(
33938 var dest_float_type: ?Type = null;33951 var dest_float_type: ?Type = null;
3393933952
33940 var lhs_bits: usize = undefined;33953 var lhs_bits: usize = undefined;
33941 if (try sema.resolveValueResolveLazy(lhs)) |lhs_val| {33954 if (maybe_lhs_val) |unresolved_lhs_val| {
33942 if (lhs_val.isUndef(zcu))33955 const lhs_val = try sema.resolveLazyValue(unresolved_lhs_val);
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 (!rhs_is_signed) {33956 if (!rhs_is_signed) {
33955 switch (lhs_val.orderAgainstZero(zcu)) {33957 switch (lhs_val.orderAgainstZero(zcu)) {
33956 .gt => {},33958 .gt => {},
...@@ -33996,19 +33998,8 @@ fn cmpNumeric(...@@ -33996,19 +33998,8 @@ fn cmpNumeric(
33996 }33998 }
3399733999
33998 var rhs_bits: usize = undefined;34000 var rhs_bits: usize = undefined;
33999 if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| {34001 if (maybe_rhs_val) |unresolved_rhs_val| {
34000 if (rhs_val.isUndef(zcu))34002 const rhs_val = try sema.resolveLazyValue(unresolved_rhs_val);
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 };
34012 if (!lhs_is_signed) {34003 if (!lhs_is_signed) {
34013 switch (rhs_val.orderAgainstZero(zcu)) {34004 switch (rhs_val.orderAgainstZero(zcu)) {
34014 .gt => {},34005 .gt => {},
...@@ -34075,90 +34066,49 @@ fn compareIntsOnlyPossibleResult(...@@ -34075,90 +34066,49 @@ fn compareIntsOnlyPossibleResult(
34075 lhs_val: Value,34066 lhs_val: Value,
34076 op: std.math.CompareOperator,34067 op: std.math.CompareOperator,
34077 rhs_ty: Type,34068 rhs_ty: Type,
34078) Allocator.Error!?bool {34069) SemaError!?bool {
34079 const pt = sema.pt;34070 const pt = sema.pt;
34080 const zcu = pt.zcu;34071 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.34073 const min_rhs = try rhs_ty.minInt(pt, rhs_ty);
34088 if (rhs_info.bits == 0) return switch (op) {34074 const max_rhs = try rhs_ty.maxInt(pt, rhs_ty);
34089 .eq, .lte, .gte => is_zero,
34090 .neq, .lt, .gt => !is_zero,
34091 };
3409234075
34093 // Special case for i1, which can only be 0 or -1.34076 if (min_rhs.toIntern() == max_rhs.toIntern()) {
34094 // Zero and positive ints have guaranteed outcome.34077 // RHS is effectively comptime-known.
34095 if (rhs_info.bits == 1 and rhs_info.signedness == .signed) {34078 return try Value.compareHeteroSema(lhs_val, op, min_rhs, pt);
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 };
34105 }34079 }
3410634080
34107 // Negative vs. unsigned has guaranteed outcome.34081 const against_min = try lhs_val.orderAdvanced(min_rhs, .sema, zcu, pt.tid);
34108 if (rhs_info.signedness == .unsigned and is_negative) return switch (op) {34082 const against_max = try lhs_val.orderAdvanced(max_rhs, .sema, zcu, pt.tid);
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;
3412534083
34126 // Oversized int has guaranteed outcome.
34127 switch (op) {34084 switch (op) {
34128 .eq => return if (!fits) false else null,34085 .eq => {
34129 .neq => return if (!fits) true else null,34086 if (against_min.compare(.lt)) return false;
34130 .lt, .lte => if (!fits) return is_negative,34087 if (against_max.compare(.gt)) return false;
34131 .gt, .gte => if (!fits) return !is_negative,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 },
34132 }34109 }
3413334110
34134 // For any other comparison, we need to know if the LHS value is34111 return null;
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 };
34162}34112}
3416334113
34164/// Asserts that lhs and rhs types are both vectors.34114/// Asserts that lhs and rhs types are both vectors.
...@@ -34189,21 +34139,17 @@ fn cmpVector(...@@ -34189,21 +34139,17 @@ fn cmpVector(
34189 .child = .bool_type,34139 .child = .bool_type,
34190 });34140 });
3419134141
34192 const runtime_src: LazySrcLoc = src: {34142 const maybe_lhs_val = try sema.resolveValue(casted_lhs);
34193 if (try sema.resolveValue(casted_lhs)) |lhs_val| {34143 const maybe_rhs_val = try sema.resolveValue(casted_rhs);
34194 if (try sema.resolveValue(casted_rhs)) |rhs_val| {34144 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
34195 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {34145 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
34196 return pt.undefRef(result_ty);34146
34197 }34147 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: {
34198 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);34148 if (maybe_rhs_val) |rhs_val| {
34199 return Air.internedToRef(cmp_val.toIntern());34149 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
34200 } else {34150 return Air.internedToRef(cmp_val.toIntern());
34201 break :src rhs_src;34151 } else break :src rhs_src;
34202 }34152 } else lhs_src;
34203 } else {
34204 break :src lhs_src;
34205 }
34206 };
3420734153
34208 try sema.requireRuntimeBlock(block, src, runtime_src);34154 try sema.requireRuntimeBlock(block, src, runtime_src);
34209 return block.addCmpVector(casted_lhs, casted_rhs, op);34155 return block.addCmpVector(casted_lhs, casted_rhs, op);
...@@ -38691,8 +38637,12 @@ fn compareVector(...@@ -38691,8 +38637,12 @@ fn compareVector(
38691 for (result_data, 0..) |*scalar, i| {38637 for (result_data, 0..) |*scalar, i| {
38692 const lhs_elem = try lhs.elemValue(pt, i);38638 const lhs_elem = try lhs.elemValue(pt, i);
38693 const rhs_elem = try rhs.elemValue(pt, i);38639 const rhs_elem = try rhs.elemValue(pt, i);
38694 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));38640 if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) {
38695 scalar.* = Value.makeBool(res_bool).toIntern();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 }
38696 }38646 }
38697 return Value.fromInterned(try pt.intern(.{ .aggregate = .{38647 return Value.fromInterned(try pt.intern(.{ .aggregate = .{
38698 .ty = (try pt.vectorType(.{ .len = ty.vectorLen(zcu), .child = .bool_type })).toIntern(),38648 .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 {...@@ -3040,8 +3040,7 @@ pub fn minInt(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
3040pub fn minIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {3040pub fn minIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
3041 const zcu = pt.zcu;3041 const zcu = pt.zcu;
3042 const info = ty.intInfo(zcu);3042 const info = ty.intInfo(zcu);
3043 if (info.signedness == .unsigned) return pt.intValue(dest_ty, 0);3043 if (info.signedness == .unsigned or info.bits == 0) return pt.intValue(dest_ty, 0);
3044 if (info.bits == 0) return pt.intValue(dest_ty, -1);
30453044
3046 if (std.math.cast(u6, info.bits - 1)) |shift| {3045 if (std.math.cast(u6, info.bits - 1)) |shift| {
3047 const n = @as(i64, std.math.minInt(i64)) >> (63 - shift);3046 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 {...@@ -3072,10 +3071,7 @@ pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
3072 const info = ty.intInfo(pt.zcu);3071 const info = ty.intInfo(pt.zcu);
30733072
3074 switch (info.bits) {3073 switch (info.bits) {
3075 0 => return switch (info.signedness) {3074 0 => return pt.intValue(dest_ty, 0),
3076 .signed => try pt.intValue(dest_ty, -1),
3077 .unsigned => try pt.intValue(dest_ty, 0),
3078 },
3079 1 => return switch (info.signedness) {3075 1 => return switch (info.signedness) {
3080 .signed => try pt.intValue(dest_ty, 0),3076 .signed => try pt.intValue(dest_ty, 0),
3081 .unsigned => try pt.intValue(dest_ty, 1),3077 .unsigned => try pt.intValue(dest_ty, 1),
src/Value.zig+2-2
...@@ -191,7 +191,7 @@ pub fn toBigIntAdvanced(...@@ -191,7 +191,7 @@ pub fn toBigIntAdvanced(
191 comptime strat: ResolveStrat,191 comptime strat: ResolveStrat,
192 zcu: *Zcu,192 zcu: *Zcu,
193 tid: strat.Tid(),193 tid: strat.Tid(),
194) Zcu.CompileError!BigIntConst {194) Zcu.SemaError!BigIntConst {
195 const ip = &zcu.intern_pool;195 const ip = &zcu.intern_pool;
196 return switch (val.toIntern()) {196 return switch (val.toIntern()) {
197 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),197 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
...@@ -1038,7 +1038,7 @@ pub fn orderAgainstZeroInner(...@@ -1038,7 +1038,7 @@ pub fn orderAgainstZeroInner(
1038 comptime strat: ResolveStrat,1038 comptime strat: ResolveStrat,
1039 zcu: *Zcu,1039 zcu: *Zcu,
1040 tid: strat.Tid(),1040 tid: strat.Tid(),
1041) Zcu.CompileError!std.math.Order {1041) Zcu.SemaError!std.math.Order {
1042 return switch (lhs.toIntern()) {1042 return switch (lhs.toIntern()) {
1043 .bool_false => .eq,1043 .bool_false => .eq,
1044 .bool_true => .gt,1044 .bool_true => .gt,
test/behavior/math.zig+62
...@@ -1729,3 +1729,65 @@ test "@clz works on both vector and scalar inputs" {...@@ -1729,3 +1729,65 @@ test "@clz works on both vector and scalar inputs" {
1729 try std.testing.expectEqual(@as(u6, 31), a);1729 try std.testing.expectEqual(@as(u6, 31), a);
1730 try std.testing.expectEqual([_]u6{ 31, 31, 31, 31 }, b);1730 try std.testing.expectEqual([_]u6{ 31, 31, 31, 31 }, b);
1731}1731}
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 @@...@@ -1,9 +1,12 @@
1export fn entry() void {1export fn foo() void {
2 if (2 == undefined) {}2 if (2 == undefined) {}
3}3}
44
5export fn bar(x: u32) void {
6 if (x == undefined) {}
7}
8
5// error9// error
6// backend=stage2
7// target=native
8//10//
9// :2:11: error: use of undefined value here causes undefined behavior11// :2:11: error: use of undefined value here causes undefined behavior
12// :6:11: error: use of undefined value here causes undefined behavior