Skip to content

Commit e54903f

Browse files
committed
Fix problem with fallback classes (again)
1 parent 433ec04 commit e54903f

File tree

5 files changed

+124
-27
lines changed

5 files changed

+124
-27
lines changed

crates/ty_python_semantic/resources/mdtest/named_tuple.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,6 @@ reveal_type(Person._make(("Alice", 42))) # revealed: Unknown
277277

278278
person = Person("Alice", 42)
279279

280-
# error: [invalid-argument-type] "Argument to bound method `_asdict` is incorrect: Expected `NamedTupleFallback`, found `Person`"
281280
reveal_type(person._asdict()) # revealed: dict[str, Any]
282281
reveal_type(person._replace(name="Bob")) # revealed: Person
283282
```

crates/ty_python_semantic/src/types.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5321,12 +5321,10 @@ impl<'db> Type<'db> {
53215321
Some(generic_context) => (
53225322
Some(class),
53235323
Some(generic_context),
5324-
Type::from(class.apply_specialization(db, |_| {
5325-
// It is important that identity_specialization specializes the class with
5326-
// _inferable_ typevars, so that our specialization inference logic will
5327-
// try to find a specialization for them.
5328-
generic_context.identity_specialization(db)
5329-
})),
5324+
// It is important that identity_specialization specializes the class with
5325+
// _inferable_ typevars, so that our specialization inference logic will
5326+
// try to find a specialization for them.
5327+
Type::from(class.identity_specialization(db, Type::TypeVar)),
53305328
),
53315329
_ => (None, None, self),
53325330
},

crates/ty_python_semantic/src/types/class.rs

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,18 @@ impl<'db> ClassLiteral<'db> {
15271527
})
15281528
}
15291529

1530+
/// Returns a specialization of this class where each typevar is mapped to itself. The second
1531+
/// parameter can be `Type::TypeVar` or `Type::NonInferableTypeVar`, depending on the use case.
1532+
pub(crate) fn identity_specialization(
1533+
self,
1534+
db: &'db dyn Db,
1535+
typevar_to_type: impl Fn(BoundTypeVarInstance<'db>) -> Type<'db>,
1536+
) -> ClassType<'db> {
1537+
self.apply_specialization(db, |generic_context| {
1538+
generic_context.identity_specialization(db, typevar_to_type)
1539+
})
1540+
}
1541+
15301542
/// Return an iterator over the inferred types of this class's *explicit* bases.
15311543
///
15321544
/// Note that any class (except for `object`) that has no explicit
@@ -2625,7 +2637,14 @@ impl<'db> ClassLiteral<'db> {
26252637
.map_type(|ty|
26262638
ty.apply_type_mapping(
26272639
db,
2628-
&TypeMapping::ReplaceSelf {new_upper_bound: determine_upper_bound(db, self, specialization, ClassBase::is_typed_dict) }
2640+
&TypeMapping::ReplaceSelf {
2641+
new_upper_bound: determine_upper_bound(
2642+
db,
2643+
self,
2644+
specialization,
2645+
ClassBase::is_typed_dict
2646+
)
2647+
}
26292648
)
26302649
)
26312650
}
@@ -4167,6 +4186,90 @@ impl KnownClass {
41674186
}
41684187
}
41694188

4189+
/// Return `true` if this class is a typeshed fallback class which is used to provide attributes and
4190+
/// methods for another type (e.g. `NamedTupleFallback` for actual `NamedTuple`s). These fallback
4191+
/// classes need special treatment in some places. For example, implicit usages of `Self` should not
4192+
/// be eagerly replaced with the fallback class itself. Instead, `Self` should eventually be treated
4193+
/// as referring to the destination type (e.g. the actual `NamedTuple`).
4194+
pub(crate) const fn is_fallback_class(self) -> bool {
4195+
match self {
4196+
KnownClass::Bool
4197+
| KnownClass::Object
4198+
| KnownClass::Bytes
4199+
| KnownClass::Bytearray
4200+
| KnownClass::Type
4201+
| KnownClass::Int
4202+
| KnownClass::Float
4203+
| KnownClass::Complex
4204+
| KnownClass::Str
4205+
| KnownClass::List
4206+
| KnownClass::Tuple
4207+
| KnownClass::Set
4208+
| KnownClass::FrozenSet
4209+
| KnownClass::Dict
4210+
| KnownClass::Slice
4211+
| KnownClass::Property
4212+
| KnownClass::BaseException
4213+
| KnownClass::Exception
4214+
| KnownClass::BaseExceptionGroup
4215+
| KnownClass::ExceptionGroup
4216+
| KnownClass::Staticmethod
4217+
| KnownClass::Classmethod
4218+
| KnownClass::Super
4219+
| KnownClass::Enum
4220+
| KnownClass::EnumType
4221+
| KnownClass::Auto
4222+
| KnownClass::Member
4223+
| KnownClass::Nonmember
4224+
| KnownClass::StrEnum
4225+
| KnownClass::ABCMeta
4226+
| KnownClass::GenericAlias
4227+
| KnownClass::ModuleType
4228+
| KnownClass::FunctionType
4229+
| KnownClass::MethodType
4230+
| KnownClass::MethodWrapperType
4231+
| KnownClass::WrapperDescriptorType
4232+
| KnownClass::UnionType
4233+
| KnownClass::GeneratorType
4234+
| KnownClass::AsyncGeneratorType
4235+
| KnownClass::CoroutineType
4236+
| KnownClass::NotImplementedType
4237+
| KnownClass::BuiltinFunctionType
4238+
| KnownClass::EllipsisType
4239+
| KnownClass::NoneType
4240+
| KnownClass::Awaitable
4241+
| KnownClass::Generator
4242+
| KnownClass::Deprecated
4243+
| KnownClass::StdlibAlias
4244+
| KnownClass::SpecialForm
4245+
| KnownClass::TypeVar
4246+
| KnownClass::ParamSpec
4247+
| KnownClass::ParamSpecArgs
4248+
| KnownClass::ParamSpecKwargs
4249+
| KnownClass::ProtocolMeta
4250+
| KnownClass::TypeVarTuple
4251+
| KnownClass::TypeAliasType
4252+
| KnownClass::NoDefaultType
4253+
| KnownClass::NewType
4254+
| KnownClass::SupportsIndex
4255+
| KnownClass::Iterable
4256+
| KnownClass::Iterator
4257+
| KnownClass::ChainMap
4258+
| KnownClass::Counter
4259+
| KnownClass::DefaultDict
4260+
| KnownClass::Deque
4261+
| KnownClass::OrderedDict
4262+
| KnownClass::VersionInfo
4263+
| KnownClass::Field
4264+
| KnownClass::KwOnly
4265+
| KnownClass::NamedTupleLike
4266+
| KnownClass::Template
4267+
| KnownClass::ConstraintSet
4268+
| KnownClass::InitVar => false,
4269+
KnownClass::NamedTupleFallback | KnownClass::TypedDictFallback => true,
4270+
}
4271+
}
4272+
41704273
pub(crate) fn name(self, db: &dyn Db) -> &'static str {
41714274
match self {
41724275
Self::Bool => "bool",

crates/ty_python_semantic/src/types/generics.rs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -109,28 +109,21 @@ pub(crate) fn get_self_type<'db>(
109109
typevar_binding_context: Option<Definition<'db>>,
110110
class: ClassLiteral<'db>,
111111
) -> Option<BoundTypeVarInstance<'db>> {
112-
// TODO: remove duplication with Type::in_type_expression
113112
let module = parsed_module(db, scope_id.file(db)).load(db);
114113
let index = semantic_index(db, scope_id.file(db));
115114

116-
let upper_bound = Type::instance(
117-
db,
118-
class.apply_specialization(db, |generic_context| {
119-
let types = generic_context
120-
.variables(db)
121-
.iter()
122-
.map(|typevar| Type::NonInferableTypeVar(*typevar));
123-
124-
generic_context.specialize(db, types.collect())
125-
}),
126-
);
127-
128115
let class_definition = class.definition(db);
129116
let typevar = TypeVarInstance::new(
130117
db,
131118
ast::name::Name::new_static("Self"),
132119
Some(class_definition),
133-
Some(TypeVarBoundOrConstraints::UpperBound(upper_bound).into()),
120+
Some(
121+
TypeVarBoundOrConstraints::UpperBound(Type::instance(
122+
db,
123+
class.identity_specialization(db, Type::NonInferableTypeVar),
124+
))
125+
.into(),
126+
),
134127
// According to the [spec], we can consider `Self`
135128
// equivalent to an invariant type variable
136129
// [spec]: https://typing.python.org/en/latest/spec/generics.html#self
@@ -336,14 +329,17 @@ impl<'db> GenericContext<'db> {
336329
}
337330

338331
/// Returns a specialization of this generic context where each typevar is mapped to itself.
339-
/// (And in particular, to an _inferable_ version of itself, since this will be used in our
340-
/// class constructor invocation machinery to infer a specialization for the class from the
341-
/// arguments passed to its constructor.)
342-
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
332+
/// The second parameter can be `Type::TypeVar` or `Type::NonInferableTypeVar`, depending on
333+
/// the use case.
334+
pub(crate) fn identity_specialization(
335+
self,
336+
db: &'db dyn Db,
337+
typevar_to_type: impl Fn(BoundTypeVarInstance<'db>) -> Type<'db>,
338+
) -> Specialization<'db> {
343339
let types = self
344340
.variables(db)
345341
.iter()
346-
.map(|typevar| Type::TypeVar(*typevar))
342+
.map(|typevar| typevar_to_type(*typevar))
347343
.collect();
348344
self.specialize(db, types)
349345
}

crates/ty_python_semantic/src/types/signatures.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,7 @@ impl<'db> Parameters<'db> {
12881288
});
12891289
let implicit_annotation = if !method_has_self_in_generic_context
12901290
&& class.is_not_generic()
1291+
&& !class.known(db).is_some_and(KnownClass::is_fallback_class)
12911292
{
12921293
Type::instance(db, class)
12931294
} else {

0 commit comments

Comments
 (0)