Skip to content

Commit 0b7e674

Browse files
committed
Sema: return undefined on comparison of runtime value against undefined
Resolves: #10703 Resolves: #17798
1 parent 5d7fa55 commit 0b7e674

File tree

2 files changed

+69
-68
lines changed

2 files changed

+69
-68
lines changed

src/Sema.zig

Lines changed: 63 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -17910,12 +17910,15 @@ fn cmpSelf(
1791017910
const pt = sema.pt;
1791117911
const zcu = pt.zcu;
1791217912
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| {
1791917922
if (resolved_type.zigTypeTag(zcu) == .vector) {
1792017923
const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
1792117924
return Air.internedToRef(cmp_val.toIntern());
@@ -17936,8 +17939,7 @@ fn cmpSelf(
1793617939
// For bools, we still check the other operand, because we can lower
1793717940
// bool eq/neq more efficiently.
1793817941
if (resolved_type.zigTypeTag(zcu) == .bool) {
17939-
if (try sema.resolveValue(casted_rhs)) |rhs_val| {
17940-
if (rhs_val.isUndef(zcu)) return pt.undefRef(Type.bool);
17942+
if (maybe_rhs_val) |rhs_val| {
1794117943
return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
1794217944
}
1794317945
}
@@ -33809,51 +33811,47 @@ fn cmpNumeric(
3380933811
else
3381033812
uncasted_rhs;
3381133813

33812-
const runtime_src: LazySrcLoc = src: {
33813-
if (try sema.resolveValue(lhs)) |lhs_val| {
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-
}
33814+
const maybe_lhs_val = try sema.resolveValue(lhs);
33815+
const maybe_rhs_val = try sema.resolveValue(rhs);
3382533816

33826-
if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
33827-
return pt.undefRef(Type.bool);
33828-
}
33829-
if (lhs_val.isNan(zcu) or rhs_val.isNan(zcu)) {
33830-
return if (op == std.math.CompareOperator.neq) .bool_true else .bool_false;
33831-
}
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;
33817+
// If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS.
33818+
if (maybe_lhs_val) |lhs_val| {
33819+
// Result based on comparison exceeding type bounds
33820+
if (!lhs_val.isUndef(zcu) and lhs_ty.isInt(zcu) and (rhs_ty.isInt(zcu) or rhs_ty_tag == .comptime_int)) {
33821+
if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| {
33822+
return if (res) .bool_true else .bool_false;
3384433823
}
33845-
} else {
33846-
if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| {
33847-
if (!rhs_val.isUndef(zcu) and (rhs_ty.isInt(zcu) or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {
33848-
// Compare ints: var vs. const
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-
}
33824+
}
33825+
// Result based on NaN comparison
33826+
if (lhs_val.isNan(zcu)) {
33827+
return if (op == .neq) .bool_true else .bool_false;
33828+
}
33829+
}
33830+
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) and (lhs_ty.isInt(zcu) or lhs_ty_tag == .comptime_int)) {
33835+
if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| {
33836+
return if (res) .bool_true else .bool_false;
3385333837
}
33854-
break :src lhs_src;
3385533838
}
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

3385833856
// TODO handle comparisons against lazy zero values
3385933857
// Some values can be compared against zero without being runtime-known or without forcing
@@ -34161,21 +34159,17 @@ fn cmpVector(
3416134159
.child = .bool_type,
3416234160
});
3416334161

34164-
const runtime_src: LazySrcLoc = src: {
34165-
if (try sema.resolveValue(casted_lhs)) |lhs_val| {
34166-
if (try sema.resolveValue(casted_rhs)) |rhs_val| {
34167-
if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
34168-
return pt.undefRef(result_ty);
34169-
}
34170-
const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
34171-
return Air.internedToRef(cmp_val.toIntern());
34172-
} else {
34173-
break :src rhs_src;
34174-
}
34175-
} else {
34176-
break :src lhs_src;
34177-
}
34178-
};
34162+
const maybe_lhs_val = try sema.resolveValue(casted_lhs);
34163+
const maybe_rhs_val = try sema.resolveValue(casted_rhs);
34164+
if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
34165+
if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
34166+
34167+
const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: {
34168+
if (maybe_rhs_val) |rhs_val| {
34169+
const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
34170+
return Air.internedToRef(cmp_val.toIntern());
34171+
} else break :src rhs_src;
34172+
} else lhs_src;
3417934173

3418034174
try sema.requireRuntimeBlock(block, src, runtime_src);
3418134175
return block.addCmpVector(casted_lhs, casted_rhs, op);
@@ -38662,8 +38656,12 @@ fn compareVector(
3866238656
for (result_data, 0..) |*scalar, i| {
3866338657
const lhs_elem = try lhs.elemValue(pt, i);
3866438658
const rhs_elem = try rhs.elemValue(pt, i);
38665-
const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));
38666-
scalar.* = Value.makeBool(res_bool).toIntern();
38659+
if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) {
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+
}
3866738665
}
3866838666
return Value.fromInterned(try pt.intern(.{ .aggregate = .{
3866938667
.ty = (try pt.vectorType(.{ .len = ty.vectorLen(zcu), .child = .bool_type })).toIntern(),
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
export fn entry() void {
1+
export fn foo() void {
22
if (2 == undefined) {}
33
}
44

5+
export 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

0 commit comments

Comments
 (0)