GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — [mypyc] Fix native int shifts by negative or too large counts by Jahongir-Qurbonov · Pull Request #22419 · python/mypy

Conversation

@Jahongir-Qurbonov

@Jahongir-Qurbonov Jahongir-Qurbonov commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Shifts of native ints (i64, i32, i16, u32, u16, u8) were compiled to a plain C shift. In C that is undefined behavior when the count is negative or at least the bit width; on x86 the count is effectively masked:

from mypy_extensions import i64, u32

def rshift(x: i64, n: i64) -> i64:
    return x >> n

def lshift(x: u32, n: u32) -> u32:
    return x << n

rshift(-8, 64)  # -8, expected -1
rshift(5, 64)   # 5, expected 0
rshift(8, -1)   # 0, expected ValueError: negative shift count
lshift(1, 32)   # 1, expected 0

With a constant count (e.g. x >> 64 for i64, or x << 40 for i32), the generated C failed to compile ("shift count >= width of type" with -Werror).

Now the plain shift is only used when the count is in range:

  • A negative count raises ValueError.
  • A count at or above the width gives 0 for left shifts and for right shifts of non-negative or unsigned values, and -1 for a right shift of a negative signed value. This matches int (truncated or wrapped, like other native int shifts).
  • Constant counts are resolved at compile time.
  • A variable count only adds one unsigned comparison on the fast path, similar to the division-by-zero check for unsigned types.

This also fixes two related codegen issues:

  • An integer literal on the left of a shift is now cast to the native int type in C. The type of a C shift is the promoted type of the left operand, so 3 << n with n: i64 was a 32-bit shift.
  • Unsigned comparisons of i16 values were missing the uint16_t cast.

Added run tests for all native int types. They cover counts 0, width-1, width, width+1, 64, 100 and negative counts, both constant and variable.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@Jahongir-Qurbonov
[mypyc] Fix native int shifts by negative or too large counts
4be18f5
Shifts of native ints (i64, i32, i16, u32, u16, u8) were compiled to a
plain C shift, which is undefined behavior if the count is negative or
at least the bit width. In practice the count was masked, so e.g.
i64 `5 >> 64` gave 5 and `8 >> -1` gave 0, and a constant count such as
`x >> 64` failed the C build with -Werror.

Use the plain shift only if the count is in range. Otherwise a negative
count raises ValueError, a left shift gives 0 and a right shift gives 0,
or -1 for a negative signed value. Constant counts are resolved at
compile time. The common case needs one extra unsigned comparison.

Also cast an integer literal left operand to the native int type in C,
since the C shift type follows the promoted left operand (`3 << n` for
i64 was a 32-bit shift), and add the missing uint16_t cast for unsigned
comparisons of i16 values.
@JukkaL

JukkaL commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

I have an alternative implementation of this in a local branch, which uses a a bit different approach. However, it depends on some other changes and won't be ready for a PR yet. I'm currently leaning towards my implementation, since it avoids raising an exception (but it doesn't match CPython semantics). I still need to think about this.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Jahongir-Qurbonov

Jahongir-Qurbonov commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, makes sense. Feel free to close this PR if you go with your implementation. If it helps, the tests in this PR may still be useful for checking the edge cases (negative and too large shift counts for each native int type).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@Jahongir-Qurbonov @JukkaL
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.