Skip to content

Mypy does not check bounds of variable type aliases #13086

Closed as not planned
Closed as not planned

Description

@harahu

Bug Report

When having defined a TypeAlias dependent on a TypeVar, mypy does not allays that instances of these are actually allowed. That is, that they respect the bounds or restrictions of the used TypeVar. This is in contrast to the behavior for generic types, which are checked.

To Reproduce

from typing import Generic, TypeVar, TypeAlias

T = TypeVar("T", bound=bytes | str)
U = TypeVar("U", bytes, str)

BoundedTuple: TypeAlias = tuple[T]
RestrictedTuple: TypeAlias = tuple[U]


i_am_illegal1: BoundedTuple[int]  # Passes, even though this type shouldn't be possible!
i_am_illegal2: RestrictedTuple[int]  # Passes, even though this type shouldn't be possible!


class Bounded(Generic[T]):
    pass
    
class Restricted(Generic[U]):
    pass


i_am_illegal3: Bounded[int]  # Behaves as expected, resulting in `error: Type argument "int" of "Bounded" must be a subtype of "Union[bytes, str]"`
i_am_illegal4: Restricted[int]  # Behaves as expected, resulting in `error: Value of type variable "U" of "Restricted" cannot be "int"`

https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=f5763650eba54f25b51553dbdd58179f

Expected Behavior

I expect four errors instead of two from the example code above.

Actual Behavior

As can be seen, I was allowed to define types that shouldn't be allowed, given the restrictions of the type variables in use.

Your Environment

  • Mypy version used: 0.961
  • Mypy command-line flags: --strict
  • Python version used: 3.10

Activity

  1. AlexWaygood commented on Jul 7, 2022

    @AlexWaygood
    Member

    According to PEP 484, if T = TypeVar("T", bound=complex | str), T can be solved as being int where necessary, since PEP 484 states that type checkers should consider int and float to be implicit subtypes of complex. In "The Numeric Tower", the PEP states:

    when an argument is annotated as having type float, an argument of type int is acceptable; similar, for an argument annotated as having type complex, arguments of type float or int are acceptable.

    This explains why mypy does not emit an error for your first and third example.

    Your second example does seem to be buggy to me, however. Constrained TypeVars should only be solveable with one of the exact constraints, i.e., subtypes should not be permitted. So I believe you're correct when you say that mypy should be emitting an error for the following:

    from typing import Generic, TypeVar, TypeAlias
    
    U = TypeVar("U", complex, str)
    BoundTupleU: TypeAlias = tuple[U]
    i_am_illegal2: BoundTupleU[int]
  2. changed the title [-]Mypy does not check bounds of all generic types[/-] [+]Mypy does not check bounds of variable type aliases[/+] on Jul 7, 2022
  3. harahu commented on Jul 7, 2022

    @harahu
    Author

    [...] This explains why mypy does not emit an error for your first and third example.

    Good catch! Appreciate it. Changed the example, but the first example also remains faulty, so it seems this is an issue for variable type aliases, but not for generics. Please let me know whether the issue is worded more clearly (and correctly) now.

  4. AlexWaygood commented on Jul 7, 2022

    @AlexWaygood
    Member

    Oh yikes — yup, your revised first example definitely looks like a bug to me!

  5. AlexWaygood commented on Jul 7, 2022

    @AlexWaygood
    Member

    Oh yikes — yup, your revised first example definitely looks like a bug to me!

    ... But it's a duplicate of #10445. Your example using the constrained TypeVar is new, but I'm closing this so that we can keep all the discussion in one place, since it's the same bug :)

  6. harahu commented on Jul 7, 2022

    @harahu
    Author

    ... But it's a duplicate of #10445. Your example using the constrained TypeVar is new, but I'm closing this so that we can keep all the discussion in one place, since it's the same bug :)

    Thank you for noticing! Not sure how that issue slipped past me.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions