Skip to content

Commit 8de49ac

Browse files
committed
Fixing rules
1 parent e33f2e9 commit 8de49ac

File tree

3 files changed

+62
-27
lines changed

3 files changed

+62
-27
lines changed

pyrefly/lib/alt/call.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ pub enum CallTarget {
6666
BoundMethod(Type, TargetWithTParams<Function>),
6767
/// A class object.
6868
Class(ClassType),
69+
/// A class object called via type[Self] (returns Self instead of ClassType).
70+
SelfClass(ClassType),
6971
/// A TypedDict.
7072
TypedDict(TypedDict),
7173
/// An overloaded function.
@@ -141,9 +143,12 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
141143
Type::ClassDef(cls) => {
142144
self.as_call_target_impl(Type::type_form(self.instantiate(&cls)), quantified)
143145
}
144-
Type::Type(box Type::ClassType(cls)) | Type::Type(box Type::SelfType(cls)) => {
146+
Type::Type(box Type::ClassType(cls)) => {
145147
Some(CallTarget::Class(cls))
146148
}
149+
Type::Type(box Type::SelfType(cls)) => {
150+
Some(CallTarget::SelfClass(cls))
151+
}
147152
Type::Type(box Type::Tuple(tuple)) => {
148153
Some(CallTarget::Class(self.erase_tuple_type(tuple)))
149154
}
@@ -633,6 +638,45 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
633638
};
634639
self.construct_class(cls, args, keywords, range, errors, context, hint)
635640
}
641+
CallTarget::SelfClass(cls) => {
642+
if cls.has_qname("typing", "Any") {
643+
return self.error(
644+
errors,
645+
range,
646+
ErrorInfo::new(ErrorKind::BadInstantiation, context),
647+
format!("`{}` can not be instantiated", cls.name()),
648+
);
649+
}
650+
if self
651+
.get_metadata_for_class(cls.class_object())
652+
.is_protocol()
653+
{
654+
self.error(
655+
errors,
656+
range,
657+
ErrorInfo::new(ErrorKind::BadInstantiation, context),
658+
format!(
659+
"Cannot instantiate `{}` because it is a protocol",
660+
cls.name()
661+
),
662+
);
663+
}
664+
if cls.has_qname("builtins", "bool") {
665+
match self.first_arg_type(args, errors) {
666+
None => (),
667+
Some(ty) => self.check_dunder_bool_is_callable(&ty, range, errors),
668+
}
669+
};
670+
// Call construct_class but wrap result in SelfType
671+
let result = self.construct_class(cls.clone(), args, keywords, range, errors, context, hint);
672+
// If construct_class returned a ClassType matching our class, convert it to SelfType
673+
match &result {
674+
Type::ClassType(result_cls) if result_cls.class_object() == cls.class_object() => {
675+
Type::SelfType(result_cls.clone())
676+
}
677+
_ => result, // Keep other return types as-is (e.g., from custom __new__)
678+
}
679+
}
636680
CallTarget::TypedDict(td) => {
637681
self.construct_typed_dict(td, args, keywords, range, errors, context, hint)
638682
}

pyrefly/lib/alt/solve.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2679,27 +2679,6 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
26792679
&|| TypeCheckContext::of_kind(TypeCheckKind::TypeGuardReturn);
26802680
self.expr(expr, hint.as_ref().map(|t| (t, tcc)), errors)
26812681
} else {
2682-
if let Some(Type::SelfType(want_class)) = hint.as_ref()
2683-
&& !self.type_order().is_final(want_class.class_object())
2684-
{
2685-
let expr_type = self.expr(expr, None, errors);
2686-
if let Type::ClassType(got_class) = &expr_type
2687-
&& got_class.class_object() == want_class.class_object()
2688-
{
2689-
self.error(
2690-
errors,
2691-
expr.range(),
2692-
ErrorInfo::Kind(ErrorKind::BadReturn),
2693-
format!(
2694-
"Returned type `{}` is not assignable to declared return type `Self@{}`",
2695-
got_class.class_object().name(),
2696-
want_class.class_object().name()
2697-
),
2698-
);
2699-
}
2700-
return expr_type;
2701-
}
2702-
27032682
let tcc: &dyn Fn() -> TypeCheckContext =
27042683
&|| TypeCheckContext::of_kind(TypeCheckKind::ExplicitFunctionReturn);
27052684
self.expr(expr, hint.as_ref().map(|t| (t, tcc)), errors)

pyrefly/lib/solver/subset.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,11 +1002,23 @@ impl<'a, Ans: LookupAnswer> Subset<'a, Ans> {
10021002
{
10031003
self.is_subset_eq(&got, want)
10041004
}
1005-
(Type::ClassType(got), Type::SelfType(want)) => ok_or(
1006-
self.type_order
1007-
.has_superclass(got.class_object(), want.class_object()),
1008-
SubsetError::Other,
1009-
),
1005+
(Type::ClassType(got), Type::SelfType(want)) => {
1006+
let has_superclass = self.type_order
1007+
.has_superclass(got.class_object(), want.class_object());
1008+
// Only apply the finality check when:
1009+
// 1. The classes are the same
1010+
// 2. Both have no type arguments (i.e., they're the exact same bare type)
1011+
// This ensures NonFinalClass is not assignable to Self@NonFinalClass,
1012+
// but allows partial[int, str] to be assignable to Self@partial
1013+
let is_same_bare_class = got.class_object() == want.class_object()
1014+
&& got.targs().is_empty()
1015+
&& want.targs().is_empty();
1016+
let is_final = self.type_order.is_final(got.class_object());
1017+
ok_or(
1018+
has_superclass && (!is_same_bare_class || is_final),
1019+
SubsetError::Other,
1020+
)
1021+
},
10101022
(Type::Type(box Type::ClassType(got)), Type::SelfType(want)) => ok_or(
10111023
self.type_order.has_metaclass(got.class_object(), want),
10121024
SubsetError::Other,

0 commit comments

Comments
 (0)