mirror of
https://github.com/ziglang/zig.git
synced 2025-12-09 07:43:10 +00:00
sema: fix safe integer arithmetic operations on undefined values
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)`.
This commit is contained in:
parent
4ace1f5a7f
commit
ec358d6db5
18
src/Sema.zig
18
src/Sema.zig
@ -16140,6 +16140,10 @@ fn analyzeArithmetic(
|
|||||||
return casted_lhs;
|
return casted_lhs;
|
||||||
}
|
}
|
||||||
if (maybe_lhs_val) |lhs_val| {
|
if (maybe_lhs_val) |lhs_val| {
|
||||||
|
if (lhs_val.isUndef(mod)) {
|
||||||
|
return mod.undefRef(resolved_type);
|
||||||
|
}
|
||||||
|
|
||||||
const val = if (scalar_tag == .ComptimeInt)
|
const val = if (scalar_tag == .ComptimeInt)
|
||||||
try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined)
|
try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined)
|
||||||
else
|
else
|
||||||
@ -16202,7 +16206,7 @@ fn analyzeArithmetic(
|
|||||||
},
|
},
|
||||||
.subwrap => {
|
.subwrap => {
|
||||||
// Integers only; floats are checked above.
|
// 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 either of the operands are undefined, the result is undefined.
|
||||||
if (maybe_rhs_val) |rhs_val| {
|
if (maybe_rhs_val) |rhs_val| {
|
||||||
if (rhs_val.isUndef(mod)) {
|
if (rhs_val.isUndef(mod)) {
|
||||||
@ -16223,8 +16227,8 @@ fn analyzeArithmetic(
|
|||||||
},
|
},
|
||||||
.sub_sat => {
|
.sub_sat => {
|
||||||
// Integers only; floats are checked above.
|
// Integers only; floats are checked above.
|
||||||
// If the RHS is zero, result is LHS.
|
// If the RHS is zero, then the LHS is returned, even if it is undefined.
|
||||||
// If either of the operands are undefined, result is undefined.
|
// If either of the operands are undefined, the result is undefined.
|
||||||
if (maybe_rhs_val) |rhs_val| {
|
if (maybe_rhs_val) |rhs_val| {
|
||||||
if (rhs_val.isUndef(mod)) {
|
if (rhs_val.isUndef(mod)) {
|
||||||
return mod.undefRef(resolved_type);
|
return mod.undefRef(resolved_type);
|
||||||
@ -16255,7 +16259,9 @@ fn analyzeArithmetic(
|
|||||||
// If either of the operands are undefined, it's a compile error
|
// If either of the operands are undefined, it's a compile error
|
||||||
// because there is a possible value for which the addition would
|
// because there is a possible value for which the addition would
|
||||||
// overflow (max_int), causing illegal behavior.
|
// 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,
|
// If either of the operands are inf, and the other operand is zero,
|
||||||
// the result is nan.
|
// the result is nan.
|
||||||
// If either of the operands are nan, the result is nan.
|
// If either of the operands are nan, the result is nan.
|
||||||
@ -38093,7 +38099,7 @@ fn numberAddWrapScalar(
|
|||||||
ty: Type,
|
ty: Type,
|
||||||
) !Value {
|
) !Value {
|
||||||
const mod = sema.mod;
|
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) {
|
if (ty.zigTypeTag(mod) == .ComptimeInt) {
|
||||||
return sema.intAdd(lhs, rhs, ty, undefined);
|
return sema.intAdd(lhs, rhs, ty, undefined);
|
||||||
@ -38183,7 +38189,7 @@ fn numberSubWrapScalar(
|
|||||||
ty: Type,
|
ty: Type,
|
||||||
) !Value {
|
) !Value {
|
||||||
const mod = sema.mod;
|
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) {
|
if (ty.zigTypeTag(mod) == .ComptimeInt) {
|
||||||
return sema.intSub(lhs, rhs, ty, undefined);
|
return sema.intSub(lhs, rhs, ty, undefined);
|
||||||
|
|||||||
@ -1,10 +1,24 @@
|
|||||||
comptime {
|
comptime {
|
||||||
const a: i64 = undefined;
|
const undef: i64 = undefined;
|
||||||
_ = a + a;
|
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
|
// error
|
||||||
// backend=stage2
|
// backend=stage2
|
||||||
// target=native
|
// 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)
|
||||||
|
|||||||
31
test/cases/compile_errors/add_sat_on_undefined_value.zig
Normal file
31
test/cases/compile_errors/add_sat_on_undefined_value.zig
Normal file
@ -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)
|
||||||
31
test/cases/compile_errors/add_wrap_on_undefined_value.zig
Normal file
31
test/cases/compile_errors/add_wrap_on_undefined_value.zig
Normal file
@ -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)
|
||||||
@ -1,10 +1,38 @@
|
|||||||
comptime {
|
comptime {
|
||||||
const a: i64 = undefined;
|
const undef: i64 = undefined;
|
||||||
_ = a * a;
|
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
|
// error
|
||||||
// backend=stage2
|
// backend=stage2
|
||||||
// target=native
|
// 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)
|
||||||
|
|||||||
38
test/cases/compile_errors/mult_sat_on_undefined_value.zig
Normal file
38
test/cases/compile_errors/mult_sat_on_undefined_value.zig
Normal file
@ -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)
|
||||||
38
test/cases/compile_errors/mult_wrap_on_undefined_value.zig
Normal file
38
test/cases/compile_errors/mult_wrap_on_undefined_value.zig
Normal file
@ -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)
|
||||||
@ -1,10 +1,20 @@
|
|||||||
comptime {
|
comptime {
|
||||||
const a: i64 = undefined;
|
const undef: i64 = undefined;
|
||||||
_ = a - a;
|
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
|
// error
|
||||||
// backend=stage2
|
// backend=stage2
|
||||||
// target=native
|
// 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)
|
||||||
|
|||||||
25
test/cases/compile_errors/sub_sat_on_undefined_value.zig
Normal file
25
test/cases/compile_errors/sub_sat_on_undefined_value.zig
Normal file
@ -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)
|
||||||
25
test/cases/compile_errors/sub_wrap_on_undefined_value.zig
Normal file
25
test/cases/compile_errors/sub_wrap_on_undefined_value.zig
Normal file
@ -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)
|
||||||
Loading…
x
Reference in New Issue
Block a user