mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 04:45:01 +00:00
[ty] Infer parameter specializations of generic aliases (#18021)
Some checks are pending
CI / cargo build (msrv) (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / cargo build (msrv) (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This updates our function specialization inference to infer type mappings from parameters that are generic aliases, e.g.: ```py def f[T](x: list[T]) -> T: ... reveal_type(f(["a", "b"])) # revealed: str ``` Though note that we're still inferring the type of list literals as `list[Unknown]`, so for now we actually need something like the following in our tests: ```py def _(x: list[str]): reveal_type(f(x)) # revealed: str ```
This commit is contained in:
parent
55df9271ba
commit
0fb94c052e
13 changed files with 98 additions and 85 deletions
|
@ -17,7 +17,6 @@ def _(x: tuple[int, str], y: tuple[None, tuple[int]]):
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _(x: tuple[int, ...], y: tuple[str, ...]):
|
def _(x: tuple[int, ...], y: tuple[str, ...]):
|
||||||
# TODO: should be `tuple[int | str, ...]`
|
reveal_type(x + y) # revealed: tuple[int | str, ...]
|
||||||
reveal_type(x + y) # revealed: tuple[int | Unknown, ...]
|
|
||||||
reveal_type(x + (1, 2)) # revealed: tuple[int, ...]
|
reveal_type(x + (1, 2)) # revealed: tuple[int, ...]
|
||||||
```
|
```
|
||||||
|
|
|
@ -88,14 +88,12 @@ def takes_in_protocol(x: CanIndex[T]) -> T:
|
||||||
return x[0]
|
return x[0]
|
||||||
|
|
||||||
def deep_list(x: list[str]) -> None:
|
def deep_list(x: list[str]) -> None:
|
||||||
# TODO: revealed: list[str]
|
reveal_type(takes_in_list(x)) # revealed: list[str]
|
||||||
reveal_type(takes_in_list(x)) # revealed: list[Unknown]
|
|
||||||
# TODO: revealed: str
|
# TODO: revealed: str
|
||||||
reveal_type(takes_in_protocol(x)) # revealed: Unknown
|
reveal_type(takes_in_protocol(x)) # revealed: Unknown
|
||||||
|
|
||||||
def deeper_list(x: list[set[str]]) -> None:
|
def deeper_list(x: list[set[str]]) -> None:
|
||||||
# TODO: revealed: list[set[str]]
|
reveal_type(takes_in_list(x)) # revealed: list[set[str]]
|
||||||
reveal_type(takes_in_list(x)) # revealed: list[Unknown]
|
|
||||||
# TODO: revealed: set[str]
|
# TODO: revealed: set[str]
|
||||||
reveal_type(takes_in_protocol(x)) # revealed: Unknown
|
reveal_type(takes_in_protocol(x)) # revealed: Unknown
|
||||||
|
|
||||||
|
@ -119,13 +117,11 @@ This also works when passing in arguments that are subclasses of the parameter t
|
||||||
class Sub(list[int]): ...
|
class Sub(list[int]): ...
|
||||||
class GenericSub(list[T]): ...
|
class GenericSub(list[T]): ...
|
||||||
|
|
||||||
# TODO: revealed: list[int]
|
reveal_type(takes_in_list(Sub())) # revealed: list[int]
|
||||||
reveal_type(takes_in_list(Sub())) # revealed: list[Unknown]
|
|
||||||
# TODO: revealed: int
|
# TODO: revealed: int
|
||||||
reveal_type(takes_in_protocol(Sub())) # revealed: Unknown
|
reveal_type(takes_in_protocol(Sub())) # revealed: Unknown
|
||||||
|
|
||||||
# TODO: revealed: list[str]
|
reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[str]
|
||||||
reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[Unknown]
|
|
||||||
# TODO: revealed: str
|
# TODO: revealed: str
|
||||||
reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown
|
reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown
|
||||||
|
|
||||||
|
|
|
@ -83,14 +83,12 @@ def takes_in_protocol[T](x: CanIndex[T]) -> T:
|
||||||
return x[0]
|
return x[0]
|
||||||
|
|
||||||
def deep_list(x: list[str]) -> None:
|
def deep_list(x: list[str]) -> None:
|
||||||
# TODO: revealed: list[str]
|
reveal_type(takes_in_list(x)) # revealed: list[str]
|
||||||
reveal_type(takes_in_list(x)) # revealed: list[Unknown]
|
|
||||||
# TODO: revealed: str
|
# TODO: revealed: str
|
||||||
reveal_type(takes_in_protocol(x)) # revealed: Unknown
|
reveal_type(takes_in_protocol(x)) # revealed: Unknown
|
||||||
|
|
||||||
def deeper_list(x: list[set[str]]) -> None:
|
def deeper_list(x: list[set[str]]) -> None:
|
||||||
# TODO: revealed: list[set[str]]
|
reveal_type(takes_in_list(x)) # revealed: list[set[str]]
|
||||||
reveal_type(takes_in_list(x)) # revealed: list[Unknown]
|
|
||||||
# TODO: revealed: set[str]
|
# TODO: revealed: set[str]
|
||||||
reveal_type(takes_in_protocol(x)) # revealed: Unknown
|
reveal_type(takes_in_protocol(x)) # revealed: Unknown
|
||||||
|
|
||||||
|
@ -114,13 +112,11 @@ This also works when passing in arguments that are subclasses of the parameter t
|
||||||
class Sub(list[int]): ...
|
class Sub(list[int]): ...
|
||||||
class GenericSub[T](list[T]): ...
|
class GenericSub[T](list[T]): ...
|
||||||
|
|
||||||
# TODO: revealed: list[int]
|
reveal_type(takes_in_list(Sub())) # revealed: list[int]
|
||||||
reveal_type(takes_in_list(Sub())) # revealed: list[Unknown]
|
|
||||||
# TODO: revealed: int
|
# TODO: revealed: int
|
||||||
reveal_type(takes_in_protocol(Sub())) # revealed: Unknown
|
reveal_type(takes_in_protocol(Sub())) # revealed: Unknown
|
||||||
|
|
||||||
# TODO: revealed: list[str]
|
reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[str]
|
||||||
reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[Unknown]
|
|
||||||
# TODO: revealed: str
|
# TODO: revealed: str
|
||||||
reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown
|
reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown
|
||||||
|
|
||||||
|
|
|
@ -562,25 +562,22 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
fn is_none(&self, db: &'db dyn Db) -> bool {
|
fn is_none(&self, db: &'db dyn Db) -> bool {
|
||||||
self.into_nominal_instance()
|
self.into_nominal_instance()
|
||||||
.is_some_and(|instance| instance.class().is_known(db, KnownClass::NoneType))
|
.is_some_and(|instance| instance.class.is_known(db, KnownClass::NoneType))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_bool(&self, db: &'db dyn Db) -> bool {
|
fn is_bool(&self, db: &'db dyn Db) -> bool {
|
||||||
self.into_nominal_instance()
|
self.into_nominal_instance()
|
||||||
.is_some_and(|instance| instance.class().is_known(db, KnownClass::Bool))
|
.is_some_and(|instance| instance.class.is_known(db, KnownClass::Bool))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_notimplemented(&self, db: &'db dyn Db) -> bool {
|
pub fn is_notimplemented(&self, db: &'db dyn Db) -> bool {
|
||||||
self.into_nominal_instance().is_some_and(|instance| {
|
self.into_nominal_instance()
|
||||||
instance
|
.is_some_and(|instance| instance.class.is_known(db, KnownClass::NotImplementedType))
|
||||||
.class()
|
|
||||||
.is_known(db, KnownClass::NotImplementedType)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_object(&self, db: &'db dyn Db) -> bool {
|
pub fn is_object(&self, db: &'db dyn Db) -> bool {
|
||||||
self.into_nominal_instance()
|
self.into_nominal_instance()
|
||||||
.is_some_and(|instance| instance.class().is_object(db))
|
.is_some_and(|instance| instance.class.is_object(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn is_todo(&self) -> bool {
|
pub const fn is_todo(&self) -> bool {
|
||||||
|
@ -1063,7 +1060,7 @@ impl<'db> Type<'db> {
|
||||||
(_, Type::Never) => false,
|
(_, Type::Never) => false,
|
||||||
|
|
||||||
// Everything is a subtype of `object`.
|
// Everything is a subtype of `object`.
|
||||||
(_, Type::NominalInstance(instance)) if instance.class().is_object(db) => true,
|
(_, Type::NominalInstance(instance)) if instance.class.is_object(db) => true,
|
||||||
|
|
||||||
// In general, a TypeVar `T` is not a subtype of a type `S` unless one of the two conditions is satisfied:
|
// In general, a TypeVar `T` is not a subtype of a type `S` unless one of the two conditions is satisfied:
|
||||||
// 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`.
|
// 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`.
|
||||||
|
@ -1373,7 +1370,7 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
// All types are assignable to `object`.
|
// All types are assignable to `object`.
|
||||||
// TODO this special case might be removable once the below cases are comprehensive
|
// TODO this special case might be removable once the below cases are comprehensive
|
||||||
(_, Type::NominalInstance(instance)) if instance.class().is_object(db) => true,
|
(_, Type::NominalInstance(instance)) if instance.class.is_object(db) => true,
|
||||||
|
|
||||||
// In general, a TypeVar `T` is not assignable to a type `S` unless one of the two conditions is satisfied:
|
// In general, a TypeVar `T` is not assignable to a type `S` unless one of the two conditions is satisfied:
|
||||||
// 1. `T` is a bound TypeVar and `T`'s upper bound is assignable to `S`.
|
// 1. `T` is a bound TypeVar and `T`'s upper bound is assignable to `S`.
|
||||||
|
@ -1547,7 +1544,7 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
(Type::NominalInstance(instance), Type::Callable(_))
|
(Type::NominalInstance(instance), Type::Callable(_))
|
||||||
if instance.class().is_subclass_of_any_or_unknown(db) =>
|
if instance.class.is_subclass_of_any_or_unknown(db) =>
|
||||||
{
|
{
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -1616,7 +1613,7 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
|
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
|
||||||
| (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => {
|
| (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => {
|
||||||
n.class().is_object(db) && protocol.normalized(db) == nominal
|
n.class.is_object(db) && protocol.normalized(db) == nominal
|
||||||
}
|
}
|
||||||
_ => self == other && self.is_fully_static(db) && other.is_fully_static(db),
|
_ => self == other && self.is_fully_static(db) && other.is_fully_static(db),
|
||||||
}
|
}
|
||||||
|
@ -1671,7 +1668,7 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
|
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
|
||||||
| (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => {
|
| (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => {
|
||||||
n.class().is_object(db) && protocol.normalized(db) == nominal
|
n.class.is_object(db) && protocol.normalized(db) == nominal
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -1883,7 +1880,7 @@ impl<'db> Type<'db> {
|
||||||
// member on `protocol`.
|
// member on `protocol`.
|
||||||
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
|
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
|
||||||
| (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => {
|
| (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => {
|
||||||
n.class().is_final(db) && !nominal.satisfies_protocol(db, protocol)
|
n.class.is_final(db) && !nominal.satisfies_protocol(db, protocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
|
@ -1948,7 +1945,7 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
(Type::KnownInstance(known_instance), Type::NominalInstance(instance))
|
(Type::KnownInstance(known_instance), Type::NominalInstance(instance))
|
||||||
| (Type::NominalInstance(instance), Type::KnownInstance(known_instance)) => {
|
| (Type::NominalInstance(instance), Type::KnownInstance(known_instance)) => {
|
||||||
!known_instance.is_instance_of(db, instance.class())
|
!known_instance.is_instance_of(db, instance.class)
|
||||||
}
|
}
|
||||||
|
|
||||||
(known_instance_ty @ Type::KnownInstance(_), Type::Tuple(tuple))
|
(known_instance_ty @ Type::KnownInstance(_), Type::Tuple(tuple))
|
||||||
|
@ -1960,7 +1957,7 @@ impl<'db> Type<'db> {
|
||||||
| (Type::NominalInstance(instance), Type::BooleanLiteral(..)) => {
|
| (Type::NominalInstance(instance), Type::BooleanLiteral(..)) => {
|
||||||
// A `Type::BooleanLiteral()` must be an instance of exactly `bool`
|
// A `Type::BooleanLiteral()` must be an instance of exactly `bool`
|
||||||
// (it cannot be an instance of a `bool` subclass)
|
// (it cannot be an instance of a `bool` subclass)
|
||||||
!KnownClass::Bool.is_subclass_of(db, instance.class())
|
!KnownClass::Bool.is_subclass_of(db, instance.class)
|
||||||
}
|
}
|
||||||
|
|
||||||
(Type::BooleanLiteral(..), _) | (_, Type::BooleanLiteral(..)) => true,
|
(Type::BooleanLiteral(..), _) | (_, Type::BooleanLiteral(..)) => true,
|
||||||
|
@ -1969,7 +1966,7 @@ impl<'db> Type<'db> {
|
||||||
| (Type::NominalInstance(instance), Type::IntLiteral(..)) => {
|
| (Type::NominalInstance(instance), Type::IntLiteral(..)) => {
|
||||||
// A `Type::IntLiteral()` must be an instance of exactly `int`
|
// A `Type::IntLiteral()` must be an instance of exactly `int`
|
||||||
// (it cannot be an instance of an `int` subclass)
|
// (it cannot be an instance of an `int` subclass)
|
||||||
!KnownClass::Int.is_subclass_of(db, instance.class())
|
!KnownClass::Int.is_subclass_of(db, instance.class)
|
||||||
}
|
}
|
||||||
|
|
||||||
(Type::IntLiteral(..), _) | (_, Type::IntLiteral(..)) => true,
|
(Type::IntLiteral(..), _) | (_, Type::IntLiteral(..)) => true,
|
||||||
|
@ -1981,7 +1978,7 @@ impl<'db> Type<'db> {
|
||||||
| (Type::NominalInstance(instance), Type::StringLiteral(..) | Type::LiteralString) => {
|
| (Type::NominalInstance(instance), Type::StringLiteral(..) | Type::LiteralString) => {
|
||||||
// A `Type::StringLiteral()` or a `Type::LiteralString` must be an instance of exactly `str`
|
// A `Type::StringLiteral()` or a `Type::LiteralString` must be an instance of exactly `str`
|
||||||
// (it cannot be an instance of a `str` subclass)
|
// (it cannot be an instance of a `str` subclass)
|
||||||
!KnownClass::Str.is_subclass_of(db, instance.class())
|
!KnownClass::Str.is_subclass_of(db, instance.class)
|
||||||
}
|
}
|
||||||
|
|
||||||
(Type::LiteralString, Type::LiteralString) => false,
|
(Type::LiteralString, Type::LiteralString) => false,
|
||||||
|
@ -1991,7 +1988,7 @@ impl<'db> Type<'db> {
|
||||||
| (Type::NominalInstance(instance), Type::BytesLiteral(..)) => {
|
| (Type::NominalInstance(instance), Type::BytesLiteral(..)) => {
|
||||||
// A `Type::BytesLiteral()` must be an instance of exactly `bytes`
|
// A `Type::BytesLiteral()` must be an instance of exactly `bytes`
|
||||||
// (it cannot be an instance of a `bytes` subclass)
|
// (it cannot be an instance of a `bytes` subclass)
|
||||||
!KnownClass::Bytes.is_subclass_of(db, instance.class())
|
!KnownClass::Bytes.is_subclass_of(db, instance.class)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A class-literal type `X` is always disjoint from an instance type `Y`,
|
// A class-literal type `X` is always disjoint from an instance type `Y`,
|
||||||
|
@ -2012,7 +2009,7 @@ impl<'db> Type<'db> {
|
||||||
| (Type::NominalInstance(instance), Type::FunctionLiteral(..)) => {
|
| (Type::NominalInstance(instance), Type::FunctionLiteral(..)) => {
|
||||||
// A `Type::FunctionLiteral()` must be an instance of exactly `types.FunctionType`
|
// A `Type::FunctionLiteral()` must be an instance of exactly `types.FunctionType`
|
||||||
// (it cannot be an instance of a `types.FunctionType` subclass)
|
// (it cannot be an instance of a `types.FunctionType` subclass)
|
||||||
!KnownClass::FunctionType.is_subclass_of(db, instance.class())
|
!KnownClass::FunctionType.is_subclass_of(db, instance.class)
|
||||||
}
|
}
|
||||||
|
|
||||||
(Type::BoundMethod(_), other) | (other, Type::BoundMethod(_)) => KnownClass::MethodType
|
(Type::BoundMethod(_), other) | (other, Type::BoundMethod(_)) => KnownClass::MethodType
|
||||||
|
@ -2440,7 +2437,7 @@ impl<'db> Type<'db> {
|
||||||
// i.e. Type::NominalInstance(type). So looking up a name in the MRO of
|
// i.e. Type::NominalInstance(type). So looking up a name in the MRO of
|
||||||
// `Type::NominalInstance(type)` is equivalent to looking up the name in the
|
// `Type::NominalInstance(type)` is equivalent to looking up the name in the
|
||||||
// MRO of the class `object`.
|
// MRO of the class `object`.
|
||||||
Type::NominalInstance(instance) if instance.class().is_known(db, KnownClass::Type) => {
|
Type::NominalInstance(instance) if instance.class.is_known(db, KnownClass::Type) => {
|
||||||
KnownClass::Object
|
KnownClass::Object
|
||||||
.to_class_literal(db)
|
.to_class_literal(db)
|
||||||
.find_name_in_mro_with_policy(db, name, policy)
|
.find_name_in_mro_with_policy(db, name, policy)
|
||||||
|
@ -2530,7 +2527,7 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
Type::Dynamic(_) | Type::Never => Symbol::bound(self).into(),
|
Type::Dynamic(_) | Type::Never => Symbol::bound(self).into(),
|
||||||
|
|
||||||
Type::NominalInstance(instance) => instance.class().instance_member(db, name),
|
Type::NominalInstance(instance) => instance.class.instance_member(db, name),
|
||||||
|
|
||||||
Type::ProtocolInstance(protocol) => protocol.instance_member(db, name),
|
Type::ProtocolInstance(protocol) => protocol.instance_member(db, name),
|
||||||
|
|
||||||
|
@ -2978,7 +2975,7 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
Type::NominalInstance(instance)
|
Type::NominalInstance(instance)
|
||||||
if matches!(name.as_str(), "major" | "minor")
|
if matches!(name.as_str(), "major" | "minor")
|
||||||
&& instance.class().is_known(db, KnownClass::VersionInfo) =>
|
&& instance.class.is_known(db, KnownClass::VersionInfo) =>
|
||||||
{
|
{
|
||||||
let python_version = Program::get(db).python_version(db);
|
let python_version = Program::get(db).python_version(db);
|
||||||
let segment = if name == "major" {
|
let segment = if name == "major" {
|
||||||
|
@ -3050,7 +3047,7 @@ impl<'db> Type<'db> {
|
||||||
// resolve the attribute.
|
// resolve the attribute.
|
||||||
if matches!(
|
if matches!(
|
||||||
self.into_nominal_instance()
|
self.into_nominal_instance()
|
||||||
.and_then(|instance| instance.class().known(db)),
|
.and_then(|instance| instance.class.known(db)),
|
||||||
Some(KnownClass::ModuleType | KnownClass::GenericAlias)
|
Some(KnownClass::ModuleType | KnownClass::GenericAlias)
|
||||||
) {
|
) {
|
||||||
return Symbol::Unbound.into();
|
return Symbol::Unbound.into();
|
||||||
|
@ -3309,7 +3306,7 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Type::NominalInstance(instance) => match instance.class().known(db) {
|
Type::NominalInstance(instance) => match instance.class.known(db) {
|
||||||
Some(known_class) => known_class.bool(),
|
Some(known_class) => known_class.bool(),
|
||||||
None => try_dunder_bool()?,
|
None => try_dunder_bool()?,
|
||||||
},
|
},
|
||||||
|
@ -4863,7 +4860,7 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
Type::Dynamic(_) => Ok(*self),
|
Type::Dynamic(_) => Ok(*self),
|
||||||
|
|
||||||
Type::NominalInstance(instance) => match instance.class().known(db) {
|
Type::NominalInstance(instance) => match instance.class.known(db) {
|
||||||
Some(KnownClass::TypeVar) => Ok(todo_type!(
|
Some(KnownClass::TypeVar) => Ok(todo_type!(
|
||||||
"Support for `typing.TypeVar` instances in type expressions"
|
"Support for `typing.TypeVar` instances in type expressions"
|
||||||
)),
|
)),
|
||||||
|
@ -5291,7 +5288,7 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
Self::GenericAlias(alias) => Some(TypeDefinition::Class(alias.definition(db))),
|
Self::GenericAlias(alias) => Some(TypeDefinition::Class(alias.definition(db))),
|
||||||
Self::NominalInstance(instance) => {
|
Self::NominalInstance(instance) => {
|
||||||
Some(TypeDefinition::Class(instance.class().definition(db)))
|
Some(TypeDefinition::Class(instance.class.definition(db)))
|
||||||
}
|
}
|
||||||
Self::KnownInstance(instance) => match instance {
|
Self::KnownInstance(instance) => match instance {
|
||||||
KnownInstanceType::TypeVar(var) => {
|
KnownInstanceType::TypeVar(var) => {
|
||||||
|
@ -8046,7 +8043,7 @@ impl<'db> SuperOwnerKind<'db> {
|
||||||
Either::Left(ClassBase::Dynamic(dynamic).mro(db, None))
|
Either::Left(ClassBase::Dynamic(dynamic).mro(db, None))
|
||||||
}
|
}
|
||||||
SuperOwnerKind::Class(class) => Either::Right(class.iter_mro(db)),
|
SuperOwnerKind::Class(class) => Either::Right(class.iter_mro(db)),
|
||||||
SuperOwnerKind::Instance(instance) => Either::Right(instance.class().iter_mro(db)),
|
SuperOwnerKind::Instance(instance) => Either::Right(instance.class.iter_mro(db)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8062,7 +8059,7 @@ impl<'db> SuperOwnerKind<'db> {
|
||||||
match self {
|
match self {
|
||||||
SuperOwnerKind::Dynamic(_) => None,
|
SuperOwnerKind::Dynamic(_) => None,
|
||||||
SuperOwnerKind::Class(class) => Some(class),
|
SuperOwnerKind::Class(class) => Some(class),
|
||||||
SuperOwnerKind::Instance(instance) => Some(instance.class()),
|
SuperOwnerKind::Instance(instance) => Some(instance.class),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8240,7 +8237,7 @@ impl<'db> BoundSuperType<'db> {
|
||||||
.expect("Calling `find_name_in_mro` on dynamic type should return `Some`")
|
.expect("Calling `find_name_in_mro` on dynamic type should return `Some`")
|
||||||
}
|
}
|
||||||
SuperOwnerKind::Class(class) => class,
|
SuperOwnerKind::Class(class) => class,
|
||||||
SuperOwnerKind::Instance(instance) => instance.class(),
|
SuperOwnerKind::Instance(instance) => instance.class,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (class_literal, _) = class.class_literal(db);
|
let (class_literal, _) = class.class_literal(db);
|
||||||
|
|
|
@ -614,7 +614,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||||
_ => {
|
_ => {
|
||||||
let known_instance = new_positive
|
let known_instance = new_positive
|
||||||
.into_nominal_instance()
|
.into_nominal_instance()
|
||||||
.and_then(|instance| instance.class().known(db));
|
.and_then(|instance| instance.class.known(db));
|
||||||
|
|
||||||
if known_instance == Some(KnownClass::Object) {
|
if known_instance == Some(KnownClass::Object) {
|
||||||
// `object & T` -> `T`; it is always redundant to add `object` to an intersection
|
// `object & T` -> `T`; it is always redundant to add `object` to an intersection
|
||||||
|
@ -634,7 +634,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||||
new_positive = Type::BooleanLiteral(false);
|
new_positive = Type::BooleanLiteral(false);
|
||||||
}
|
}
|
||||||
Type::NominalInstance(instance)
|
Type::NominalInstance(instance)
|
||||||
if instance.class().is_known(db, KnownClass::Bool) =>
|
if instance.class.is_known(db, KnownClass::Bool) =>
|
||||||
{
|
{
|
||||||
match new_positive {
|
match new_positive {
|
||||||
// `bool & AlwaysTruthy` -> `Literal[True]`
|
// `bool & AlwaysTruthy` -> `Literal[True]`
|
||||||
|
@ -728,7 +728,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||||
self.positive
|
self.positive
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|ty| ty.into_nominal_instance())
|
.filter_map(|ty| ty.into_nominal_instance())
|
||||||
.filter_map(|instance| instance.class().known(db))
|
.filter_map(|instance| instance.class.known(db))
|
||||||
.any(KnownClass::is_bool)
|
.any(KnownClass::is_bool)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -744,7 +744,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||||
Type::Never => {
|
Type::Never => {
|
||||||
// Adding ~Never to an intersection is a no-op.
|
// Adding ~Never to an intersection is a no-op.
|
||||||
}
|
}
|
||||||
Type::NominalInstance(instance) if instance.class().is_object(db) => {
|
Type::NominalInstance(instance) if instance.class.is_object(db) => {
|
||||||
// Adding ~object to an intersection results in Never.
|
// Adding ~object to an intersection results in Never.
|
||||||
*self = Self::default();
|
*self = Self::default();
|
||||||
self.positive.insert(Type::Never);
|
self.positive.insert(Type::Never);
|
||||||
|
|
|
@ -2733,7 +2733,7 @@ impl<'db> Type<'db> {
|
||||||
/// The type must be a specialization of the `slice` builtin type, where the specialized
|
/// The type must be a specialization of the `slice` builtin type, where the specialized
|
||||||
/// typevars are statically known integers or `None`.
|
/// typevars are statically known integers or `None`.
|
||||||
pub(crate) fn slice_literal(self, db: &'db dyn Db) -> Option<SliceLiteral> {
|
pub(crate) fn slice_literal(self, db: &'db dyn Db) -> Option<SliceLiteral> {
|
||||||
let ClassType::Generic(alias) = self.into_nominal_instance()?.class() else {
|
let ClassType::Generic(alias) = self.into_nominal_instance()?.class else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
if !alias.origin(db).is_known(db, KnownClass::Slice) {
|
if !alias.origin(db).is_known(db, KnownClass::Slice) {
|
||||||
|
@ -2747,7 +2747,7 @@ impl<'db> Type<'db> {
|
||||||
Type::IntLiteral(n) => i32::try_from(*n).map(Some).ok(),
|
Type::IntLiteral(n) => i32::try_from(*n).map(Some).ok(),
|
||||||
Type::BooleanLiteral(b) => Some(Some(i32::from(*b))),
|
Type::BooleanLiteral(b) => Some(Some(i32::from(*b))),
|
||||||
Type::NominalInstance(instance)
|
Type::NominalInstance(instance)
|
||||||
if instance.class().is_known(db, KnownClass::NoneType) =>
|
if instance.class.is_known(db, KnownClass::NoneType) =>
|
||||||
{
|
{
|
||||||
Some(None)
|
Some(None)
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ impl<'db> ClassBase<'db> {
|
||||||
}
|
}
|
||||||
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
|
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
|
||||||
Type::NominalInstance(instance)
|
Type::NominalInstance(instance)
|
||||||
if instance.class().is_known(db, KnownClass::GenericAlias) =>
|
if instance.class.is_known(db, KnownClass::GenericAlias) =>
|
||||||
{
|
{
|
||||||
Self::try_from_type(db, todo_type!("GenericAlias instance"))
|
Self::try_from_type(db, todo_type!("GenericAlias instance"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl Display for DisplayRepresentation<'_> {
|
||||||
Type::Dynamic(dynamic) => dynamic.fmt(f),
|
Type::Dynamic(dynamic) => dynamic.fmt(f),
|
||||||
Type::Never => f.write_str("Never"),
|
Type::Never => f.write_str("Never"),
|
||||||
Type::NominalInstance(instance) => {
|
Type::NominalInstance(instance) => {
|
||||||
match (instance.class(), instance.class().known(self.db)) {
|
match (instance.class, instance.class.known(self.db)) {
|
||||||
(_, Some(KnownClass::NoneType)) => f.write_str("None"),
|
(_, Some(KnownClass::NoneType)) => f.write_str("None"),
|
||||||
(_, Some(KnownClass::NoDefaultType)) => f.write_str("NoDefault"),
|
(_, Some(KnownClass::NoDefaultType)) => f.write_str("NoDefault"),
|
||||||
(ClassType::NonGeneric(class), _) => f.write_str(class.name(self.db)),
|
(ClassType::NonGeneric(class), _) => f.write_str(class.name(self.db)),
|
||||||
|
|
|
@ -2,6 +2,9 @@ use ruff_python_ast as ast;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::semantic_index::SemanticIndex;
|
use crate::semantic_index::SemanticIndex;
|
||||||
|
use crate::types::class::ClassType;
|
||||||
|
use crate::types::class_base::ClassBase;
|
||||||
|
use crate::types::instance::NominalInstanceType;
|
||||||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
declaration_type, todo_type, KnownInstanceType, Type, TypeVarBoundOrConstraints,
|
declaration_type, todo_type, KnownInstanceType, Type, TypeVarBoundOrConstraints,
|
||||||
|
@ -671,6 +674,35 @@ impl<'db> SpecializationBuilder<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
Type::NominalInstance(NominalInstanceType {
|
||||||
|
class: ClassType::Generic(formal_alias),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
Type::NominalInstance(NominalInstanceType {
|
||||||
|
class: actual_class,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
) => {
|
||||||
|
let formal_origin = formal_alias.origin(self.db);
|
||||||
|
for base in actual_class.iter_mro(self.db) {
|
||||||
|
let ClassBase::Class(ClassType::Generic(base_alias)) = base else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if formal_origin != base_alias.origin(self.db) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let formal_specialization = formal_alias.specialization(self.db).types(self.db);
|
||||||
|
let base_specialization = base_alias.specialization(self.db).types(self.db);
|
||||||
|
for (formal_ty, base_ty) in
|
||||||
|
formal_specialization.iter().zip(base_specialization)
|
||||||
|
{
|
||||||
|
self.infer(*formal_ty, *base_ty)?;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(Type::Union(formal), _) => {
|
(Type::Union(formal), _) => {
|
||||||
// TODO: We haven't implemented a full unification solver yet. If typevars appear
|
// TODO: We haven't implemented a full unification solver yet. If typevars appear
|
||||||
// in multiple union elements, we ideally want to express that _only one_ of them
|
// in multiple union elements, we ideally want to express that _only one_ of them
|
||||||
|
|
|
@ -1384,7 +1384,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
Type::BooleanLiteral(_) | Type::IntLiteral(_) => {}
|
Type::BooleanLiteral(_) | Type::IntLiteral(_) => {}
|
||||||
Type::NominalInstance(instance)
|
Type::NominalInstance(instance)
|
||||||
if matches!(
|
if matches!(
|
||||||
instance.class().known(self.db()),
|
instance.class.known(self.db()),
|
||||||
Some(KnownClass::Float | KnownClass::Int | KnownClass::Bool)
|
Some(KnownClass::Float | KnownClass::Int | KnownClass::Bool)
|
||||||
) => {}
|
) => {}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
|
@ -2477,7 +2477,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
definition: Definition<'db>,
|
definition: Definition<'db>,
|
||||||
) {
|
) {
|
||||||
fn extract_tuple_specialization<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Type<'db>> {
|
fn extract_tuple_specialization<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Type<'db>> {
|
||||||
let class = ty.into_nominal_instance()?.class();
|
let class = ty.into_nominal_instance()?.class;
|
||||||
if !class.is_known(db, KnownClass::Tuple) {
|
if !class.is_known(db, KnownClass::Tuple) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -2933,7 +2933,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Super instances do not allow attribute assignment
|
// Super instances do not allow attribute assignment
|
||||||
Type::NominalInstance(instance) if instance.class().is_known(db, KnownClass::Super) => {
|
Type::NominalInstance(instance) if instance.class.is_known(db, KnownClass::Super) => {
|
||||||
if emit_diagnostics {
|
if emit_diagnostics {
|
||||||
if let Some(builder) = self.context.report_lint(&INVALID_ASSIGNMENT, target) {
|
if let Some(builder) = self.context.report_lint(&INVALID_ASSIGNMENT, target) {
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
|
@ -3462,10 +3462,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
// Handle various singletons.
|
// Handle various singletons.
|
||||||
if let Type::NominalInstance(instance) = declared_ty.inner_type() {
|
if let Type::NominalInstance(instance) = declared_ty.inner_type() {
|
||||||
if instance
|
if instance.class.is_known(self.db(), KnownClass::SpecialForm) {
|
||||||
.class()
|
|
||||||
.is_known(self.db(), KnownClass::SpecialForm)
|
|
||||||
{
|
|
||||||
if let Some(name_expr) = target.as_name_expr() {
|
if let Some(name_expr) = target.as_name_expr() {
|
||||||
if let Some(known_instance) = KnownInstanceType::try_from_file_and_name(
|
if let Some(known_instance) = KnownInstanceType::try_from_file_and_name(
|
||||||
self.db(),
|
self.db(),
|
||||||
|
@ -6593,9 +6590,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
range,
|
range,
|
||||||
),
|
),
|
||||||
(Type::Tuple(_), Type::NominalInstance(instance))
|
(Type::Tuple(_), Type::NominalInstance(instance))
|
||||||
if instance
|
if instance.class.is_known(self.db(), KnownClass::VersionInfo) =>
|
||||||
.class()
|
|
||||||
.is_known(self.db(), KnownClass::VersionInfo) =>
|
|
||||||
{
|
{
|
||||||
self.infer_binary_type_comparison(
|
self.infer_binary_type_comparison(
|
||||||
left,
|
left,
|
||||||
|
@ -6605,9 +6600,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(Type::NominalInstance(instance), Type::Tuple(_))
|
(Type::NominalInstance(instance), Type::Tuple(_))
|
||||||
if instance
|
if instance.class.is_known(self.db(), KnownClass::VersionInfo) =>
|
||||||
.class()
|
|
||||||
.is_known(self.db(), KnownClass::VersionInfo) =>
|
|
||||||
{
|
{
|
||||||
self.infer_binary_type_comparison(
|
self.infer_binary_type_comparison(
|
||||||
Type::version_info_tuple(self.db()),
|
Type::version_info_tuple(self.db()),
|
||||||
|
@ -6999,9 +6992,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
match (value_ty, slice_ty, slice_ty.slice_literal(self.db())) {
|
match (value_ty, slice_ty, slice_ty.slice_literal(self.db())) {
|
||||||
(Type::NominalInstance(instance), _, _)
|
(Type::NominalInstance(instance), _, _)
|
||||||
if instance
|
if instance.class.is_known(self.db(), KnownClass::VersionInfo) =>
|
||||||
.class()
|
|
||||||
.is_known(self.db(), KnownClass::VersionInfo) =>
|
|
||||||
{
|
{
|
||||||
self.infer_subscript_expression_types(
|
self.infer_subscript_expression_types(
|
||||||
value_node,
|
value_node,
|
||||||
|
@ -7362,7 +7353,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let type_to_slice_argument = |ty: Option<Type<'db>>| match ty {
|
let type_to_slice_argument = |ty: Option<Type<'db>>| match ty {
|
||||||
Some(ty @ (Type::IntLiteral(_) | Type::BooleanLiteral(_))) => SliceArg::Arg(ty),
|
Some(ty @ (Type::IntLiteral(_) | Type::BooleanLiteral(_))) => SliceArg::Arg(ty),
|
||||||
Some(ty @ Type::NominalInstance(instance))
|
Some(ty @ Type::NominalInstance(instance))
|
||||||
if instance.class().is_known(self.db(), KnownClass::NoneType) =>
|
if instance.class.is_known(self.db(), KnownClass::NoneType) =>
|
||||||
{
|
{
|
||||||
SliceArg::Arg(ty)
|
SliceArg::Arg(ty)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Instance types: both nominal and structural.
|
//! Instance types: both nominal and structural.
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use super::protocol_class::ProtocolInterface;
|
use super::protocol_class::ProtocolInterface;
|
||||||
use super::{ClassType, KnownClass, SubclassOfType, Type};
|
use super::{ClassType, KnownClass, SubclassOfType, Type};
|
||||||
use crate::symbol::{Symbol, SymbolAndQualifiers};
|
use crate::symbol::{Symbol, SymbolAndQualifiers};
|
||||||
|
@ -14,7 +16,10 @@ impl<'db> Type<'db> {
|
||||||
if class.class_literal(db).0.is_protocol(db) {
|
if class.class_literal(db).0.is_protocol(db) {
|
||||||
Self::ProtocolInstance(ProtocolInstanceType(Protocol::FromClass(class)))
|
Self::ProtocolInstance(ProtocolInstanceType(Protocol::FromClass(class)))
|
||||||
} else {
|
} else {
|
||||||
Self::NominalInstance(NominalInstanceType { class })
|
Self::NominalInstance(NominalInstanceType {
|
||||||
|
class,
|
||||||
|
_phantom: PhantomData,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,16 +61,14 @@ impl<'db> Type<'db> {
|
||||||
/// A type representing the set of runtime objects which are instances of a certain nominal class.
|
/// A type representing the set of runtime objects which are instances of a certain nominal class.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update)]
|
||||||
pub struct NominalInstanceType<'db> {
|
pub struct NominalInstanceType<'db> {
|
||||||
|
pub(super) class: ClassType<'db>,
|
||||||
|
|
||||||
// Keep this field private, so that the only way of constructing `NominalInstanceType` instances
|
// Keep this field private, so that the only way of constructing `NominalInstanceType` instances
|
||||||
// is through the `Type::instance` constructor function.
|
// is through the `Type::instance` constructor function.
|
||||||
class: ClassType<'db>,
|
_phantom: PhantomData<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> NominalInstanceType<'db> {
|
impl<'db> NominalInstanceType<'db> {
|
||||||
pub(super) fn class(self) -> ClassType<'db> {
|
|
||||||
self.class
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
|
pub(super) fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
// N.B. The subclass relation is fully static
|
// N.B. The subclass relation is fully static
|
||||||
self.class.is_subclass_of(db, other.class)
|
self.class.is_subclass_of(db, other.class)
|
||||||
|
@ -130,6 +133,7 @@ impl<'db> NominalInstanceType<'db> {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
class: self.class.apply_type_mapping(db, type_mapping),
|
class: self.class.apply_type_mapping(db, type_mapping),
|
||||||
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -474,7 +474,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||||
}
|
}
|
||||||
// Treat `bool` as `Literal[True, False]`.
|
// Treat `bool` as `Literal[True, False]`.
|
||||||
Type::NominalInstance(instance)
|
Type::NominalInstance(instance)
|
||||||
if instance.class().is_known(db, KnownClass::Bool) =>
|
if instance.class.is_known(db, KnownClass::Bool) =>
|
||||||
{
|
{
|
||||||
UnionType::from_elements(
|
UnionType::from_elements(
|
||||||
db,
|
db,
|
||||||
|
@ -505,7 +505,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||||
fn evaluate_expr_ne(&mut self, lhs_ty: Type<'db>, rhs_ty: Type<'db>) -> Option<Type<'db>> {
|
fn evaluate_expr_ne(&mut self, lhs_ty: Type<'db>, rhs_ty: Type<'db>) -> Option<Type<'db>> {
|
||||||
match (lhs_ty, rhs_ty) {
|
match (lhs_ty, rhs_ty) {
|
||||||
(Type::NominalInstance(instance), Type::IntLiteral(i))
|
(Type::NominalInstance(instance), Type::IntLiteral(i))
|
||||||
if instance.class().is_known(self.db, KnownClass::Bool) =>
|
if instance.class.is_known(self.db, KnownClass::Bool) =>
|
||||||
{
|
{
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
Some(Type::BooleanLiteral(false).negate(self.db))
|
Some(Type::BooleanLiteral(false).negate(self.db))
|
||||||
|
|
|
@ -126,9 +126,7 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
||||||
(Type::SubclassOf(_), _) => Ordering::Less,
|
(Type::SubclassOf(_), _) => Ordering::Less,
|
||||||
(_, Type::SubclassOf(_)) => Ordering::Greater,
|
(_, Type::SubclassOf(_)) => Ordering::Greater,
|
||||||
|
|
||||||
(Type::NominalInstance(left), Type::NominalInstance(right)) => {
|
(Type::NominalInstance(left), Type::NominalInstance(right)) => left.class.cmp(&right.class),
|
||||||
left.class().cmp(&right.class())
|
|
||||||
}
|
|
||||||
(Type::NominalInstance(_), _) => Ordering::Less,
|
(Type::NominalInstance(_), _) => Ordering::Less,
|
||||||
(_, Type::NominalInstance(_)) => Ordering::Greater,
|
(_, Type::NominalInstance(_)) => Ordering::Greater,
|
||||||
|
|
||||||
|
@ -171,7 +169,7 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
||||||
(SuperOwnerKind::Class(_), _) => Ordering::Less,
|
(SuperOwnerKind::Class(_), _) => Ordering::Less,
|
||||||
(_, SuperOwnerKind::Class(_)) => Ordering::Greater,
|
(_, SuperOwnerKind::Class(_)) => Ordering::Greater,
|
||||||
(SuperOwnerKind::Instance(left), SuperOwnerKind::Instance(right)) => {
|
(SuperOwnerKind::Instance(left), SuperOwnerKind::Instance(right)) => {
|
||||||
left.class().cmp(&right.class())
|
left.class.cmp(&right.class)
|
||||||
}
|
}
|
||||||
(SuperOwnerKind::Instance(_), _) => Ordering::Less,
|
(SuperOwnerKind::Instance(_), _) => Ordering::Less,
|
||||||
(_, SuperOwnerKind::Instance(_)) => Ordering::Greater,
|
(_, SuperOwnerKind::Instance(_)) => Ordering::Greater,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue