Repository navigation
[mypyc] Fix negation and constant folding of unsigned native int constants - #22469
Open
Jahongir-Qurbonov wants to merge 3 commits into
Open
Jahongir-Qurbonov wants to merge 3 commits into
Jahongir-Qurbonov wants to merge 3 commits into
Conversation
Unary minus on an integer literal is constant folded in LowLevelIRBuilder.unary_minus, but the result wasn't wrapped around for unsigned native int types. For example, -u64(1) was folded into the u64 constant -1 instead of 2**64 - 1, so int(-u64(1)) and str(-u64(1)) produced -1, and comparisons such as x == -u64(1) emitted C code that failed to compile with -Werror=sign-compare. The same happened with u32, u16 and u8. Wrap the folded value modulo 2**bits for unsigned types, which matches the behavior of negating a non-literal unsigned value at runtime.
Constant folding evaluated operations on Final native int values using arbitrary-precision int semantics, so expressions such as "-X", "X - 2" or "~X" with "X: Final[u64] = 1" produced a negative value that was then rejected with 'Value -1 is out of range for "u64"'. Similar errors happened for u8/u16/u32 (e.g. "X + 1" with "X: Final[u8] = 255"), and "%d" % (-X,) silently produced "-1". At runtime unsigned native int operations wrap around. Wrap each folded int result around based on the native int type of the expression, so that intermediate results also behave like at runtime (e.g. (X - 2) // 2). mypy also folds Final initializers like "Y: Final[u64] = -X" into the out-of-range final value -1, so also wrap final values when loading them. Signed native int overflow is undefined behavior, so folded expressions that overflow a signed type are still reported as errors.
…32-bit Converting a native int constant that doesn't fit in a short int, such as -u32(1) or -u64(1), to int generated an unreachable fast path that truncated or shifted the constant. On 32-bit platforms the C compiler rejected this with -Werror=overflow. Load such constants as int literals instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Unary minus on an integer literal is constant folded in
LowLevelIRBuilder.unary_minus, but the folded value wasn't wrapped around for unsigned native int types (there was a# TODO: Overflow? Unsigned?comment). So-u64(1)became the u64 constant-1instead of2**64 - 1:str(-u64(1))also gave"-1", and comparisons or other operations with a negated unsigned literal could produce C code that fails to compile with-Werror=sign-compare. u64, u32, u16 and u8 were all affected.Now the folded value is taken modulo
2**bitsfor unsigned types, which matches what negating a non-literal unsigned value does at runtime (and the docs say unsigned operations wrap around on overflow). Large u64 values are already emitted with aULLsuffix. Signed overflow (e.g.-i64(-2**63)) is left alone, since it's undefined behavior per the docs.I checked related cases like
0 - u64(1)and~u64(0); they don't go through this folding and already worked, so I only added run tests for them.Added a
test_negate_literalrun test to run-u8/u16/u32/u64.test, and irbuild tests for u8 and u64 that show the folded constants.Negating a
Finalunsigned native int constant was also rejected, e.g.-XwithX: Final[u64] = 1gaveValue -1 is out of range for "u64"(likewiseX - 2,~X,X + 1forX: Final[u8] = 255, and references toZ: Final[u64] = -X), and"%d" % (-X,)silently produced"-1". Constant folding and Final value loading now wrap int results around for unsigned native int types, at each folded operation, so the results match runtime semantics (e.g.(X - 2) // 2is2**63 - 1). Signed native int overflow is undefined behavior, so overflowing signed constant expressions are still reported as errors.Converting such a constant to
int(e.g.int(-u32(1))) generated an unreachable fast path that truncated the constant, which C compilers reject on 32-bit platforms, so large native int constants are now loaded directly as int literals in that case.