fix Preserve arity for tuple literals with * #2861#2888
fix Preserve arity for tuple literals with * #2861#2888asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes type inference for tuple multiplication when the repeat count is a known integer literal, preserving concrete tuple arity (up to the existing 256-element expansion cap) instead of degrading to an unbounded tuple[T, ...].
Changes:
- Add tuple-repeat inference for
tuple * Literal[int]/Literal[int] * tuple, expanding concrete tuples when the resulting length stays withinMAX_TUPLE_LENGTH. - Re-export the tuple literal expansion cap (
MAX_TUPLE_LENGTH) for reuse in operator inference. - Add regression tests covering tuple repetition (including
0, negative, and overflow-to-fallback cases).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| pyrefly/lib/alt/operators.rs | Adds try_tuple_repeat and wires it into * inference for tuples using literal repeat counts. |
| pyrefly/lib/alt/expr.rs | Exposes MAX_TUPLE_LENGTH for cross-module use. |
| pyrefly/lib/test/tuple.rs | Adds test coverage for tuple repetition type inference and the 256-element cap fallback behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Type::Literal(box Literal { | ||
| value: Lit::Int(n), .. | ||
| }) => n.as_i64(), |
There was a problem hiding this comment.
try_tuple_repeat only recognizes Literal[int] as the repeat count. In Python, bool is a subtype of int and tuple * True/False is valid; right now those cases will fall through to the generic __mul__ typing and won't preserve arity. Consider extracting the count via Literal.value.as_index_i64() (covers both Int and Bool) instead of matching only Lit::Int.
| Type::Literal(box Literal { | |
| value: Lit::Int(n), .. | |
| }) => n.as_i64(), | |
| Type::Literal(box Literal { value, .. }) => value.as_index_i64(), |
| self.tuple_concat(l, r) | ||
| } else if x.op == Operator::Mult | ||
| && let Some(result) = self.try_tuple_repeat(lhs, rhs) | ||
| { | ||
| result |
There was a problem hiding this comment.
The tuple-repeat special case is implemented for normal binary * in binop_types, but *= goes through augassign_infer and currently has no analogous handling (unlike +=, which special-cases tuples). This can make x *= 2 produce a less precise type than x = x * 2 for tuples; consider adding a Operator::Mult branch in augassign_infer that reuses try_tuple_repeat.
| } | ||
|
|
||
| static MAX_TUPLE_LENGTH: usize = 256; | ||
| pub(crate) static MAX_TUPLE_LENGTH: usize = 256; |
There was a problem hiding this comment.
MAX_TUPLE_LENGTH is a compile-time constant but is declared as a static and is now being re-exported for cross-module use. Elsewhere in the repo similar limits are typically const (e.g. pyrefly/lib/alt/answers_solver.rs:1979, pyrefly/lib/binding/scope.rs:519). Consider making this pub(crate) const MAX_TUPLE_LENGTH: usize = 256; to match that pattern and avoid introducing an addressable global unnecessarily.
| pub(crate) static MAX_TUPLE_LENGTH: usize = 256; | |
| pub(crate) const MAX_TUPLE_LENGTH: usize = 256; |
This comment has been minimized.
This comment has been minimized.
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
Summary
Fixes #2861
special-casing tuple multiplication so known integer repeats preserve concrete tuple arity instead of falling through to the generic tuple[T, ...] stub.
respects the existing 256-element literal tuple cap by falling back once expansion would get too large.
Test Plan
add test