| author | |
| committer | |
| log | ec358d6db5a10989590e11bbbbd3bed9f81cf1f4 |
| tree | bd5affb8ff6bbbb4ea8ac22554c0a782d73bb261 |
| parent | 4ace1f5a7ffeb569e97dc78807a1eacc5565a272 |
Previously `@as(i64, undefined) +% 1` would produce `@as(@TypeOf(undefined), undefined)` which now gives `@as(i64, undefined)`.
Previously `@as(i64, undefined) +| 1` would hit an assertion which now gives `@as(i64, undefined)`.10 files changed, 261 insertions(+), 15 deletions(-)
src/Sema.zig+12-6| ... | @@ -16140,6 +16140,10 @@ fn analyzeArithmetic( | ... | @@ -16140,6 +16140,10 @@ fn analyzeArithmetic( |
| 16140 | return casted_lhs; | 16140 | return casted_lhs; |
| 16141 | } | 16141 | } |
| 16142 | if (maybe_lhs_val) |lhs_val| { | 16142 | if (maybe_lhs_val) |lhs_val| { |
| 16143 | if (lhs_val.isUndef(mod)) { | ||
| 16144 | return mod.undefRef(resolved_type); | ||
| 16145 | } | ||
| 16146 | |||
| 16143 | const val = if (scalar_tag == .ComptimeInt) | 16147 | const val = if (scalar_tag == .ComptimeInt) |
| 16144 | try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined) | 16148 | try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined) |
| 16145 | else | 16149 | else |
| ... | @@ -16202,7 +16206,7 @@ fn analyzeArithmetic( | ... | @@ -16202,7 +16206,7 @@ fn analyzeArithmetic( |
| 16202 | }, | 16206 | }, |
| 16203 | .subwrap => { | 16207 | .subwrap => { |
| 16204 | // Integers only; floats are checked above. | 16208 | // Integers only; floats are checked above. |
| 16205 | // If the RHS is zero, then the other operand is returned, even if it is undefined. | 16209 | // If the RHS is zero, then the LHS is returned, even if it is undefined. |
| 16206 | // If either of the operands are undefined, the result is undefined. | 16210 | // If either of the operands are undefined, the result is undefined. |
| 16207 | if (maybe_rhs_val) |rhs_val| { | 16211 | if (maybe_rhs_val) |rhs_val| { |
| 16208 | if (rhs_val.isUndef(mod)) { | 16212 | if (rhs_val.isUndef(mod)) { |
| ... | @@ -16223,8 +16227,8 @@ fn analyzeArithmetic( | ... | @@ -16223,8 +16227,8 @@ fn analyzeArithmetic( |
| 16223 | }, | 16227 | }, |
| 16224 | .sub_sat => { | 16228 | .sub_sat => { |
| 16225 | // Integers only; floats are checked above. | 16229 | // Integers only; floats are checked above. |
| 16226 | // If the RHS is zero, result is LHS. | 16230 | // If the RHS is zero, then the LHS is returned, even if it is undefined. |
| 16227 | // If either of the operands are undefined, result is undefined. | 16231 | // If either of the operands are undefined, the result is undefined. |
| 16228 | if (maybe_rhs_val) |rhs_val| { | 16232 | if (maybe_rhs_val) |rhs_val| { |
| 16229 | if (rhs_val.isUndef(mod)) { | 16233 | if (rhs_val.isUndef(mod)) { |
| 16230 | return mod.undefRef(resolved_type); | 16234 | return mod.undefRef(resolved_type); |
| ... | @@ -16255,7 +16259,9 @@ fn analyzeArithmetic( | ... | @@ -16255,7 +16259,9 @@ fn analyzeArithmetic( |
| 16255 | // If either of the operands are undefined, it's a compile error | 16259 | // If either of the operands are undefined, it's a compile error |
| 16256 | // because there is a possible value for which the addition would | 16260 | // because there is a possible value for which the addition would |
| 16257 | // overflow (max_int), causing illegal behavior. | 16261 | // overflow (max_int), causing illegal behavior. |
| 16258 | // For floats: either operand being undef makes the result undef. | 16262 | // |
| 16263 | // For floats: | ||
| 16264 | // If either of the operands are undefined, the result is undefined. | ||
| 16259 | // If either of the operands are inf, and the other operand is zero, | 16265 | // If either of the operands are inf, and the other operand is zero, |
| 16260 | // the result is nan. | 16266 | // the result is nan. |
| 16261 | // If either of the operands are nan, the result is nan. | 16267 | // If either of the operands are nan, the result is nan. |
| ... | @@ -38093,7 +38099,7 @@ fn numberAddWrapScalar( | ... | @@ -38093,7 +38099,7 @@ fn numberAddWrapScalar( |
| 38093 | ty: Type, | 38099 | ty: Type, |
| 38094 | ) !Value { | 38100 | ) !Value { |
| 38095 | const mod = sema.mod; | 38101 | const mod = sema.mod; |
| 38096 | if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; | 38102 | if (lhs.isUndef(mod) or rhs.isUndef(mod)) return mod.undefValue(ty); |
| 38097 | 38103 | ||
| 38098 | if (ty.zigTypeTag(mod) == .ComptimeInt) { | 38104 | if (ty.zigTypeTag(mod) == .ComptimeInt) { |
| 38099 | return sema.intAdd(lhs, rhs, ty, undefined); | 38105 | return sema.intAdd(lhs, rhs, ty, undefined); |
| ... | @@ -38183,7 +38189,7 @@ fn numberSubWrapScalar( | ... | @@ -38183,7 +38189,7 @@ fn numberSubWrapScalar( |
| 38183 | ty: Type, | 38189 | ty: Type, |
| 38184 | ) !Value { | 38190 | ) !Value { |
| 38185 | const mod = sema.mod; | 38191 | const mod = sema.mod; |
| 38186 | if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; | 38192 | if (lhs.isUndef(mod) or rhs.isUndef(mod)) return mod.undefValue(ty); |
| 38187 | 38193 | ||
| 38188 | if (ty.zigTypeTag(mod) == .ComptimeInt) { | 38194 | if (ty.zigTypeTag(mod) == .ComptimeInt) { |
| 38189 | return sema.intSub(lhs, rhs, ty, undefined); | 38195 | return sema.intSub(lhs, rhs, ty, undefined); |
test/cases/compile_errors/add_on_undefined_value.zig+17-3| ... | @@ -1,10 +1,24 @@ | ... | @@ -1,10 +1,24 @@ |
| 1 | comptime { | 1 | comptime { |
| 2 | const a: i64 = undefined; | 2 | const undef: i64 = undefined; |
| 3 | _ = a + a; | 3 | const not_undef: i64 = 32; |
| 4 | |||
| 5 | // If either of the operands are zero, then the other operand is returned. | ||
| 6 | @compileLog(undef + 0); | ||
| 7 | @compileLog(not_undef + 0); | ||
| 8 | @compileLog(0 + undef); | ||
| 9 | @compileLog(0 + not_undef); | ||
| 10 | |||
| 11 | _ = undef + undef; | ||
| 4 | } | 12 | } |
| 5 | 13 | ||
| 6 | // error | 14 | // error |
| 7 | // backend=stage2 | 15 | // backend=stage2 |
| 8 | // target=native | 16 | // target=native |
| 9 | // | 17 | // |
| 10 | // :3:13: error: use of undefined value here causes undefined behavior | 18 | // :11:17: error: use of undefined value here causes undefined behavior |
| 19 | // | ||
| 20 | // Compile Log Output: | ||
| 21 | // @as(i64, undefined) | ||
| 22 | // @as(i64, 32) | ||
| 23 | // @as(i64, undefined) | ||
| 24 | // @as(i64, 32) |
test/cases/compile_errors/add_sat_on_undefined_value.zig created+31| ... | @@ -0,0 +1,31 @@ | ||
| 1 | comptime { | ||
| 2 | const undef: i64 = undefined; | ||
| 3 | const not_undef: i64 = 32; | ||
| 4 | |||
| 5 | // If either of the operands are zero, then the other operand is returned. | ||
| 6 | @compileLog(undef +| 0); | ||
| 7 | @compileLog(not_undef +| 0); | ||
| 8 | @compileLog(0 +| undef); | ||
| 9 | @compileLog(0 +| not_undef); | ||
| 10 | // If either of the operands are undefined, the result is undefined. | ||
| 11 | @compileLog(undef +| 1); | ||
| 12 | @compileLog(not_undef +| 1); | ||
| 13 | @compileLog(1 +| undef); | ||
| 14 | @compileLog(1 +| not_undef); | ||
| 15 | } | ||
| 16 | |||
| 17 | // error | ||
| 18 | // backend=stage2 | ||
| 19 | // target=native | ||
| 20 | // | ||
| 21 | // :6:5: error: found compile log statement | ||
| 22 | // | ||
| 23 | // Compile Log Output: | ||
| 24 | // @as(i64, undefined) | ||
| 25 | // @as(i64, 32) | ||
| 26 | // @as(i64, undefined) | ||
| 27 | // @as(i64, 32) | ||
| 28 | // @as(i64, undefined) | ||
| 29 | // @as(i64, 33) | ||
| 30 | // @as(i64, undefined) | ||
| 31 | // @as(i64, 33) | ||
test/cases/compile_errors/add_wrap_on_undefined_value.zig created+31| ... | @@ -0,0 +1,31 @@ | ||
| 1 | comptime { | ||
| 2 | const undef: i64 = undefined; | ||
| 3 | const not_undef: i64 = 32; | ||
| 4 | |||
| 5 | // If either of the operands are zero, then the other operand is returned. | ||
| 6 | @compileLog(undef +% 0); | ||
| 7 | @compileLog(not_undef +% 0); | ||
| 8 | @compileLog(0 +% undef); | ||
| 9 | @compileLog(0 +% not_undef); | ||
| 10 | // If either of the operands are undefined, the result is undefined. | ||
| 11 | @compileLog(undef +% 1); | ||
| 12 | @compileLog(not_undef +% 1); | ||
| 13 | @compileLog(1 +% undef); | ||
| 14 | @compileLog(1 +% not_undef); | ||
| 15 | } | ||
| 16 | |||
| 17 | // error | ||
| 18 | // backend=stage2 | ||
| 19 | // target=native | ||
| 20 | // | ||
| 21 | // :6:5: error: found compile log statement | ||
| 22 | // | ||
| 23 | // Compile Log Output: | ||
| 24 | // @as(i64, undefined) | ||
| 25 | // @as(i64, 32) | ||
| 26 | // @as(i64, undefined) | ||
| 27 | // @as(i64, 32) | ||
| 28 | // @as(i64, undefined) | ||
| 29 | // @as(i64, 33) | ||
| 30 | // @as(i64, undefined) | ||
| 31 | // @as(i64, 33) | ||
test/cases/compile_errors/mult_on_undefined_value.zig+31-3| ... | @@ -1,10 +1,38 @@ | ... | @@ -1,10 +1,38 @@ |
| 1 | comptime { | 1 | comptime { |
| 2 | const a: i64 = undefined; | 2 | const undef: i64 = undefined; |
| 3 | _ = a * a; | 3 | const not_undef: i64 = 32; |
| 4 | |||
| 5 | // If either of the operands are zero, the result is zero. | ||
| 6 | @compileLog(undef * 0); | ||
| 7 | @compileLog(not_undef * 0); | ||
| 8 | @compileLog(0 * undef); | ||
| 9 | @compileLog(0 * not_undef); | ||
| 10 | |||
| 11 | // If either of the operands are one, the result is the other | ||
| 12 | // operand, even if it is undefined. | ||
| 13 | @compileLog(undef * 1); | ||
| 14 | @compileLog(not_undef * 1); | ||
| 15 | @compileLog(1 * undef); | ||
| 16 | @compileLog(1 * not_undef); | ||
| 17 | |||
| 18 | // If either of the operands are undefined, it's a compile error | ||
| 19 | // because there is a possible value for which the addition would | ||
| 20 | // overflow (max_int), causing illegal behavior. | ||
| 21 | _ = undef * undef; | ||
| 4 | } | 22 | } |
| 5 | 23 | ||
| 6 | // error | 24 | // error |
| 7 | // backend=stage2 | 25 | // backend=stage2 |
| 8 | // target=native | 26 | // target=native |
| 9 | // | 27 | // |
| 10 | // :3:13: error: use of undefined value here causes undefined behavior | 28 | // :21:17: error: use of undefined value here causes undefined behavior |
| 29 | // | ||
| 30 | // Compile Log Output: | ||
| 31 | // @as(i64, 0) | ||
| 32 | // @as(i64, 0) | ||
| 33 | // @as(i64, 0) | ||
| 34 | // @as(i64, 0) | ||
| 35 | // @as(i64, undefined) | ||
| 36 | // @as(i64, 32) | ||
| 37 | // @as(i64, undefined) | ||
| 38 | // @as(i64, 32) |
test/cases/compile_errors/mult_sat_on_undefined_value.zig created+38| ... | @@ -0,0 +1,38 @@ | ||
| 1 | comptime { | ||
| 2 | const undef: i64 = undefined; | ||
| 3 | const not_undef: i64 = 32; | ||
| 4 | |||
| 5 | // If either of the operands are zero, the result is zero. | ||
| 6 | @compileLog(undef *| 0); | ||
| 7 | @compileLog(not_undef *| 0); | ||
| 8 | @compileLog(0 *| undef); | ||
| 9 | @compileLog(0 *| not_undef); | ||
| 10 | |||
| 11 | // If either of the operands are one, result is the other operand. | ||
| 12 | @compileLog(undef *| 1); | ||
| 13 | @compileLog(not_undef *| 1); | ||
| 14 | @compileLog(1 *| undef); | ||
| 15 | @compileLog(1 *| not_undef); | ||
| 16 | |||
| 17 | // If either of the operands are undefined, result is undefined. | ||
| 18 | @compileLog(undef *| 2); | ||
| 19 | @compileLog(2 *| undef); | ||
| 20 | } | ||
| 21 | |||
| 22 | // error | ||
| 23 | // backend=stage2 | ||
| 24 | // target=native | ||
| 25 | // | ||
| 26 | // :6:5: error: found compile log statement | ||
| 27 | // | ||
| 28 | // Compile Log Output: | ||
| 29 | // @as(i64, 0) | ||
| 30 | // @as(i64, 0) | ||
| 31 | // @as(i64, 0) | ||
| 32 | // @as(i64, 0) | ||
| 33 | // @as(i64, undefined) | ||
| 34 | // @as(i64, 32) | ||
| 35 | // @as(i64, undefined) | ||
| 36 | // @as(i64, 32) | ||
| 37 | // @as(i64, undefined) | ||
| 38 | // @as(i64, undefined) | ||
test/cases/compile_errors/mult_wrap_on_undefined_value.zig created+38| ... | @@ -0,0 +1,38 @@ | ||
| 1 | comptime { | ||
| 2 | const undef: i64 = undefined; | ||
| 3 | const not_undef: i64 = 32; | ||
| 4 | |||
| 5 | // If either of the operands are zero, the result is zero. | ||
| 6 | @compileLog(undef *% 0); | ||
| 7 | @compileLog(not_undef *% 0); | ||
| 8 | @compileLog(0 *% undef); | ||
| 9 | @compileLog(0 *% not_undef); | ||
| 10 | |||
| 11 | // If either of the operands are one, result is the other operand. | ||
| 12 | @compileLog(undef *% 1); | ||
| 13 | @compileLog(not_undef *% 1); | ||
| 14 | @compileLog(1 *% undef); | ||
| 15 | @compileLog(1 *% not_undef); | ||
| 16 | |||
| 17 | // If either of the operands are undefined, result is undefined. | ||
| 18 | @compileLog(undef *% 2); | ||
| 19 | @compileLog(2 *% undef); | ||
| 20 | } | ||
| 21 | |||
| 22 | // error | ||
| 23 | // backend=stage2 | ||
| 24 | // target=native | ||
| 25 | // | ||
| 26 | // :6:5: error: found compile log statement | ||
| 27 | // | ||
| 28 | // Compile Log Output: | ||
| 29 | // @as(i64, 0) | ||
| 30 | // @as(i64, 0) | ||
| 31 | // @as(i64, 0) | ||
| 32 | // @as(i64, 0) | ||
| 33 | // @as(i64, undefined) | ||
| 34 | // @as(i64, 32) | ||
| 35 | // @as(i64, undefined) | ||
| 36 | // @as(i64, 32) | ||
| 37 | // @as(i64, undefined) | ||
| 38 | // @as(i64, undefined) | ||
test/cases/compile_errors/sub_on_undefined_value.zig+13-3| ... | @@ -1,10 +1,20 @@ | ... | @@ -1,10 +1,20 @@ |
| 1 | comptime { | 1 | comptime { |
| 2 | const a: i64 = undefined; | 2 | const undef: i64 = undefined; |
| 3 | _ = a - a; | 3 | const not_undef: i64 = 32; |
| 4 | |||
| 5 | // If the rhs is zero, then the other operand is returned, even if it is undefined. | ||
| 6 | @compileLog(undef - 0); | ||
| 7 | @compileLog(not_undef - 0); | ||
| 8 | |||
| 9 | _ = undef - undef; | ||
| 4 | } | 10 | } |
| 5 | 11 | ||
| 6 | // error | 12 | // error |
| 7 | // backend=stage2 | 13 | // backend=stage2 |
| 8 | // target=native | 14 | // target=native |
| 9 | // | 15 | // |
| 10 | // :3:13: error: use of undefined value here causes undefined behavior | 16 | // :9:17: error: use of undefined value here causes undefined behavior |
| 17 | // | ||
| 18 | // Compile Log Output: | ||
| 19 | // @as(i64, undefined) | ||
| 20 | // @as(i64, 32) |
test/cases/compile_errors/sub_sat_on_undefined_value.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | comptime { | ||
| 2 | const undef: i64 = undefined; | ||
| 3 | const not_undef: i64 = 32; | ||
| 4 | |||
| 5 | // If the RHS is zero, then the LHS is returned, even if it is undefined. | ||
| 6 | @compileLog(undef -| 0); | ||
| 7 | @compileLog(not_undef -| 0); | ||
| 8 | // If either of the operands are undefined, the result is undefined. | ||
| 9 | @compileLog(undef -| not_undef); | ||
| 10 | @compileLog(not_undef -| undef); | ||
| 11 | @compileLog(undef -| undef); | ||
| 12 | } | ||
| 13 | |||
| 14 | // error | ||
| 15 | // backend=stage2 | ||
| 16 | // target=native | ||
| 17 | // | ||
| 18 | // :6:5: error: found compile log statement | ||
| 19 | // | ||
| 20 | // Compile Log Output: | ||
| 21 | // @as(i64, undefined) | ||
| 22 | // @as(i64, 32) | ||
| 23 | // @as(i64, undefined) | ||
| 24 | // @as(i64, undefined) | ||
| 25 | // @as(i64, undefined) | ||
test/cases/compile_errors/sub_wrap_on_undefined_value.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | comptime { | ||
| 2 | const undef: i64 = undefined; | ||
| 3 | const not_undef: i64 = 32; | ||
| 4 | |||
| 5 | // If the RHS is zero, then the LHS is returned, even if it is undefined. | ||
| 6 | @compileLog(undef -% 0); | ||
| 7 | @compileLog(not_undef -% 0); | ||
| 8 | // If either of the operands are undefined, the result is undefined. | ||
| 9 | @compileLog(undef -% not_undef); | ||
| 10 | @compileLog(not_undef -% undef); | ||
| 11 | @compileLog(undef -% undef); | ||
| 12 | } | ||
| 13 | |||
| 14 | // error | ||
| 15 | // backend=stage2 | ||
| 16 | // target=native | ||
| 17 | // | ||
| 18 | // :6:5: error: found compile log statement | ||
| 19 | // | ||
| 20 | // Compile Log Output: | ||
| 21 | // @as(i64, undefined) | ||
| 22 | // @as(i64, 32) | ||
| 23 | // @as(i64, undefined) | ||
| 24 | // @as(i64, undefined) | ||
| 25 | // @as(i64, undefined) | ||