Sema: rewrite comptime arithmetic#23177
Conversation
63d6140 to
e5f0c5d
Compare
andrewrk
left a comment
There was a problem hiding this comment.
This could potentially be loosened in future (for instance, 0 * rt could be comptime-known 0 with a runtime assertion that rt is not undefined),
I suspect this will be a desirable change in the future, but, let's proceed with this at least for now.
Thanks for doing a thorough investigation and doing all this work to make the semantics consistent.
This commit reworks how Sema handles arithmetic on comptime-known values, fixing many bugs in the process. The general pattern is that arithmetic on comptime-known values is now handled by the new namespace `Sema.arith`. Functions handling comptime arithmetic no longer live on `Value`; this is because some of them can emit compile errors, so some *can't* go on `Value`. Only semantic analysis should really be doing arithmetic on `Value`s anyway, so it makes sense for it to integrate more tightly with `Sema`. This commit also implements more coherent rules surrounding how `undefined` interacts with comptime and mixed-comptime-runtime arithmetic. The rules are as follows. * If an operation cannot trigger Illegal Behavior, and any operand is `undefined`, the result is `undefined`. This includes operations like `0 *| undef`, where the LHS logically *could* be used to determine a defined result. This is partly to simplify the language, but mostly to permit codegen backends to represent `undefined` values as completely invalid states. * If an operation *can* trigger Illegal Behvaior, and any operand is `undefined`, then Illegal Behavior results. This occurs even if the operand in question isn't the one that "decides" illegal behavior; for instance, `undef / 1` is undefined. This is for the same reasons as described above. * An operation which would trigger Illegal Behavior, when evaluated at comptime, instead triggers a compile error. Additionally, if one operand is comptime-known undef, such that the other (runtime-known) operand isn't needed to determine that Illegal Behavior would occur, the compile error is triggered. * The only situation in which an operation with one comptime-known operand has a comptime-known result is if that operand is undefined, in which case the result is either undefined or a compile error per the above rules. This could potentially be loosened in future (for instance, `0 * rt` could be comptime-known 0 with a runtime assertion that `rt` is not undefined), but at least for now, defining it more conservatively simplifies the language and allows us to easily change this in future if desired. This commit fixes many bugs regarding the handling of `undefined`, particularly in vectors. Along with a collection of smaller tests, two very large test cases are added to check arithmetic on `undefined`. The operations which have been rewritten in this PR are: * `+`, `+%`, `+|`, `@addWithOverflow` * `-`, `-%`, `-|`, `@subWithOverflow` * `*`, `*%`, `*|`, `@mulWithOverflow` * `/`, `@divFloor`, `@divTrunc`, `@divExact` * `%`, `@rem`, `@mod` Other arithmetic operations are currently unchanged. Resolves: ziglang#22743 Resolves: ziglang#22745 Resolves: ziglang#22748 Resolves: ziglang#22749 Resolves: ziglang#22914
e5f0c5d to
42ea2f1
Compare
|
Latest push was just to fix conflict; CI should pass. |
| ) CompileError!Value { | ||
| const zcu = sema.pt.zcu; | ||
| if (lhs.isUndef(zcu)) return lhs; | ||
| if (lhs.isUndef(zcu)) return rhs; |
There was a problem hiding this comment.
Is those supposed to be rhs on second line? @mlugg
There was a problem hiding this comment.
Oh, yes it is, thank you. I'll open a follow-up soon to fix that typo.
This is a complementary PR to #23487 (I had only found one typo before). Now I've looked at the whole `arith.zig` file, trying to find other potential problems. Discussion about these changes: #23177 (comment)
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw pendants * `@truncate`, `@bitReverse`, `@byteSwap` Resolves ziglang#16466 `*` Resolves ziglang#21266 Resolves ziglang#21943 Resolves ziglang#23034 Resolves ziglang#24392 `*` (was already kind of resolved before this patch I think, but addresses the comment)
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw pendants * `@truncate`, `@bitReverse`, `@byteSwap` Resolves ziglang#16466 `*` Resolves ziglang#21266 Resolves ziglang#21943 Resolves ziglang#23034 Resolves ziglang#24392 `*` (was already kind of resolved before this patch I think, but addresses the comment)
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw pendants * `@truncate`, `@bitReverse`, `@byteSwap` Resolves ziglang#16466 `*` Resolves ziglang#21266 Resolves ziglang#21943 Resolves ziglang#23034 Resolves ziglang#24392 `*` (was already kind of resolved before this patch I think, but addresses the comment)
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. New rules applied to operators: * `<<`, `@shlExact`, `@shlWithOverflow`, `@shrExact`: compile error if any operator is undef * `<<|`, `>>`, all other affected ops: return undef if any operator is undef Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw pendants * `@truncate`, `@bitReverse`, `@byteSwap` Resolves ziglang#16466 `*` Resolves ziglang#21266 Resolves ziglang#21943 Resolves ziglang#23034 Resolves ziglang#24392 `*` (was already kind of resolved before this patch I think, but addresses the comment)
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. New rules applied to operators: * `<<`, `@shlExact`, `@shlWithOverflow`, `@shrExact`: compile error if any operator is undef * `<<|`, `>>`, all other affected ops: return undef if any operator is undef Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw pendants * `@truncate`, `@bitReverse`, `@byteSwap` Resolves ziglang#16466 `*` Resolves ziglang#21266 Resolves ziglang#21943 Resolves ziglang#23034 Resolves ziglang#24392 `*` (was already kind of resolved before this patch I think, but addresses the comment)
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. New rules applied to operators: * `<<`, `@shlExact`, `@shlWithOverflow`, `@shrExact`: compile error if any operator is undef * `<<|`, `>>`, all other affected ops: return undef if any operator is undef Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw pendants * `@truncate`, `@bitReverse`, `@byteSwap`
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. New rules applied to operators: * `<<`, `@shlExact`, `@shlWithOverflow`, `>>`, `@shrExact`: compile error if any operand is undef * `<<|`, `~`, `^`, `@truncate`, `@bitReverse`, `@byteSwap`: return undef if any operand is undef * `&`, `|`: Return undef if both operands are undef, turn undef into actual `0xAA` bytes otherwise Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw + reduce pendants * `@truncate`, `@bitReverse`, `@byteSwap`
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. New rules applied to operators: * `<<`, `@shlExact`, `@shlWithOverflow`, `>>`, `@shrExact`: compile error if any operand is undef * `<<|`, `~`, `^`, `@truncate`, `@bitReverse`, `@byteSwap`: return undef if any operand is undef * `&`, `|`: Return undef if both operands are undef, turn undef into actual `0xAA` bytes otherwise Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw + reduce pendants * `@truncate`, `@bitReverse`, `@byteSwap`
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. New rules applied to operators: * `<<`, `@shlExact`, `@shlWithOverflow`, `>>`, `@shrExact`: compile error if any operand is undef * `<<|`, `~`, `^`, `@truncate`, `@bitReverse`, `@byteSwap`: return undef if any operand is undef * `&`, `|`: Return undef if both operands are undef, turn undef into actual `0xAA` bytes otherwise Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw + reduce pendants * `@truncate`, `@bitReverse`, `@byteSwap`
This commit expands on the foundations laid by ziglang#23177 and moves even more `Sema`-only functionality from `Value` to `Sema.arith`. Specifically all shift and bitwise operations, `@truncate`, `@bitReverse` and `@byteSwap` have been moved and adapted to the new rules around `undefined`. Especially the comptime shift operations have been basically rewritten, fixing many open issues in the process. New rules applied to operators: * `<<`, `@shlExact`, `@shlWithOverflow`, `>>`, `@shrExact`: compile error if any operand is undef * `<<|`, `~`, `^`, `@truncate`, `@bitReverse`, `@byteSwap`: return undef if any operand is undef * `&`, `|`: Return undef if both operands are undef, turn undef into actual `0xAA` bytes otherwise Additionally this commit canonicalizes the representation of aggregates with all-undefined members in the `InternPool` by disallowing them and enforcing the usage of a single typed `undef` value instead. This reduces the amount of edge cases and fixes a bunch of bugs related to partially undefined vecs. List of operations directly affected by this patch: * `<<`, `<<|`, `@shlExact`, `@shlWithOverflow` * `>>`, `@shrExact` * `&`, `|`, `~`, `^` and their atomic rmw + reduce pendants * `@truncate`, `@bitReverse`, `@byteSwap`
|
Skipping these release notes; they don't look directly copy pastable. Please add them if you want them mentioned |

Hey @kristoff-it, this is what that "simple little one-line fix" evolved into! Isn't that fun?
This commit reworks how Sema handles arithmetic on comptime-known values, fixing many bugs in the process.
The general pattern is that arithmetic on comptime-known values is now handled by the new namespace
Sema.arith. Functions handling comptime arithmetic no longer live onValue; this is because some of them can emit compile errors, so some can't go onValue. Only semantic analysis should really be doing arithmetic onValues anyway, so it makes sense for it to integrate more tightly withSema.This commit also implements more coherent rules surrounding how
undefinedinteracts with comptime and mixed-comptime-runtime arithmetic. The rules are as follows.If an operation cannot trigger Illegal Behavior, and any operand is
undefined, the result isundefined. This includes operations like0 *| undef, where the LHS logically could be used to determine a defined result. This is partly to simplify the language, but mostly to permit codegen backends to representundefinedvalues as completely invalid states.If an operation can trigger Illegal Behvaior, and any operand is
undefined, then Illegal Behavior results. This occurs even if the operand in question isn't the one that "decides" illegal behavior; for instance,undef / 1triggers IB. This is for the same reasons as described above.An operation which would trigger Illegal Behavior, when evaluated at comptime, instead triggers a compile error. Additionally, if one operand is comptime-known undef, such that the other (runtime-known) operand isn't needed to determine that Illegal Behavior would occur, the compile error is triggered.
The only situation in which an operation with one comptime-known operand has a comptime-known result is if that operand is undefined, in which case the result is either undefined or a compile error per the above rules. This could potentially be loosened in future (for instance,
0 * rtcould be comptime-known 0 with a runtime assertion thatrtis not undefined), but at least for now, defining it more conservatively simplifies the language and allows us to easily change this in future if desired.This commit fixes many bugs regarding the handling of
undefined, particularly in vectors. Along with a collection of smaller tests, two very large test cases are added to check arithmetic onundefined.The operations which have been rewritten in this PR are:
+,+%,+|,@addWithOverflow-,-%,-|,@subWithOverflow*,*%,*|,@mulWithOverflow/,@divFloor,@divTrunc,@divExact%,@rem,@modOther arithmetic operations are currently unchanged.
Resolves: #22743
Resolves: #22745
Resolves: #22748
Resolves: #22749
Resolves: #22914