Compare commits

...

13 Commits

Author SHA1 Message Date
David Peter
96e7ebb588 New KnownInstanceType 2025-11-26 13:24:21 +01:00
David Peter
54c88b599d Store binding context 2025-11-26 12:02:48 +01:00
David Peter
8ed96b04e4 Cleanup 2025-11-26 09:04:44 +01:00
David Peter
0a2536736b Better diagnostic message 2025-11-25 14:54:04 +01:00
David Peter
6aaa9d784a Fix problem with np.array related to type[T] 2025-11-25 12:04:41 +01:00
David Peter
d85469e94c Store definition in instance types 2025-11-25 09:56:21 +01:00
David Peter
f184132d69 Fix value-position specializations 2025-11-25 08:57:30 +01:00
David Peter
96c491099f Rename 2025-11-25 08:57:30 +01:00
David Peter
c1e6ecccc0 Patch panics for stringified annotations for now 2025-11-25 08:57:30 +01:00
David Peter
343c6b6287 Handle PEP 613 aliases as well 2025-11-25 08:57:30 +01:00
David Peter
f40ab81093 Handle attribute expressions as well 2025-11-25 08:57:30 +01:00
David Peter
eee6f25f2e Use assignment definition as typevar binding context 2025-11-25 08:57:30 +01:00
David Peter
013d43a2dd [ty] Generic implicit types aliases 2025-11-25 08:57:30 +01:00
9 changed files with 747 additions and 143 deletions

View File

@@ -79,9 +79,8 @@ async def main():
task("B"),
)
# TODO: these should be `int`
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
```
## Under the hood

View File

@@ -191,13 +191,13 @@ def _(
reveal_type(int_or_callable) # revealed: int | ((str, /) -> bytes)
reveal_type(callable_or_int) # revealed: ((str, /) -> bytes) | int
# TODO should be Unknown | int
reveal_type(type_var_or_int) # revealed: typing.TypeVar | int
reveal_type(type_var_or_int) # revealed: T@TypeVarOrInt | int
# TODO should be int | Unknown
reveal_type(int_or_type_var) # revealed: int | typing.TypeVar
reveal_type(int_or_type_var) # revealed: int | T@IntOrTypeVar
# TODO should be Unknown | None
reveal_type(type_var_or_none) # revealed: typing.TypeVar | None
reveal_type(type_var_or_none) # revealed: T@TypeVarOrNone | None
# TODO should be None | Unknown
reveal_type(none_or_type_var) # revealed: None | typing.TypeVar
reveal_type(none_or_type_var) # revealed: None | T@NoneOrTypeVar
```
If a type is unioned with itself in a value expression, the result is just that type. No
@@ -366,7 +366,9 @@ def g(obj: Y):
reveal_type(obj) # revealed: list[int | str]
```
## Generic types
## Generic implicit type aliases
### Functionality
Implicit type aliases can also be generic:
@@ -388,24 +390,25 @@ ListOrTuple = list[T] | tuple[T, ...]
ListOrTupleLegacy = Union[list[T], tuple[T, ...]]
MyCallable = Callable[P, T]
AnnotatedType = Annotated[T, "tag"]
TransparentAlias = T
MyOptional = T | None
# TODO: Consider displaying this as `<class 'list[T]'>`, … instead? (and similar for some others below)
reveal_type(MyList) # revealed: <class 'list[typing.TypeVar]'>
reveal_type(MyDict) # revealed: <class 'dict[typing.TypeVar, typing.TypeVar]'>
reveal_type(MyList) # revealed: <class 'list[T@MyList]'>
reveal_type(MyDict) # revealed: <class 'dict[T@MyDict, U@MyDict]'>
reveal_type(MyType) # revealed: GenericAlias
reveal_type(IntAndType) # revealed: <class 'tuple[int, typing.TypeVar]'>
reveal_type(Pair) # revealed: <class 'tuple[typing.TypeVar, typing.TypeVar]'>
reveal_type(Sum) # revealed: <class 'tuple[typing.TypeVar, typing.TypeVar]'>
reveal_type(IntAndType) # revealed: <class 'tuple[int, T@IntAndType]'>
reveal_type(Pair) # revealed: <class 'tuple[T@Pair, T@Pair]'>
reveal_type(Sum) # revealed: <class 'tuple[T@Sum, U@Sum]'>
reveal_type(ListOrTuple) # revealed: types.UnionType
reveal_type(ListOrTupleLegacy) # revealed: types.UnionType
reveal_type(MyCallable) # revealed: GenericAlias
reveal_type(AnnotatedType) # revealed: <typing.Annotated special form>
reveal_type(TransparentAlias) # revealed: typing.TypeVar
reveal_type(MyOptional) # revealed: types.UnionType
def _(
list_of_ints: MyList[int],
dict_str_to_int: MyDict[str, int],
# TODO: no error here
# error: [invalid-type-form] "`typing.TypeVar` is not a generic class"
subclass_of_int: MyType[int],
int_and_str: IntAndType[str],
pair_of_ints: Pair[int],
@@ -413,48 +416,40 @@ def _(
list_or_tuple: ListOrTuple[int],
list_or_tuple_legacy: ListOrTupleLegacy[int],
# TODO: no error here
# error: [too-many-positional-arguments] "Too many positional arguments: expected 1, got 2"
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression: Did you mean `tuple[str, bytes]`?"
my_callable: MyCallable[[str, bytes], int],
annotated_int: AnnotatedType[int],
transparent_alias: TransparentAlias[int],
optional_int: MyOptional[int],
):
# TODO: This should be `list[int]`
reveal_type(list_of_ints) # revealed: @Todo(specialized generic alias in type expression)
# TODO: This should be `dict[str, int]`
reveal_type(dict_str_to_int) # revealed: @Todo(specialized generic alias in type expression)
# TODO: This should be `type[int]`
reveal_type(subclass_of_int) # revealed: Unknown
# TODO: This should be `tuple[int, str]`
reveal_type(int_and_str) # revealed: @Todo(specialized generic alias in type expression)
# TODO: This should be `tuple[int, int]`
reveal_type(pair_of_ints) # revealed: @Todo(specialized generic alias in type expression)
# TODO: This should be `tuple[int, bytes]`
reveal_type(int_and_bytes) # revealed: @Todo(specialized generic alias in type expression)
# TODO: This should be `list[int] | tuple[int, ...]`
reveal_type(list_or_tuple) # revealed: @Todo(Generic specialization of types.UnionType)
# TODO: This should be `list[int] | tuple[int, ...]`
reveal_type(list_or_tuple_legacy) # revealed: @Todo(Generic specialization of types.UnionType)
reveal_type(list_of_ints) # revealed: list[int]
reveal_type(dict_str_to_int) # revealed: dict[str, int]
reveal_type(subclass_of_int) # revealed: type[int]
reveal_type(int_and_str) # revealed: tuple[int, str]
reveal_type(pair_of_ints) # revealed: tuple[int, int]
reveal_type(int_and_bytes) # revealed: tuple[int, bytes]
reveal_type(list_or_tuple) # revealed: list[int] | tuple[int, ...]
reveal_type(list_or_tuple_legacy) # revealed: list[int] | tuple[int, ...]
reveal_type(list_or_tuple_legacy) # revealed: list[int] | tuple[int, ...]
# TODO: This should be `(str, bytes) -> int`
reveal_type(my_callable) # revealed: @Todo(Generic specialization of typing.Callable)
# TODO: This should be `int`
reveal_type(annotated_int) # revealed: @Todo(Generic specialization of typing.Annotated)
reveal_type(my_callable) # revealed: Unknown
reveal_type(annotated_int) # revealed: int
reveal_type(transparent_alias) # revealed: int
reveal_type(optional_int) # revealed: int | None
```
Generic implicit type aliases can be partially specialized:
```py
U = TypeVar("U")
DictStrTo = MyDict[str, U]
reveal_type(DictStrTo) # revealed: GenericAlias
reveal_type(DictStrTo) # revealed: <class 'dict[str, U@DictStrTo]'>
def _(
# TODO: No error here
# error: [invalid-type-form] "Invalid subscript of object of type `GenericAlias` in type expression"
dict_str_to_int: DictStrTo[int],
):
# TODO: This should be `dict[str, int]`
reveal_type(dict_str_to_int) # revealed: Unknown
reveal_type(dict_str_to_int) # revealed: dict[str, int]
```
Using specializations of generic implicit type aliases in other implicit type aliases works as
@@ -464,26 +459,65 @@ expected:
IntsOrNone = MyList[int] | None
IntsOrStrs = Pair[int] | Pair[str]
ListOfPairs = MyList[Pair[str]]
ListOrTupleOfInts = ListOrTuple[int]
AnnotatedInt = AnnotatedType[int]
SubclassOfInt = MyType[int]
# TODO: No error here
# error: [too-many-positional-arguments] "Too many positional arguments: expected 1, got 2"
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression: Did you mean `list[int]`?"
CallableIntToStr = MyCallable[[int], str]
reveal_type(IntsOrNone) # revealed: UnionType
reveal_type(IntsOrStrs) # revealed: UnionType
reveal_type(ListOfPairs) # revealed: GenericAlias
reveal_type(IntsOrNone) # revealed: types.UnionType
reveal_type(IntsOrStrs) # revealed: types.UnionType
reveal_type(ListOfPairs) # revealed: <class 'list[tuple[str, str]]'>
reveal_type(ListOrTupleOfInts) # revealed: types.UnionType
reveal_type(AnnotatedInt) # revealed: <typing.Annotated special form>
reveal_type(SubclassOfInt) # revealed: GenericAlias
reveal_type(CallableIntToStr) # revealed: Unknown
def _(
# TODO: This should not be an error
# error: [invalid-type-form] "Variable of type `UnionType` is not allowed in a type expression"
ints_or_none: IntsOrNone,
# TODO: This should not be an error
# error: [invalid-type-form] "Variable of type `UnionType` is not allowed in a type expression"
ints_or_strs: IntsOrStrs,
list_of_pairs: ListOfPairs,
list_or_tuple_of_ints: ListOrTupleOfInts,
annotated_int: AnnotatedInt,
subclass_of_int: SubclassOfInt,
callable_int_to_str: CallableIntToStr,
):
# TODO: This should be `list[int] | None`
reveal_type(ints_or_none) # revealed: Unknown
# TODO: This should be `tuple[int, int] | tuple[str, str]`
reveal_type(ints_or_strs) # revealed: Unknown
# TODO: This should be `list[tuple[str, str]]`
reveal_type(list_of_pairs) # revealed: @Todo(Support for `typing.GenericAlias` instances in type expressions)
reveal_type(ints_or_none) # revealed: list[int] | None
reveal_type(ints_or_strs) # revealed: tuple[int, int] | tuple[str, str]
reveal_type(list_of_pairs) # revealed: list[tuple[str, str]]
reveal_type(list_or_tuple_of_ints) # revealed: list[int] | tuple[int, ...]
reveal_type(annotated_int) # revealed: int
reveal_type(subclass_of_int) # revealed: type[int]
# TODO: This should be `(int, /) -> str`
reveal_type(callable_int_to_str) # revealed: Unknown
```
A generic implicit type alias can also be used in another generic implicit type alias:
```py
from typing_extensions import Any
B = TypeVar("B", bound=int)
MyOtherList = MyList[T]
MyOtherType = MyType[T]
TypeOrList = MyType[B] | MyList[B]
reveal_type(MyOtherList) # revealed: <class 'list[T@MyOtherList]'>
reveal_type(MyOtherType) # revealed: GenericAlias
reveal_type(TypeOrList) # revealed: types.UnionType
def _(
list_of_ints: MyOtherList[int],
subclass_of_int: MyOtherType[int],
type_or_list: TypeOrList[Any],
):
reveal_type(list_of_ints) # revealed: list[int]
reveal_type(subclass_of_int) # revealed: type[int]
# TODO: Should be `type[Any] | list[Any]`
reveal_type(type_or_list) # revealed: @Todo(type[T] for typevar T) | list[Any]
```
If a generic implicit type alias is used unspecialized in a type expression, we treat it as an
@@ -496,11 +530,11 @@ def _(
my_callable: MyCallable,
):
# TODO: Should be `list[Unknown]`
reveal_type(my_list) # revealed: list[typing.TypeVar]
reveal_type(my_list) # revealed: list[T@MyList]
# TODO: Should be `dict[Unknown, Unknown]`
reveal_type(my_dict) # revealed: dict[typing.TypeVar, typing.TypeVar]
reveal_type(my_dict) # revealed: dict[T@MyDict, U@MyDict]
# TODO: Should be `(...) -> Unknown`
reveal_type(my_callable) # revealed: (...) -> typing.TypeVar
reveal_type(my_callable) # revealed: (...) -> T@MyCallable
```
(Generic) implicit type aliases can be used as base classes:
@@ -522,37 +556,182 @@ reveal_mro(Derived1)
GenericBaseAlias = GenericBase[T]
# TODO: No error here
# error: [non-subscriptable] "Cannot subscript object of type `<class 'GenericBase[typing.TypeVar]'>` with no `__class_getitem__` method"
class Derived2(GenericBaseAlias[int]):
pass
```
### Imported aliases
Generic implicit type aliases can be imported from other modules and specialized:
`my_types.py`:
```py
from typing_extensions import TypeVar
T = TypeVar("T")
MyList = list[T]
```
`main.py`:
```py
from my_types import MyList
import my_types as mt
def _(
list_of_ints1: MyList[int],
list_of_ints2: mt.MyList[int],
):
reveal_type(list_of_ints1) # revealed: list[int]
reveal_type(list_of_ints2) # revealed: list[int]
```
### In stringified annotations
Generic implicit type aliases can be specialized in stringified annotations:
```py
from typing_extensions import TypeVar
T = TypeVar("T")
MyList = list[T]
def _(
list_of_ints: "MyList[int]",
):
reveal_type(list_of_ints) # revealed: list[int]
```
### Error cases
A generic alias that is already fully specialized cannot be specialized again:
```py
ListOfInts = list[int]
# TODO: this should be an error
# error: [too-many-positional-arguments] "Too many positional arguments: expected 0, got 1"
def _(doubly_specialized: ListOfInts[int]):
# TODO: this should be `Unknown`
reveal_type(doubly_specialized) # revealed: @Todo(specialized generic alias in type expression)
reveal_type(doubly_specialized) # revealed: Unknown
```
Specializing a generic implicit type alias with an incorrect number of type arguments also results
in an error:
```py
from typing_extensions import TypeVar
T = TypeVar("T")
U = TypeVar("U")
MyList = list[T]
MyDict = dict[T, U]
def _(
# TODO: this should be an error
# error: [too-many-positional-arguments] "Too many positional arguments: expected 1, got 2"
list_too_many_args: MyList[int, str],
# TODO: this should be an error
# error: [missing-argument] "No argument provided for required parameter `U`"
dict_too_few_args: MyDict[int],
):
# TODO: this should be `Unknown`
reveal_type(list_too_many_args) # revealed: @Todo(specialized generic alias in type expression)
# TODO: this should be `Unknown`
reveal_type(dict_too_few_args) # revealed: @Todo(specialized generic alias in type expression)
reveal_type(list_too_many_args) # revealed: Unknown
reveal_type(dict_too_few_args) # revealed: Unknown
```
Trying to specialize a non-name node results in an error:
```py
from ty_extensions import TypeOf
IntOrStr = int | str
def this_does_not_work() -> TypeOf[IntOrStr]:
raise NotImplementedError()
def _(
# TODO: Better error message? `invalid-type-form`
# error: [too-many-positional-arguments] "Too many positional arguments: expected 0, got 1"
specialized: this_does_not_work()[int],
):
reveal_type(specialized) # revealed: Unknown
```
Similarly, if you try to specialize a union type without a binding context, we emit an error:
```py
# error: [invalid-type-form] "`types.UnionType` is not subscriptable"
x: (list[T] | set[T])[int]
def _():
reveal_type(x) # revealed: Unknown
```
### Multiple definitions
#### Shadowed definitions
When a generic type alias shadows a definition from an outer scope, the inner definition is used:
```py
from typing_extensions import TypeVar
T = TypeVar("T")
MyAlias = list[T]
def outer():
MyAlias = set[T]
def _(x: MyAlias[int]):
reveal_type(x) # revealed: set[int]
```
#### Statically known conditions
```py
from typing_extensions import TypeVar
T = TypeVar("T")
if True:
MyAlias1 = list[T]
else:
MyAlias1 = set[T]
if False:
MyAlias2 = list[T]
else:
MyAlias2 = set[T]
def _(
x1: MyAlias1[int],
x2: MyAlias2[int],
):
reveal_type(x1) # revealed: list[int]
reveal_type(x2) # revealed: set[int]
```
#### Statically unknown conditions
If several definitions are visible, we emit an error:
```py
from typing_extensions import TypeVar
T = TypeVar("T")
def flag() -> bool:
return True
if flag():
MyAlias = list[T]
else:
MyAlias = set[T]
# error: [invalid-type-form] "Invalid subscript of object of type `<class 'list[T@MyAlias]'> | <class 'set[T@MyAlias]'>` in type expression"
def _(x: MyAlias[int]):
reveal_type(x) # revealed: Unknown
```
## `Literal`s
@@ -642,8 +821,7 @@ Deprecated = Annotated[T, "deprecated attribute"]
class C:
old: Deprecated[int]
# TODO: Should be `int`
reveal_type(C().old) # revealed: @Todo(Generic specialization of typing.Annotated)
reveal_type(C().old) # revealed: int
```
If the metadata argument is missing, we emit an error (because this code fails at runtime), but
@@ -1298,3 +1476,14 @@ def _(
reveal_type(recursive_dict3) # revealed: dict[Divergent, int]
reveal_type(recursive_dict4) # revealed: dict[Divergent, int]
```
### Self-referential generic implicit type aliases
<!-- expect-panic: execute: too many cycle iterations -->
```py
from typing import TypeVar
T = TypeVar("T")
NestedDict = dict[str, "NestedDict[T] | T"]
```

View File

@@ -96,6 +96,24 @@ def _(x: MyAlias):
reveal_type(x) # revealed: int | ((str, /) -> int)
```
## Generic aliases
```py
from typing import TypeAlias, TypeVar
T = TypeVar("T")
MyList: TypeAlias = list[T]
ListOrSet: TypeAlias = list[T] | set[T]
reveal_type(MyList) # revealed: <class 'list[T]'>
reveal_type(ListOrSet) # revealed: types.UnionType
def _(list_of_int: MyList[int], list_or_set_of_str: ListOrSet[str]):
reveal_type(list_of_int) # revealed: list[int]
reveal_type(list_or_set_of_str) # revealed: list[str] | set[str]
```
## Subscripted generic alias in union
```py
@@ -107,8 +125,7 @@ Alias1: TypeAlias = list[T] | set[T]
MyAlias: TypeAlias = int | Alias1[str]
def _(x: MyAlias):
# TODO: int | list[str] | set[str]
reveal_type(x) # revealed: int | @Todo(Specialization of union type alias)
reveal_type(x) # revealed: int | list[str] | set[str]
```
## Imported

View File

@@ -90,6 +90,12 @@ impl<'db> Definition<'db> {
.to_string(),
)
}
DefinitionKind::Assignment(assignment) => {
let target_node = assignment.target.node(&module);
target_node
.as_name_expr()
.map(|name_expr| name_expr.id.as_str().to_string())
}
_ => None,
}
}

View File

@@ -906,6 +906,13 @@ impl<'db> Type<'db> {
matches!(self, Type::GenericAlias(_))
}
// pub(crate) fn as_generic_alias(&self) -> Option<GenericAlias<'db>> {
// match self {
// Type::GenericAlias(alias) => Some(*alias),
// _ => None,
// }
// }
const fn is_dynamic(&self) -> bool {
matches!(self, Type::Dynamic(_))
}
@@ -6792,7 +6799,7 @@ impl<'db> Type<'db> {
}
KnownInstanceType::Literal(ty) => Ok(ty.inner(db)),
KnownInstanceType::Annotated(ty) => Ok(ty.inner(db)),
KnownInstanceType::TypeGenericAlias(ty) => {
KnownInstanceType::TypeGenericAlias(instance) => {
// When `type[…]` appears in a value position (e.g. in an implicit type alias),
// we infer its argument as a type expression. This ensures that we can emit
// diagnostics for invalid type expressions, and more importantly, that we can
@@ -6801,9 +6808,26 @@ impl<'db> Type<'db> {
// (`int` -> instance of `int` -> subclass of `int`) can be lossy, but it is
// okay for all valid arguments to `type[…]`.
Ok(ty.inner(db).to_meta_type(db))
let ty = instance.inner(db);
if ty.is_type_var() {
// TODO:
// This is a temporary workaround until we have proper support for type[T].
// If we pass a typevar to `.to_meta_type()`, we currently get `type[B]`,
// where `B` is the upper bound of `T`. However, we really need `type[T]`
// here. Otherwise, when we specialize a generic implicit type alias like
// `TypeOrList[T] = type[T] | list[T]` using `TypeOrList[Any]`, we would get
// `type[B] | list[Any]`, which leads to a lot of false positives for numpy-
// users.
Ok(todo_type!("type[T] for typevar T"))
} else {
Ok(ty.to_meta_type(db))
}
}
KnownInstanceType::GenericAlias(instance) => Ok(instance.inner(db)),
KnownInstanceType::Callable(instance) => {
Ok(Type::Callable(instance.callable_type(db)))
}
KnownInstanceType::Callable(callable) => Ok(Type::Callable(*callable)),
KnownInstanceType::LiteralStringAlias(ty) => Ok(ty.inner(db)),
},
@@ -7198,17 +7222,83 @@ impl<'db> Type<'db> {
}
}
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => match type_mapping {
TypeMapping::BindLegacyTypevars(binding_context) => {
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
Type::KnownInstance(known_instance) => match known_instance {
KnownInstanceType::TypeVar(typevar) => {
match type_mapping {
TypeMapping::BindLegacyTypevars(binding_context) => {
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
}
TypeMapping::Specialization(_) |
TypeMapping::PartialSpecialization(_) |
TypeMapping::PromoteLiterals(_) |
TypeMapping::BindSelf(_) |
TypeMapping::ReplaceSelf { .. } |
TypeMapping::Materialize(_) |
TypeMapping::ReplaceParameterDefaults => self,
}
}
TypeMapping::Specialization(_) |
TypeMapping::PartialSpecialization(_) |
TypeMapping::PromoteLiterals(_) |
TypeMapping::BindSelf(_) |
TypeMapping::ReplaceSelf { .. } |
TypeMapping::Materialize(_) |
TypeMapping::ReplaceParameterDefaults => self,
KnownInstanceType::UnionType(instance) => {
if let Ok(union_type) = instance.union_type(db) {
Type::KnownInstance(KnownInstanceType::UnionType(
UnionTypeInstance::new(
db,
instance._value_expr_types(db),
Ok(union_type.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
instance.binding_context(db),
)
))
} else {
self
}
},
KnownInstanceType::Annotated(instance) => {
Type::KnownInstance(KnownInstanceType::Annotated(
TypeInContext::new(
db,
instance.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
instance.binding_context(db),
)
))
},
KnownInstanceType::TypeGenericAlias(instance) => {
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
TypeInContext::new(
db,
instance.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
instance.binding_context(db),
)
))
},
KnownInstanceType::GenericAlias(instance) => {
Type::KnownInstance(KnownInstanceType::GenericAlias(
TypeInContext::new(
db,
instance.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
instance.binding_context(db),
)
))
},
KnownInstanceType::Callable(instance) => {
Type::KnownInstance(KnownInstanceType::Callable(CallableTypeInstance::new(
db,
instance.callable_type(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
instance.binding_context(db),
)))
},
KnownInstanceType::SubscriptedProtocol(_) |
KnownInstanceType::SubscriptedGeneric(_) |
KnownInstanceType::TypeAliasType(_) |
KnownInstanceType::Deprecated(_) |
KnownInstanceType::Field(_) |
KnownInstanceType::ConstraintSet(_) |
KnownInstanceType::GenericContext(_) |
KnownInstanceType::Specialization(_) |
KnownInstanceType::Literal(_) |
KnownInstanceType::LiteralStringAlias(_) |
KnownInstanceType::NewType(_) => {
// TODO: For some of these, we may need to apply the type mapping to inner types.
self
},
}
Type::FunctionLiteral(function) => {
@@ -7369,8 +7459,7 @@ impl<'db> Type<'db> {
// some other generic context's specialization is applied to it.
| Type::ClassLiteral(_)
| Type::BoundSuper(_)
| Type::SpecialForm(_)
| Type::KnownInstance(_) => self,
| Type::SpecialForm(_) => self,
}
}
@@ -7507,6 +7596,53 @@ impl<'db> Type<'db> {
});
}
Type::KnownInstance(known_instance) => match known_instance {
KnownInstanceType::UnionType(instance) => {
if let Ok(union_type) = instance.union_type(db) {
union_type.find_legacy_typevars_impl(
db,
binding_context,
typevars,
visitor,
);
}
}
KnownInstanceType::Annotated(ty) => {
ty.inner(db)
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
}
KnownInstanceType::Callable(instance) => {
instance.callable_type(db).find_legacy_typevars_impl(
db,
binding_context,
typevars,
visitor,
);
}
KnownInstanceType::TypeGenericAlias(ty) => {
ty.inner(db)
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
}
KnownInstanceType::GenericAlias(ty) => {
ty.inner(db)
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
}
KnownInstanceType::SubscriptedProtocol(_)
| KnownInstanceType::SubscriptedGeneric(_)
| KnownInstanceType::TypeVar(_)
| KnownInstanceType::TypeAliasType(_)
| KnownInstanceType::Deprecated(_)
| KnownInstanceType::Field(_)
| KnownInstanceType::ConstraintSet(_)
| KnownInstanceType::GenericContext(_)
| KnownInstanceType::Specialization(_)
| KnownInstanceType::Literal(_)
| KnownInstanceType::LiteralStringAlias(_)
| KnownInstanceType::NewType(_) => {
// TODO: For some of these, we may need to try to find legacy typevars in inner types.
}
},
Type::Dynamic(_)
| Type::Never
| Type::AlwaysTruthy
@@ -7534,7 +7670,6 @@ impl<'db> Type<'db> {
| Type::EnumLiteral(_)
| Type::BoundSuper(_)
| Type::SpecialForm(_)
| Type::KnownInstance(_)
| Type::TypedDict(_) => {}
}
}
@@ -8075,13 +8210,15 @@ pub enum KnownInstanceType<'db> {
Literal(InternedType<'db>),
/// A single instance of `typing.Annotated`
Annotated(InternedType<'db>),
Annotated(TypeInContext<'db>),
/// An instance of `typing.GenericAlias` representing a `type[...]` expression.
TypeGenericAlias(InternedType<'db>),
TypeGenericAlias(TypeInContext<'db>),
GenericAlias(TypeInContext<'db>),
/// An instance of `typing.GenericAlias` representing a `Callable[...]` expression.
Callable(CallableType<'db>),
Callable(CallableTypeInstance<'db>),
/// A literal string which is the right-hand side of a PEP 613 `TypeAlias`.
LiteralStringAlias(InternedType<'db>),
@@ -8123,14 +8260,16 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
visitor.visit_type(db, *union_type);
}
}
KnownInstanceType::Literal(ty)
| KnownInstanceType::Annotated(ty)
| KnownInstanceType::TypeGenericAlias(ty)
| KnownInstanceType::LiteralStringAlias(ty) => {
KnownInstanceType::Annotated(instance)
| KnownInstanceType::TypeGenericAlias(instance)
| KnownInstanceType::GenericAlias(instance) => {
visitor.visit_type(db, instance.inner(db));
}
KnownInstanceType::Literal(ty) | KnownInstanceType::LiteralStringAlias(ty) => {
visitor.visit_type(db, ty.inner(db));
}
KnownInstanceType::Callable(callable) => {
visitor.visit_callable_type(db, callable);
KnownInstanceType::Callable(instance) => {
visitor.visit_callable_type(db, instance.callable_type(db));
}
KnownInstanceType::NewType(newtype) => {
if let ClassType::Generic(generic_alias) = newtype.base_class_type(db) {
@@ -8169,6 +8308,7 @@ impl<'db> KnownInstanceType<'db> {
Self::Literal(ty) => Self::Literal(ty.normalized_impl(db, visitor)),
Self::Annotated(ty) => Self::Annotated(ty.normalized_impl(db, visitor)),
Self::TypeGenericAlias(ty) => Self::TypeGenericAlias(ty.normalized_impl(db, visitor)),
Self::GenericAlias(ty) => Self::GenericAlias(ty.normalized_impl(db, visitor)),
Self::Callable(callable) => Self::Callable(callable.normalized_impl(db, visitor)),
Self::LiteralStringAlias(ty) => {
Self::LiteralStringAlias(ty.normalized_impl(db, visitor))
@@ -8207,6 +8347,7 @@ impl<'db> KnownInstanceType<'db> {
Self::Literal(_)
| Self::Annotated(_)
| Self::TypeGenericAlias(_)
| Self::GenericAlias(_)
| Self::Callable(_) => KnownClass::GenericAlias,
Self::LiteralStringAlias(_) => KnownClass::Str,
Self::NewType(_) => KnownClass::NewType,
@@ -8312,6 +8453,7 @@ impl<'db> KnownInstanceType<'db> {
KnownInstanceType::TypeGenericAlias(_) | KnownInstanceType::Callable(_) => {
f.write_str("GenericAlias")
}
KnownInstanceType::GenericAlias(_) => f.write_str("GenericAlias(…)"), //TODO
KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"),
KnownInstanceType::NewType(declaration) => {
write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db))
@@ -9494,6 +9636,11 @@ pub struct UnionTypeInstance<'db> {
/// contains the first encountered error.
#[returns(ref)]
union_type: Result<Type<'db>, InvalidTypeExpressionError<'db>>,
/// The typevar binding context in which this union type instance was created.
/// For an implicit type alias like `ListOrSet = list[T] | set[T]`, this would
/// be the definition of `ListOrSet`.
binding_context: Option<Definition<'db>>,
}
impl get_size2::GetSize for UnionTypeInstance<'_> {}
@@ -9513,7 +9660,12 @@ impl<'db> UnionTypeInstance<'db> {
Ok(ty) => builder.add_in_place(ty),
Err(error) => {
return Type::KnownInstance(KnownInstanceType::UnionType(
UnionTypeInstance::new(db, Some(value_expr_types), Err(error)),
UnionTypeInstance::new(
db,
Some(value_expr_types),
Err(error),
typevar_binding_context,
),
));
}
}
@@ -9523,6 +9675,7 @@ impl<'db> UnionTypeInstance<'db> {
db,
Some(value_expr_types),
Ok(builder.build()),
typevar_binding_context,
)))
}
@@ -9568,7 +9721,7 @@ impl<'db> UnionTypeInstance<'db> {
.clone()
.map(|ty| ty.normalized_impl(db, visitor));
Self::new(db, value_expr_types, union_type)
Self::new(db, value_expr_types, union_type, self.binding_context(db))
}
}
@@ -9591,6 +9744,56 @@ impl<'db> InternedType<'db> {
}
}
/// A salsa-interned `Type` with an associated binding context for type variables.
///
/// # Ordering
/// Ordering is based on the context's salsa-assigned id and not on its values.
/// The id may change between runs, or when the context was garbage collected and recreated.
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct TypeInContext<'db> {
inner: Type<'db>,
/// The typevar binding context in which this type was inferred.
binding_context: Option<Definition<'db>>,
}
impl get_size2::GetSize for TypeInContext<'_> {}
impl<'db> TypeInContext<'db> {
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
TypeInContext::new(
db,
self.inner(db).normalized_impl(db, visitor),
self.binding_context(db),
)
}
}
/// An instance of `typing.GenericAlias` representing a `Callable[...]` expression.
///
/// # Ordering
/// Ordering is based on the context's salsa-assigned id and not on its values.
/// The id may change between runs, or when the context was garbage collected and recreated.
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct CallableTypeInstance<'db> {
callable_type: CallableType<'db>,
/// The binding context in which this callable type instance was inferred.
binding_context: Option<Definition<'db>>,
}
impl get_size2::GetSize for CallableTypeInstance<'_> {}
impl<'db> CallableTypeInstance<'db> {
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
CallableTypeInstance::new(
db,
self.callable_type(db).normalized_impl(db, visitor),
self.binding_context(db),
)
}
}
/// Error returned if a type is not awaitable.
#[derive(Debug)]
enum AwaitError<'db> {

View File

@@ -185,6 +185,9 @@ impl<'db> ClassBase<'db> {
KnownInstanceType::TypeGenericAlias(_) => {
Self::try_from_type(db, KnownClass::Type.to_class_literal(db), subclass)
}
KnownInstanceType::GenericAlias(instance) => {
Self::try_from_type(db, instance.inner(db), subclass)
}
KnownInstanceType::Annotated(ty) => {
// Unions are not supported in this position, so we only need to support
// something like `class C(Annotated[Base, "metadata"]): ...`, which we

View File

@@ -101,14 +101,14 @@ use crate::types::typed_dict::{
};
use crate::types::visitor::any_over_type;
use crate::types::{
CallDunderError, CallableBinding, CallableType, CallableTypes, ClassLiteral, ClassType,
DataclassParams, DynamicType, InternedType, IntersectionBuilder, IntersectionType, KnownClass,
KnownInstanceType, LintDiagnosticGuard, MemberLookupPolicy, MetaclassCandidate,
PEP695TypeAliasType, Parameter, ParameterForm, Parameters, SpecialFormType, SubclassOfType,
TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers, TypeContext,
TypeQualifiers, TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarIdentity,
TypeVarInstance, TypeVarKind, TypeVarVariance, TypedDictType, UnionBuilder, UnionType,
UnionTypeInstance, binding_type, liskov, todo_type,
CallDunderError, CallableBinding, CallableType, CallableTypeInstance, CallableTypes,
ClassLiteral, ClassType, DataclassParams, DynamicType, InternedType, IntersectionBuilder,
IntersectionType, KnownClass, KnownInstanceType, LintDiagnosticGuard, MemberLookupPolicy,
MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm, Parameters, SpecialFormType,
SubclassOfType, TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers,
TypeContext, TypeInContext, TypeQualifiers, TypeVarBoundOrConstraintsEvaluation,
TypeVarDefaultEvaluation, TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance,
TypedDictType, UnionBuilder, UnionType, UnionTypeInstance, binding_type, liskov, todo_type,
};
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
use crate::unpack::{EvaluationMode, UnpackPosition};
@@ -4739,6 +4739,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
unpacked.expression_type(target)
}
TargetKind::Single => {
// This could be an implicit type alias (OptionalList = list[T] | None). Use the definition
// of `OptionalList` as the binding context while inferring the RHS (`list[T] | None`), in
// order to bind `T` to `OptionalList`.
let previous_typevar_binding_context =
self.typevar_binding_context.replace(definition);
let value_ty = if let Some(standalone_expression) = self.index.try_expression(value)
{
self.infer_standalone_expression_impl(value, standalone_expression, tcx)
@@ -4777,6 +4783,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.infer_expression(value, tcx)
};
self.typevar_binding_context = previous_typevar_binding_context;
// `TYPE_CHECKING` is a special variable that should only be assigned `False`
// at runtime, but is always considered `True` in type checking.
// See mdtest/known_constants.md#user-defined-type_checking for details.
@@ -5523,11 +5531,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.deferred_state = DeferredExpressionState::Deferred;
}
// This might be a PEP-613 type alias (`OptionalList: TypeAlias = list[T] | None`). Use
// the definition of `OptionalList` as the binding context while inferring the
// RHS (`list[T] | None`), in order to bind `T` to `OptionalList`.
let previous_typevar_binding_context = self.typevar_binding_context.replace(definition);
let inferred_ty = self.infer_maybe_standalone_expression(
value,
TypeContext::new(Some(declared.inner_type())),
);
self.typevar_binding_context = previous_typevar_binding_context;
self.deferred_state = previous_deferred_state;
self.dataclass_field_specifiers.clear();
@@ -10794,7 +10809,52 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
fn infer_subscript_load(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
let value_ty = self.infer_expression(&subscript.value, TypeContext::default());
self.infer_subscript_load_impl(value_ty, subscript)
// If we have an implicit type alias like `MyList = list[T]`, and if `MyList` is being
// used in another implicit type alias like `Numbers = MyList[int]`, then we infer the
// right hand side as a value expression, and need to handle the specialization here.
if let Type::KnownInstance(KnownInstanceType::GenericAlias(alias)) = value_ty {
return Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
self.db(),
self.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
alias.binding_context(self.db()),
false,
),
self.typevar_binding_context,
)));
}
// if let Type::GenericAlias(alias) = value_ty {
// return Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
// self.db(),
// self.infer_explicitly_specialized_type_alias(
// subscript,
// value_ty,
// Some(alias.definition(self.db())),
// false,
// ),
// self.typevar_binding_context,
// )));
// }
let result_ty = self.infer_subscript_load_impl(value_ty, subscript);
// let result_ty = if result_ty.is_generic_alias() {
// Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
// self.db(),
// result_ty,
// self.typevar_binding_context,
// )))
// } else {
// result_ty
// };
// eprintln!("Subscripting type: {}", value_ty.display(self.db()));
// eprintln!("Inferred subscript type: {}", result_ty.display(self.db()));
result_ty
}
fn infer_subscript_load_impl(
@@ -10848,7 +10908,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
} else if class.is_known(self.db(), KnownClass::Type) {
let argument_ty = self.infer_type_expression(slice);
return Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
InternedType::new(self.db(), argument_ty),
TypeInContext::new(self.db(), argument_ty, self.typevar_binding_context),
));
}
@@ -10926,9 +10986,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let ty = self.infer_type_expression(type_expr);
return Type::KnownInstance(KnownInstanceType::Annotated(InternedType::new(
return Type::KnownInstance(KnownInstanceType::Annotated(TypeInContext::new(
self.db(),
ty,
self.typevar_binding_context,
)));
}
Type::SpecialForm(SpecialFormType::Optional) => {
@@ -10959,6 +11020,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
match **slice {
ast::Expr::Tuple(ref tuple) => {
let typevar_binding_context = self.typevar_binding_context;
let mut elements = tuple
.elts
.iter()
@@ -10971,6 +11033,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
db,
None,
Ok(UnionType::from_elements(db, elements)),
typevar_binding_context,
),
));
@@ -10995,7 +11058,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// Similar to the branch above that handles `type[…]`, handle `typing.Type[…]`
let argument_ty = self.infer_type_expression(slice);
return Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
InternedType::new(self.db(), argument_ty),
TypeInContext::new(self.db(), argument_ty, self.typevar_binding_context),
));
}
Type::SpecialForm(SpecialFormType::Callable) => {
@@ -11004,7 +11067,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
.as_callable()
.expect("always returns Type::Callable");
return Type::KnownInstance(KnownInstanceType::Callable(callable));
return Type::KnownInstance(KnownInstanceType::Callable(
CallableTypeInstance::new(self.db(), callable, self.typevar_binding_context),
));
}
// `typing` special forms with a single generic argument
Type::SpecialForm(
@@ -11095,8 +11160,32 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
.map(Type::from)
.unwrap_or_else(Type::unknown);
}
Type::KnownInstance(KnownInstanceType::UnionType(_)) => {
return todo_type!("Specialization of union type alias");
Type::KnownInstance(KnownInstanceType::UnionType(instance)) => {
return self.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
instance.binding_context(self.db()),
false,
);
}
Type::KnownInstance(
KnownInstanceType::Annotated(instance)
| KnownInstanceType::TypeGenericAlias(instance),
) => {
return self.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
instance.binding_context(self.db()),
false,
);
}
Type::KnownInstance(KnownInstanceType::Callable(instance)) => {
return self.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
instance.binding_context(self.db()),
false,
);
}
_ => {}
}

View File

@@ -2,6 +2,8 @@ use itertools::Either;
use ruff_python_ast as ast;
use super::{DeferredExpressionState, TypeInferenceBuilder};
use crate::FxOrderSet;
use crate::semantic_index::definition::Definition;
use crate::types::diagnostic::{
self, INVALID_TYPE_FORM, NON_SUBSCRIPTABLE, report_invalid_argument_number_to_special_form,
report_invalid_arguments_to_callable,
@@ -11,9 +13,10 @@ use crate::types::string_annotation::parse_string_annotation;
use crate::types::tuple::{TupleSpecBuilder, TupleType};
use crate::types::visitor::any_over_type;
use crate::types::{
CallableType, DynamicType, IntersectionBuilder, KnownClass, KnownInstanceType,
LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType, Type,
TypeAliasType, TypeContext, TypeIsType, UnionBuilder, UnionType, todo_type,
BindingContext, CallableType, DynamicType, GenericContext, IntersectionBuilder, KnownClass,
KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType,
Type, TypeAliasType, TypeContext, TypeInContext, TypeIsType, TypeMapping, UnionBuilder,
UnionType, todo_type,
};
/// Type expressions
@@ -750,6 +753,84 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
}
/// Infer the type of an explicitly specialized generic type alias (implicit or PEP 613).
pub(crate) fn infer_explicitly_specialized_type_alias(
&mut self,
subscript: &ast::ExprSubscript,
mut value_ty: Type<'db>,
typevar_binding_context: Option<Definition<'db>>,
in_type_expression: bool,
) -> Type<'db> {
let db = self.db();
let Some(typevar_binding_context) = typevar_binding_context else {
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
let mut diag = builder.into_diagnostic(format_args!(
"`{}` is not subscriptable",
value_ty.display(db)
));
diag.info("Consider creating a type alias to create a binding context for the type variable(s)");
}
return Type::unknown();
};
if let Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) = value_ty
&& let Some(definition) = typevar.definition(db)
{
value_ty = value_ty.apply_type_mapping(
db,
&TypeMapping::BindLegacyTypevars(BindingContext::Definition(definition)),
TypeContext::default(),
);
}
let mut variables = FxOrderSet::default();
value_ty.find_legacy_typevars(db, Some(typevar_binding_context), &mut variables);
let generic_context = GenericContext::from_typevar_instances(db, variables);
let scope_id = self.scope();
let current_typevar_binding_context = self.typevar_binding_context;
let specialize = |types: &[Option<Type<'db>>]| {
let specialized = value_ty.apply_specialization(
db,
generic_context.specialize_partial(db, types.iter().copied()),
);
if in_type_expression {
specialized
.in_type_expression(db, scope_id, current_typevar_binding_context)
.unwrap_or_else(|_| Type::unknown())
} else {
// Update the binding context
match specialized {
// Type::GenericAlias(alias) => Type::GenericAlias(GenericAlias::new(
// db,
// alias.origin(db),
// alias.specialization(db),
// current_typevar_binding_context,
// )),
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(instance)) => {
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
TypeInContext::new(
db,
instance.inner(db),
current_typevar_binding_context,
),
))
}
_ => specialized,
}
}
};
self.infer_explicit_callable_specialization(
subscript,
value_ty,
generic_context,
specialize,
)
}
fn infer_subscript_type_expression(
&mut self,
subscript: &ast::ExprSubscript,
@@ -840,10 +921,6 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
Type::unknown()
}
KnownInstanceType::TypeVar(_) => {
self.infer_type_expression(slice);
todo_type!("TypeVar annotations")
}
KnownInstanceType::TypeAliasType(type_alias @ TypeAliasType::PEP695(_)) => {
match type_alias.generic_context(self.db()) {
Some(generic_context) => {
@@ -886,11 +963,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
self.infer_type_expression(slice);
todo_type!("Generic stringified PEP-613 type alias")
}
KnownInstanceType::UnionType(_) => {
self.infer_type_expression(slice);
todo_type!("Generic specialization of types.UnionType")
}
KnownInstanceType::Literal(ty) | KnownInstanceType::TypeGenericAlias(ty) => {
KnownInstanceType::Literal(ty) => {
self.infer_type_expression(slice);
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
builder.into_diagnostic(format_args!(
@@ -900,14 +973,36 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
Type::unknown()
}
KnownInstanceType::Callable(_) => {
self.infer_type_expression(slice);
todo_type!("Generic specialization of typing.Callable")
}
KnownInstanceType::Annotated(_) => {
self.infer_type_expression(slice);
todo_type!("Generic specialization of typing.Annotated")
}
KnownInstanceType::TypeVar(instance) => self
.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
instance.definition(self.db()),
false,
),
KnownInstanceType::UnionType(instance) => self
.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
instance.binding_context(self.db()),
true,
),
KnownInstanceType::Annotated(instance)
| KnownInstanceType::TypeGenericAlias(instance)
| KnownInstanceType::GenericAlias(instance) => self
.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
instance.binding_context(self.db()),
true,
),
KnownInstanceType::Callable(instance) => self
.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
instance.binding_context(self.db()),
true,
),
KnownInstanceType::NewType(newtype) => {
self.infer_type_expression(&subscript.slice);
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
@@ -948,13 +1043,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
}
}
Type::GenericAlias(_) => {
self.infer_type_expression(slice);
// If the generic alias is already fully specialized, this is an error. But it
// could have been specialized with another typevar (e.g. a type alias like `MyList
// = list[T]`), in which case it's later valid to do `MyList[int]`.
todo_type!("specialized generic alias in type expression")
}
// Type::GenericAlias(alias) => {
// self.infer_explicitly_specialized_type_alias(subscript, value_ty, None, true)
// }
Type::StringLiteral(_) => {
self.infer_type_expression(slice);
// For stringified TypeAlias; remove once properly supported
@@ -963,6 +1054,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
_ => {
self.infer_type_expression(slice);
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
dbg!(&value_ty);
builder.into_diagnostic(format_args!(
"Invalid subscript of object of type `{}` in type expression",
value_ty.display(self.db())

View File

@@ -100,7 +100,13 @@ impl<'db> Mro<'db> {
if original_bases.contains(&Type::SpecialForm(SpecialFormType::Protocol)) {
return;
}
if remaining_bases.iter().any(Type::is_generic_alias) {
if remaining_bases.iter().any(|ty| {
matches!(
ty,
Type::GenericAlias(..)
| Type::KnownInstance(KnownInstanceType::GenericAlias(_))
)
}) {
return;
}
resolved_bases.push(ClassBase::Generic);