Skip to content

Fix IndexError when unpacking empty tuple in type alias#20931

Open
bxff wants to merge 2 commits intopython:masterfrom
bxff:fix/unpack-index-error
Open

Fix IndexError when unpacking empty tuple in type alias#20931
bxff wants to merge 2 commits intopython:masterfrom
bxff:fix/unpack-index-error

Conversation

@bxff
Copy link

@bxff bxff commented Feb 28, 2026

Fixes #20913

mypy crashes with IndexError when using Unpack[tuple[()]] (or just omitting the TypeVarTuple args entirely) in a PEP 695 type alias:

class C[*Ts]:
    pass

type T[T, *Ts] = C[*Ts]

x: T[bool, Unpack[tuple[()]]]  # IndexError
y: T[bool]                      # also crashes

The crash is in visit_instance() — when expanding builtins.tuple args that reduce to nothing, args[0] is accessed on an empty list.

The fix handles this in two places:

  1. visit_instance: when builtins.tuple ends up with empty args after expansion, return TupleType([], ...) (the empty fixed-length tuple) instead of crashing
  2. expand_type_list_with_unpack / expand_type_tuple_with_unpack: inline Unpack[TupleType] results into their items, so Unpack[tuple[()]] correctly expands to nothing

Both T[bool] and T[bool, Unpack[tuple[()]]] now correctly resolve to C[()].

@github-actions

This comment has been minimized.


type T[T, *Ts] = C[*Ts]

x: T[bool, Unpack[tuple[()]]]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a reveal type to make sure the type is being preserved?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. reveal_type(x) -> __main__.C[Unpack[builtins.tuple]]

@bxff bxff force-pushed the fix/unpack-index-error branch 2 times, most recently from 5f2afae to debf2a5 Compare March 1, 2026 15:45
@github-actions

This comment has been minimized.

type T[T, *Ts] = C[*Ts]

x: T[bool, Unpack[tuple[()]]]
reveal_type(x) # N: Revealed type is "__main__.C[Unpack[builtins.tuple]]"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok this is the wrong type.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but this is pre-existing, on unpatched master (without this fix), C[Unpack[tuple[()]]] already reveals identically to unparameterized C:

# on master, no fix applied
x: C[Unpack[tuple[()]]]
reveal_type(x)   # C[Unpack[builtins.tuple[Any, ...]]]

y: C
reveal_type(y)   # C[Unpack[builtins.tuple[Any, ...]]]

This PR only prevents the IndexError crash; the wrong type for empty tuple unpacks is a separate issue.

Copy link
Collaborator

@A5rocks A5rocks Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that's identical behavior (note that the messages there have parameters provided to tuple). IIRC the issue you're showing is that we don't pass empty_index (or whatever the name is)... but in this case I don't think that's relevant, because if you unpack something empty that should be like x: T[bool] which currently has the reveal_type output.

Basically, I think what you show and this PR are different code paths (based on what I remember), so I think that behavior is irrelevant...? I'd be fine if this is a followup tbh, as long as you poke at it a bit and make sure it's not a 1 line change or something wrong with your crash fix.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh I see my mistake, you're right, I was comparing against the wrong code path. T[bool, Unpack[tuple[()]]] should just be T[bool] which gives C[()]. Fixed and updated the test.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gah, no, I misread what T was. My bad! Interesting that this also crashes:

class C[*Ts]:
    pass

type T[T, *Ts] = C[*Ts]

x: T[bool]

reveal_type(x)

So no Unpack[tuple[()]] necessary. Given that, I'm a bit surprised about the code modifications you made -- what's the tuple instance without .args coming from? (unless that's a different error!)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same error actually. T[bool] hits the same args[0] IndexError at expandtype.py:228. Tested both on 3.12 with the fix:

x: T[bool]                        -> C[()]
y: T[bool, Unpack[tuple[()]]]     -> C[()]

I'll add T[bool] as a test case too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so in args[0] it's synthesized based on the args from T, I assume? Interesting.... Sorry for all the comments! I'm not comfortable enough with this part of mypy to know if this is the right approach.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, appreciate the review! The T[bool] catch was really helpful.

@bxff bxff force-pushed the fix/unpack-index-error branch 3 times, most recently from 8d4dd34 to debf2a5 Compare March 1, 2026 21:52
@github-actions

This comment has been minimized.

@bxff bxff force-pushed the fix/unpack-index-error branch from debf2a5 to ab2bdd7 Compare March 3, 2026 17:39
@github-actions

This comment has been minimized.

When expanding type arguments for a builtins.tuple instance, the
normalization code accessed args[0] without checking if args was
non-empty. This caused an IndexError when Unpack[tuple[()]] was used
in a type alias, since the empty tuple expansion produces zero args.

Fixes python#20913
@bxff bxff force-pushed the fix/unpack-index-error branch from dbc02ee to 4c1229e Compare March 3, 2026 18:13
Copy link
Collaborator

@A5rocks A5rocks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not completely comfortable with this, but I'm not familiar with this part of mypy so that doesn't mean anything :/

@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IndexError when unpacking type

2 participants