Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ def _(x: tuple[int, str], y: tuple[None, tuple[int]]):

```py
def _(x: tuple[int, ...], y: tuple[str, ...]):
# TODO: should be `tuple[int | str, ...]`
reveal_type(x + y) # revealed: tuple[int | Unknown, ...]
reveal_type(x + y) # revealed: tuple[int | str, ...]
reveal_type(x + (1, 2)) # revealed: tuple[int, ...]
```
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,12 @@ def takes_in_protocol(x: CanIndex[T]) -> T:
return x[0]

def deep_list(x: list[str]) -> None:
# TODO: revealed: list[str]
reveal_type(takes_in_list(x)) # revealed: list[Unknown]
reveal_type(takes_in_list(x)) # revealed: list[str]
# TODO: revealed: str
reveal_type(takes_in_protocol(x)) # revealed: Unknown

def deeper_list(x: list[set[str]]) -> None:
# TODO: revealed: list[set[str]]
reveal_type(takes_in_list(x)) # revealed: list[Unknown]
reveal_type(takes_in_list(x)) # revealed: list[set[str]]
# TODO: revealed: set[str]
reveal_type(takes_in_protocol(x)) # revealed: Unknown

Expand All @@ -119,13 +117,11 @@ This also works when passing in arguments that are subclasses of the parameter t
class Sub(list[int]): ...
class GenericSub(list[T]): ...

# TODO: revealed: list[int]
reveal_type(takes_in_list(Sub())) # revealed: list[Unknown]
reveal_type(takes_in_list(Sub())) # revealed: list[int]
# TODO: revealed: int
reveal_type(takes_in_protocol(Sub())) # revealed: Unknown

# TODO: revealed: list[str]
reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[Unknown]
reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[str]
# TODO: revealed: str
reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,12 @@ def takes_in_protocol[T](x: CanIndex[T]) -> T:
return x[0]

def deep_list(x: list[str]) -> None:
# TODO: revealed: list[str]
reveal_type(takes_in_list(x)) # revealed: list[Unknown]
reveal_type(takes_in_list(x)) # revealed: list[str]
# TODO: revealed: str
reveal_type(takes_in_protocol(x)) # revealed: Unknown

def deeper_list(x: list[set[str]]) -> None:
# TODO: revealed: list[set[str]]
reveal_type(takes_in_list(x)) # revealed: list[Unknown]
reveal_type(takes_in_list(x)) # revealed: list[set[str]]
# TODO: revealed: set[str]
reveal_type(takes_in_protocol(x)) # revealed: Unknown

Expand All @@ -114,13 +112,11 @@ This also works when passing in arguments that are subclasses of the parameter t
class Sub(list[int]): ...
class GenericSub[T](list[T]): ...

# TODO: revealed: list[int]
reveal_type(takes_in_list(Sub())) # revealed: list[Unknown]
reveal_type(takes_in_list(Sub())) # revealed: list[int]
# TODO: revealed: int
reveal_type(takes_in_protocol(Sub())) # revealed: Unknown

# TODO: revealed: list[str]
reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[Unknown]
reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[str]
# TODO: revealed: str
reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown

Expand Down
57 changes: 27 additions & 30 deletions crates/ty_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,25 +562,22 @@ impl<'db> Type<'db> {

fn is_none(&self, db: &'db dyn Db) -> bool {
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 {
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 {
self.into_nominal_instance().is_some_and(|instance| {
instance
.class()
.is_known(db, KnownClass::NotImplementedType)
})
self.into_nominal_instance()
.is_some_and(|instance| instance.class.is_known(db, KnownClass::NotImplementedType))
}

pub fn is_object(&self, db: &'db dyn Db) -> bool {
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 {
Expand Down Expand Up @@ -1063,7 +1060,7 @@ impl<'db> Type<'db> {
(_, Type::Never) => false,

// 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:
// 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`.
Expand Down Expand Up @@ -1373,7 +1370,7 @@ impl<'db> Type<'db> {

// All types are assignable to `object`.
// 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:
// 1. `T` is a bound TypeVar and `T`'s upper bound is assignable to `S`.
Expand Down Expand Up @@ -1547,7 +1544,7 @@ impl<'db> Type<'db> {
}

(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
}
Expand Down Expand Up @@ -1616,7 +1613,7 @@ impl<'db> Type<'db> {
}
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
| (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),
}
Expand Down Expand Up @@ -1671,7 +1668,7 @@ impl<'db> Type<'db> {
}
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
| (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,
}
Expand Down Expand Up @@ -1883,7 +1880,7 @@ impl<'db> Type<'db> {
// member on `protocol`.
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
| (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)
}

(
Expand Down Expand Up @@ -1948,7 +1945,7 @@ impl<'db> Type<'db> {

(Type::KnownInstance(known_instance), Type::NominalInstance(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))
Expand All @@ -1960,7 +1957,7 @@ impl<'db> Type<'db> {
| (Type::NominalInstance(instance), Type::BooleanLiteral(..)) => {
// A `Type::BooleanLiteral()` must be an instance of exactly `bool`
// (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,
Expand All @@ -1969,7 +1966,7 @@ impl<'db> Type<'db> {
| (Type::NominalInstance(instance), Type::IntLiteral(..)) => {
// A `Type::IntLiteral()` must be an instance of exactly `int`
// (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,
Expand All @@ -1981,7 +1978,7 @@ impl<'db> Type<'db> {
| (Type::NominalInstance(instance), Type::StringLiteral(..) | Type::LiteralString) => {
// A `Type::StringLiteral()` or a `Type::LiteralString` must be an instance of exactly `str`
// (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,
Expand All @@ -1991,7 +1988,7 @@ impl<'db> Type<'db> {
| (Type::NominalInstance(instance), Type::BytesLiteral(..)) => {
// A `Type::BytesLiteral()` must be an instance of exactly `bytes`
// (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`,
Expand All @@ -2012,7 +2009,7 @@ impl<'db> Type<'db> {
| (Type::NominalInstance(instance), Type::FunctionLiteral(..)) => {
// A `Type::FunctionLiteral()` must be an instance of exactly `types.FunctionType`
// (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
Expand Down Expand Up @@ -2440,7 +2437,7 @@ impl<'db> Type<'db> {
// 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
// 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
.to_class_literal(db)
.find_name_in_mro_with_policy(db, name, policy)
Expand Down Expand Up @@ -2530,7 +2527,7 @@ impl<'db> Type<'db> {

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),

Expand Down Expand Up @@ -2978,7 +2975,7 @@ impl<'db> Type<'db> {

Type::NominalInstance(instance)
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 segment = if name == "major" {
Expand Down Expand Up @@ -3050,7 +3047,7 @@ impl<'db> Type<'db> {
// resolve the attribute.
if matches!(
self.into_nominal_instance()
.and_then(|instance| instance.class().known(db)),
.and_then(|instance| instance.class.known(db)),
Some(KnownClass::ModuleType | KnownClass::GenericAlias)
) {
return Symbol::Unbound.into();
Expand Down Expand Up @@ -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(),
None => try_dunder_bool()?,
},
Expand Down Expand Up @@ -4863,7 +4860,7 @@ impl<'db> Type<'db> {

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!(
"Support for `typing.TypeVar` instances in type expressions"
)),
Expand Down Expand Up @@ -5291,7 +5288,7 @@ impl<'db> Type<'db> {
}
Self::GenericAlias(alias) => Some(TypeDefinition::Class(alias.definition(db))),
Self::NominalInstance(instance) => {
Some(TypeDefinition::Class(instance.class().definition(db)))
Some(TypeDefinition::Class(instance.class.definition(db)))
}
Self::KnownInstance(instance) => match instance {
KnownInstanceType::TypeVar(var) => {
Expand Down Expand Up @@ -8046,7 +8043,7 @@ impl<'db> SuperOwnerKind<'db> {
Either::Left(ClassBase::Dynamic(dynamic).mro(db, None))
}
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)),
}
}

Expand All @@ -8062,7 +8059,7 @@ impl<'db> SuperOwnerKind<'db> {
match self {
SuperOwnerKind::Dynamic(_) => None,
SuperOwnerKind::Class(class) => Some(class),
SuperOwnerKind::Instance(instance) => Some(instance.class()),
SuperOwnerKind::Instance(instance) => Some(instance.class),
}
}

Expand Down Expand Up @@ -8240,7 +8237,7 @@ impl<'db> BoundSuperType<'db> {
.expect("Calling `find_name_in_mro` on dynamic type should return `Some`")
}
SuperOwnerKind::Class(class) => class,
SuperOwnerKind::Instance(instance) => instance.class(),
SuperOwnerKind::Instance(instance) => instance.class,
};

let (class_literal, _) = class.class_literal(db);
Expand Down
8 changes: 4 additions & 4 deletions crates/ty_python_semantic/src/types/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
_ => {
let known_instance = new_positive
.into_nominal_instance()
.and_then(|instance| instance.class().known(db));
.and_then(|instance| instance.class.known(db));

if known_instance == Some(KnownClass::Object) {
// `object & T` -> `T`; it is always redundant to add `object` to an intersection
Expand All @@ -634,7 +634,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
new_positive = Type::BooleanLiteral(false);
}
Type::NominalInstance(instance)
if instance.class().is_known(db, KnownClass::Bool) =>
if instance.class.is_known(db, KnownClass::Bool) =>
{
match new_positive {
// `bool & AlwaysTruthy` -> `Literal[True]`
Expand Down Expand Up @@ -728,7 +728,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
self.positive
.iter()
.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)
};

Expand All @@ -744,7 +744,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
Type::Never => {
// 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.
*self = Self::default();
self.positive.insert(Type::Never);
Expand Down
4 changes: 2 additions & 2 deletions crates/ty_python_semantic/src/types/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2733,7 +2733,7 @@ impl<'db> Type<'db> {
/// The type must be a specialization of the `slice` builtin type, where the specialized
/// typevars are statically known integers or `None`.
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;
};
if !alias.origin(db).is_known(db, KnownClass::Slice) {
Expand All @@ -2747,7 +2747,7 @@ impl<'db> Type<'db> {
Type::IntLiteral(n) => i32::try_from(*n).map(Some).ok(),
Type::BooleanLiteral(b) => Some(Some(i32::from(*b))),
Type::NominalInstance(instance)
if instance.class().is_known(db, KnownClass::NoneType) =>
if instance.class.is_known(db, KnownClass::NoneType) =>
{
Some(None)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_python_semantic/src/types/class_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl<'db> ClassBase<'db> {
}
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
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"))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_python_semantic/src/types/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl Display for DisplayRepresentation<'_> {
Type::Dynamic(dynamic) => dynamic.fmt(f),
Type::Never => f.write_str("Never"),
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::NoDefaultType)) => f.write_str("NoDefault"),
(ClassType::NonGeneric(class), _) => f.write_str(class.name(self.db)),
Expand Down
Loading
Loading