diff --git a/src/Sema.zig b/src/Sema.zig index 2a30247108e4..aa624e06e721 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16140,6 +16140,10 @@ fn analyzeArithmetic( return casted_lhs; } if (maybe_lhs_val) |lhs_val| { + if (lhs_val.isUndef(mod)) { + return mod.undefRef(resolved_type); + } + const val = if (scalar_tag == .ComptimeInt) try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined) else @@ -16202,7 +16206,7 @@ fn analyzeArithmetic( }, .subwrap => { // Integers only; floats are checked above. - // If the RHS is zero, then the other operand is returned, even if it is undefined. + // If the RHS is zero, then the LHS is returned, even if it is undefined. // If either of the operands are undefined, the result is undefined. if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef(mod)) { @@ -16223,8 +16227,8 @@ fn analyzeArithmetic( }, .sub_sat => { // Integers only; floats are checked above. - // If the RHS is zero, result is LHS. - // If either of the operands are undefined, result is undefined. + // If the RHS is zero, then the LHS is returned, even if it is undefined. + // If either of the operands are undefined, the result is undefined. if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef(mod)) { return mod.undefRef(resolved_type); @@ -16255,7 +16259,9 @@ fn analyzeArithmetic( // If either of the operands are undefined, it's a compile error // because there is a possible value for which the addition would // overflow (max_int), causing illegal behavior. - // For floats: either operand being undef makes the result undef. + // + // For floats: + // If either of the operands are undefined, the result is undefined. // If either of the operands are inf, and the other operand is zero, // the result is nan. // If either of the operands are nan, the result is nan. @@ -38093,7 +38099,7 @@ fn numberAddWrapScalar( ty: Type, ) !Value { const mod = sema.mod; - if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; + if (lhs.isUndef(mod) or rhs.isUndef(mod)) return mod.undefValue(ty); if (ty.zigTypeTag(mod) == .ComptimeInt) { return sema.intAdd(lhs, rhs, ty, undefined); @@ -38183,7 +38189,7 @@ fn numberSubWrapScalar( ty: Type, ) !Value { const mod = sema.mod; - if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; + if (lhs.isUndef(mod) or rhs.isUndef(mod)) return mod.undefValue(ty); if (ty.zigTypeTag(mod) == .ComptimeInt) { return sema.intSub(lhs, rhs, ty, undefined); diff --git a/test/cases/compile_errors/add_on_undefined_value.zig b/test/cases/compile_errors/add_on_undefined_value.zig index b59de233c57c..eb081a851095 100644 --- a/test/cases/compile_errors/add_on_undefined_value.zig +++ b/test/cases/compile_errors/add_on_undefined_value.zig @@ -1,10 +1,24 @@ comptime { - const a: i64 = undefined; - _ = a + a; + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If either of the operands are zero, then the other operand is returned. + @compileLog(undef + 0); + @compileLog(not_undef + 0); + @compileLog(0 + undef); + @compileLog(0 + not_undef); + + _ = undef + undef; } // error // backend=stage2 // target=native // -// :3:13: error: use of undefined value here causes undefined behavior +// :11:17: error: use of undefined value here causes undefined behavior +// +// Compile Log Output: +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, 32) diff --git a/test/cases/compile_errors/add_sat_on_undefined_value.zig b/test/cases/compile_errors/add_sat_on_undefined_value.zig new file mode 100644 index 000000000000..e23a985d1a59 --- /dev/null +++ b/test/cases/compile_errors/add_sat_on_undefined_value.zig @@ -0,0 +1,31 @@ +comptime { + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If either of the operands are zero, then the other operand is returned. + @compileLog(undef +| 0); + @compileLog(not_undef +| 0); + @compileLog(0 +| undef); + @compileLog(0 +| not_undef); + // If either of the operands are undefined, the result is undefined. + @compileLog(undef +| 1); + @compileLog(not_undef +| 1); + @compileLog(1 +| undef); + @compileLog(1 +| not_undef); +} + +// error +// backend=stage2 +// target=native +// +// :6:5: error: found compile log statement +// +// Compile Log Output: +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, 33) +// @as(i64, undefined) +// @as(i64, 33) diff --git a/test/cases/compile_errors/add_wrap_on_undefined_value.zig b/test/cases/compile_errors/add_wrap_on_undefined_value.zig new file mode 100644 index 000000000000..b957485735a3 --- /dev/null +++ b/test/cases/compile_errors/add_wrap_on_undefined_value.zig @@ -0,0 +1,31 @@ +comptime { + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If either of the operands are zero, then the other operand is returned. + @compileLog(undef +% 0); + @compileLog(not_undef +% 0); + @compileLog(0 +% undef); + @compileLog(0 +% not_undef); + // If either of the operands are undefined, the result is undefined. + @compileLog(undef +% 1); + @compileLog(not_undef +% 1); + @compileLog(1 +% undef); + @compileLog(1 +% not_undef); +} + +// error +// backend=stage2 +// target=native +// +// :6:5: error: found compile log statement +// +// Compile Log Output: +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, 33) +// @as(i64, undefined) +// @as(i64, 33) diff --git a/test/cases/compile_errors/mult_on_undefined_value.zig b/test/cases/compile_errors/mult_on_undefined_value.zig index 1e6579715352..3516253d5c94 100644 --- a/test/cases/compile_errors/mult_on_undefined_value.zig +++ b/test/cases/compile_errors/mult_on_undefined_value.zig @@ -1,10 +1,38 @@ comptime { - const a: i64 = undefined; - _ = a * a; + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If either of the operands are zero, the result is zero. + @compileLog(undef * 0); + @compileLog(not_undef * 0); + @compileLog(0 * undef); + @compileLog(0 * not_undef); + + // If either of the operands are one, the result is the other + // operand, even if it is undefined. + @compileLog(undef * 1); + @compileLog(not_undef * 1); + @compileLog(1 * undef); + @compileLog(1 * not_undef); + + // If either of the operands are undefined, it's a compile error + // because there is a possible value for which the addition would + // overflow (max_int), causing illegal behavior. + _ = undef * undef; } // error // backend=stage2 // target=native // -// :3:13: error: use of undefined value here causes undefined behavior +// :21:17: error: use of undefined value here causes undefined behavior +// +// Compile Log Output: +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, 32) diff --git a/test/cases/compile_errors/mult_sat_on_undefined_value.zig b/test/cases/compile_errors/mult_sat_on_undefined_value.zig new file mode 100644 index 000000000000..987161c8ed48 --- /dev/null +++ b/test/cases/compile_errors/mult_sat_on_undefined_value.zig @@ -0,0 +1,38 @@ +comptime { + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If either of the operands are zero, the result is zero. + @compileLog(undef *| 0); + @compileLog(not_undef *| 0); + @compileLog(0 *| undef); + @compileLog(0 *| not_undef); + + // If either of the operands are one, result is the other operand. + @compileLog(undef *| 1); + @compileLog(not_undef *| 1); + @compileLog(1 *| undef); + @compileLog(1 *| not_undef); + + // If either of the operands are undefined, result is undefined. + @compileLog(undef *| 2); + @compileLog(2 *| undef); +} + +// error +// backend=stage2 +// target=native +// +// :6:5: error: found compile log statement +// +// Compile Log Output: +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, undefined) diff --git a/test/cases/compile_errors/mult_wrap_on_undefined_value.zig b/test/cases/compile_errors/mult_wrap_on_undefined_value.zig new file mode 100644 index 000000000000..2ecb6753f673 --- /dev/null +++ b/test/cases/compile_errors/mult_wrap_on_undefined_value.zig @@ -0,0 +1,38 @@ +comptime { + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If either of the operands are zero, the result is zero. + @compileLog(undef *% 0); + @compileLog(not_undef *% 0); + @compileLog(0 *% undef); + @compileLog(0 *% not_undef); + + // If either of the operands are one, result is the other operand. + @compileLog(undef *% 1); + @compileLog(not_undef *% 1); + @compileLog(1 *% undef); + @compileLog(1 *% not_undef); + + // If either of the operands are undefined, result is undefined. + @compileLog(undef *% 2); + @compileLog(2 *% undef); +} + +// error +// backend=stage2 +// target=native +// +// :6:5: error: found compile log statement +// +// Compile Log Output: +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, 0) +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, undefined) diff --git a/test/cases/compile_errors/sub_on_undefined_value.zig b/test/cases/compile_errors/sub_on_undefined_value.zig index de305be576c5..5d2510f26324 100644 --- a/test/cases/compile_errors/sub_on_undefined_value.zig +++ b/test/cases/compile_errors/sub_on_undefined_value.zig @@ -1,10 +1,20 @@ comptime { - const a: i64 = undefined; - _ = a - a; + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If the rhs is zero, then the other operand is returned, even if it is undefined. + @compileLog(undef - 0); + @compileLog(not_undef - 0); + + _ = undef - undef; } // error // backend=stage2 // target=native // -// :3:13: error: use of undefined value here causes undefined behavior +// :9:17: error: use of undefined value here causes undefined behavior +// +// Compile Log Output: +// @as(i64, undefined) +// @as(i64, 32) diff --git a/test/cases/compile_errors/sub_sat_on_undefined_value.zig b/test/cases/compile_errors/sub_sat_on_undefined_value.zig new file mode 100644 index 000000000000..967f24130e08 --- /dev/null +++ b/test/cases/compile_errors/sub_sat_on_undefined_value.zig @@ -0,0 +1,25 @@ +comptime { + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If the RHS is zero, then the LHS is returned, even if it is undefined. + @compileLog(undef -| 0); + @compileLog(not_undef -| 0); + // If either of the operands are undefined, the result is undefined. + @compileLog(undef -| not_undef); + @compileLog(not_undef -| undef); + @compileLog(undef -| undef); +} + +// error +// backend=stage2 +// target=native +// +// :6:5: error: found compile log statement +// +// Compile Log Output: +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, undefined) +// @as(i64, undefined) diff --git a/test/cases/compile_errors/sub_wrap_on_undefined_value.zig b/test/cases/compile_errors/sub_wrap_on_undefined_value.zig new file mode 100644 index 000000000000..12dfeff4e5ef --- /dev/null +++ b/test/cases/compile_errors/sub_wrap_on_undefined_value.zig @@ -0,0 +1,25 @@ +comptime { + const undef: i64 = undefined; + const not_undef: i64 = 32; + + // If the RHS is zero, then the LHS is returned, even if it is undefined. + @compileLog(undef -% 0); + @compileLog(not_undef -% 0); + // If either of the operands are undefined, the result is undefined. + @compileLog(undef -% not_undef); + @compileLog(not_undef -% undef); + @compileLog(undef -% undef); +} + +// error +// backend=stage2 +// target=native +// +// :6:5: error: found compile log statement +// +// Compile Log Output: +// @as(i64, undefined) +// @as(i64, 32) +// @as(i64, undefined) +// @as(i64, undefined) +// @as(i64, undefined)