Skip to content

Narrowing of constrained typevar discards constraint static type generic information #1475

@MeGaGiGaGon

Description

@MeGaGiGaGon

Summary

While reading astral-sh/ruff#21172 , I wanted to try using the changes that PR made, but found a bug. With this code:

https://play.ty.dev/a370a55d-5dc3-4da3-aa4b-62a0553b9c16

from typing import reveal_type


def foo[T: (list[int], None)](x: T) -> T:
    match x:
        case list():
            reveal_type(x)  # Revealed type: `Top[list[Unknown]]`
    if isinstance(x, list):
        reveal_type(x)  # Revealed type: `Top[list[Unknown]]`
    return x

The two xs should be narrowed to list[int], not Top[list[Unknown]]. From what I understand reading astral-sh/ruff#21172 , the code now converts the constraint to the union of the top of it's members in the narrowing, so T becomes Top[list[int]] | Top[None], but since list[int] is a fully static type, Top[list[int]] should just be list[int]. So it looks like the generic information is getting discarded somewhere along the way.

Edit:

I did some messing around with ty versions, and I found some interesting things:
In <=alpha.21 both reveal T@foo & list[Unknown]
In alpha.22 - alpha.25 only the isinstance gives Top[list[Unknown]], the match is still T@foo & list[Unknown]
In the playground (and presumably in the future alpha.26) the above behavior happens.

So it looks like this is not a new issue, at least for isinstance narrowing.

Version

playground (3c8fb6876)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggenericsBugs or features relating to ty's generics implementationnarrowingrelated to flow-sensitive type narrowing

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions