authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-15 19:17:06+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-16 17:11:47+01:00
loga5c922179f99591d20e5b6b203c7e292692e0c28
treedece01f22014ee46f41abf68ab104f9486359ba9
parent5d7fa5513f92a43a418e3c5c4d27f0b61db313ff
signaturelock-open Commit is signed but in an unrecognized format.

Sema: return undefined on comparison of runtime value against undefined

Resolves: #10703 Resolves: #17798

2 files changed, 69 insertions(+), 68 deletions(-)

src/Sema.zig+63-65
...@@ -17910,12 +17910,15 @@ fn cmpSelf(...@@ -17910,12 +17910,15 @@ fn cmpSelf(
17910 const pt = sema.pt;17910 const pt = sema.pt;
17911 const zcu = pt.zcu;17911 const zcu = pt.zcu;
17912 const resolved_type = sema.typeOf(casted_lhs);17912 const resolved_type = sema.typeOf(casted_lhs);
17913 const runtime_src: LazySrcLoc = src: {
17914 if (try sema.resolveValue(casted_lhs)) |lhs_val| {
17915 if (lhs_val.isUndef(zcu)) return pt.undefRef(Type.bool);
17916 if (try sema.resolveValue(casted_rhs)) |rhs_val| {
17917 if (rhs_val.isUndef(zcu)) return pt.undefRef(Type.bool);
1791817913
17914 const maybe_lhs_val = try sema.resolveValue(casted_lhs);
17915 const maybe_rhs_val = try sema.resolveValue(casted_rhs);
17916 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(Type.bool);
17917 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(Type.bool);
17918
17919 const runtime_src: LazySrcLoc = src: {
17920 if (maybe_lhs_val) |lhs_val| {
17921 if (maybe_rhs_val) |rhs_val| {
17919 if (resolved_type.zigTypeTag(zcu) == .vector) {17922 if (resolved_type.zigTypeTag(zcu) == .vector) {
17920 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);17923 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
17921 return Air.internedToRef(cmp_val.toIntern());17924 return Air.internedToRef(cmp_val.toIntern());
...@@ -17936,8 +17939,7 @@ fn cmpSelf(...@@ -17936,8 +17939,7 @@ fn cmpSelf(
17936 // For bools, we still check the other operand, because we can lower17939 // For bools, we still check the other operand, because we can lower
17937 // bool eq/neq more efficiently.17940 // bool eq/neq more efficiently.
17938 if (resolved_type.zigTypeTag(zcu) == .bool) {17941 if (resolved_type.zigTypeTag(zcu) == .bool) {
17939 if (try sema.resolveValue(casted_rhs)) |rhs_val| {17942 if (maybe_rhs_val) |rhs_val| {
17940 if (rhs_val.isUndef(zcu)) return pt.undefRef(Type.bool);
17941 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);17943 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
17942 }17944 }
17943 }17945 }
...@@ -33809,51 +33811,47 @@ fn cmpNumeric(...@@ -33809,51 +33811,47 @@ fn cmpNumeric(
33809 else33811 else
33810 uncasted_rhs;33812 uncasted_rhs;
3381133813
33812 const runtime_src: LazySrcLoc = src: {33814 const maybe_lhs_val = try sema.resolveValue(lhs);
33813 if (try sema.resolveValue(lhs)) |lhs_val| {33815 const maybe_rhs_val = try sema.resolveValue(rhs);
33814 if (try sema.resolveValue(rhs)) |rhs_val| {
33815 // Compare ints: const vs. undefined (or vice versa)
33816 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)) {
33817 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
33818 return if (res) .bool_true else .bool_false;
33819 }
33820 } 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)) {
33821 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
33822 return if (res) .bool_true else .bool_false;
33823 }
33824 }
3382533816
33826 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {33817 // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS.
33827 return pt.undefRef(Type.bool);33818 if (maybe_lhs_val) |lhs_val| {
33828 }33819 // Result based on comparison exceeding type bounds
33829 if (lhs_val.isNan(zcu) or rhs_val.isNan(zcu)) {33820 if (!lhs_val.isUndef(zcu) and (lhs_ty.isInt(zcu) or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {
33830 return if (op == std.math.CompareOperator.neq) .bool_true else .bool_false;33821 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
33831 }33822 return if (res) .bool_true else .bool_false;
33832 return if (try Value.compareHeteroSema(lhs_val, op, rhs_val, pt))
33833 .bool_true
33834 else
33835 .bool_false;
33836 } else {
33837 if (!lhs_val.isUndef(zcu) and (lhs_ty.isInt(zcu) or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {
33838 // Compare ints: const vs. var
33839 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
33840 return if (res) .bool_true else .bool_false;
33841 }
33842 }
33843 break :src rhs_src;
33844 }33823 }
33845 } else {33824 }
33846 if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| {33825 // Result based on NaN comparison
33847 if (!rhs_val.isUndef(zcu) and (rhs_ty.isInt(zcu) or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {33826 if (lhs_val.isNan(zcu)) {
33848 // Compare ints: var vs. const33827 return if (op == .neq) .bool_true else .bool_false;
33849 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {33828 }
33850 return if (res) .bool_true else .bool_false;33829 }
33851 }33830
33852 }33831 // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS.
33832 if (maybe_rhs_val) |rhs_val| {
33833 // Result based on comparison exceeding type bounds
33834 if (!rhs_val.isUndef(zcu) and (rhs_ty.isInt(zcu) or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {
33835 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
33836 return if (res) .bool_true else .bool_false;
33853 }33837 }
33854 break :src lhs_src;
33855 }33838 }
33856 };33839 // Result based on NaN comparison
33840 if (rhs_val.isNan(zcu)) {
33841 return if (op == .neq) .bool_true else .bool_false;
33842 }
33843 }
33844
33845 // Any other comparison depends on both values, so the result is undef if either is undef.
33846 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(Type.bool);
33847 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(Type.bool);
33848
33849 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: {
33850 if (maybe_rhs_val) |rhs_val| {
33851 const res = try Value.compareHeteroSema(lhs_val, op, rhs_val, pt);
33852 return if (res) .bool_true else .bool_false;
33853 } else break :rs rhs_src;
33854 } else lhs_src;
3385733855
33858 // TODO handle comparisons against lazy zero values33856 // TODO handle comparisons against lazy zero values
33859 // Some values can be compared against zero without being runtime-known or without forcing33857 // Some values can be compared against zero without being runtime-known or without forcing
...@@ -34161,21 +34159,17 @@ fn cmpVector(...@@ -34161,21 +34159,17 @@ fn cmpVector(
34161 .child = .bool_type,34159 .child = .bool_type,
34162 });34160 });
3416334161
34164 const runtime_src: LazySrcLoc = src: {34162 const maybe_lhs_val = try sema.resolveValue(casted_lhs);
34165 if (try sema.resolveValue(casted_lhs)) |lhs_val| {34163 const maybe_rhs_val = try sema.resolveValue(casted_rhs);
34166 if (try sema.resolveValue(casted_rhs)) |rhs_val| {34164 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
34167 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {34165 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
34168 return pt.undefRef(result_ty);34166
34169 }34167 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: {
34170 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);34168 if (maybe_rhs_val) |rhs_val| {
34171 return Air.internedToRef(cmp_val.toIntern());34169 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
34172 } else {34170 return Air.internedToRef(cmp_val.toIntern());
34173 break :src rhs_src;34171 } else break :src rhs_src;
34174 }34172 } else lhs_src;
34175 } else {
34176 break :src lhs_src;
34177 }
34178 };
3417934173
34180 try sema.requireRuntimeBlock(block, src, runtime_src);34174 try sema.requireRuntimeBlock(block, src, runtime_src);
34181 return block.addCmpVector(casted_lhs, casted_rhs, op);34175 return block.addCmpVector(casted_lhs, casted_rhs, op);
...@@ -38662,8 +38656,12 @@ fn compareVector(...@@ -38662,8 +38656,12 @@ fn compareVector(
38662 for (result_data, 0..) |*scalar, i| {38656 for (result_data, 0..) |*scalar, i| {
38663 const lhs_elem = try lhs.elemValue(pt, i);38657 const lhs_elem = try lhs.elemValue(pt, i);
38664 const rhs_elem = try rhs.elemValue(pt, i);38658 const rhs_elem = try rhs.elemValue(pt, i);
38665 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));38659 if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) {
38666 scalar.* = Value.makeBool(res_bool).toIntern();38660 scalar.* = try pt.intern(.{ .undef = .bool_type });
38661 } else {
38662 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));
38663 scalar.* = Value.makeBool(res_bool).toIntern();
38664 }
38667 }38665 }
38668 return Value.fromInterned(try pt.intern(.{ .aggregate = .{38666 return Value.fromInterned(try pt.intern(.{ .aggregate = .{
38669 .ty = (try pt.vectorType(.{ .len = ty.vectorLen(zcu), .child = .bool_type })).toIntern(),38667 .ty = (try pt.vectorType(.{ .len = ty.vectorLen(zcu), .child = .bool_type })).toIntern(),
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