Skip to content

ENH: Support slack variables (lower bound emulation) #20648

@randolf-scholz

Description

@randolf-scholz

Describe the Bug

Context: python/typing#674

Slack variables would allow us to emulate lower bounds on type variables, which is relevant for methods like dict.__or__ or list concatenation, when an outer context is present.

from typing import Never, assert_type, reveal_type

class Vec[T]:  # invariant in T
    def _items(self) -> list[T]: ...
    def _append(self, item: T) -> None: ...

def concat_vecs[T1, T2, _T_slack=Never](
    left: Vec[T1],
    right: Vec[T2],
) -> Vec[T1 | T2 | _T_slack]:  # <-- Slack variable makes return a supertype of T1 | T2
    return Vec()

class A: ...
class B: ...
class C: ...

def test_vec_concat(left: Vec[A], right: Vec[B]) -> None:
    # without outer context, the type is precise
    assert_type(concat_vecs(left, right), Vec[A | B])  # ✅️
    # outer context allows widening due to slack variables
    _0: Vec[A | B]     = concat_vecs(left, right) # ❌️
    _1: Vec[A | B | C] = concat_vecs(left, right) # ❌️
    _2: Vec[object]    = concat_vecs(left, right) # ❌️

mypy fails to find existing solutions on some of these function calls:

  • _0: (T1=A, T2=B, _T_slack=Never)
  • _1: (T1=A, T2=B, _T_slack=C)
  • _2: (T1=A, T2=B, _T_slack=object)
21:38: error: Argument 1 to "concat_vecs" has incompatible type "Vec[A]"; expected "Vec[A | B]"  [arg-type]
21:44: error: Argument 2 to "concat_vecs" has incompatible type "Vec[B]"; expected "Vec[A | B]"  [arg-type]
22:38: error: Argument 1 to "concat_vecs" has incompatible type "Vec[A]"; expected "Vec[A | B | C]"  [arg-type]
22:44: error: Argument 2 to "concat_vecs" has incompatible type "Vec[B]"; expected "Vec[A | B | C]"  [arg-type]
23:38: error: Argument 1 to "concat_vecs" has incompatible type "Vec[A]"; expected "Vec[object]"  [arg-type]
23:44: error: Argument 2 to "concat_vecs" has incompatible type "Vec[B]"; expected "Vec[object]"  [arg-type]

no playground link as typevar defaults are still not supported.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions