Skip to content

Commit c8dd5be

Browse files
committed
Fixing rules
1 parent e33f2e9 commit c8dd5be

File tree

3 files changed

+62
-29
lines changed

3 files changed

+62
-29
lines changed

pyrefly/lib/alt/call.rs

Lines changed: 45 additions & 3 deletions
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,8 @@ 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)) => {
145-
Some(CallTarget::Class(cls))
146-
}
146+
Type::Type(box Type::ClassType(cls)) => Some(CallTarget::Class(cls)),
147+
Type::Type(box Type::SelfType(cls)) => Some(CallTarget::SelfClass(cls)),
147148
Type::Type(box Type::Tuple(tuple)) => {
148149
Some(CallTarget::Class(self.erase_tuple_type(tuple)))
149150
}
@@ -633,6 +634,47 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
633634
};
634635
self.construct_class(cls, args, keywords, range, errors, context, hint)
635636
}
637+
CallTarget::SelfClass(cls) => {
638+
if cls.has_qname("typing", "Any") {
639+
return self.error(
640+
errors,
641+
range,
642+
ErrorInfo::new(ErrorKind::BadInstantiation, context),
643+
format!("`{}` can not be instantiated", cls.name()),
644+
);
645+
}
646+
if self
647+
.get_metadata_for_class(cls.class_object())
648+
.is_protocol()
649+
{
650+
self.error(
651+
errors,
652+
range,
653+
ErrorInfo::new(ErrorKind::BadInstantiation, context),
654+
format!(
655+
"Cannot instantiate `{}` because it is a protocol",
656+
cls.name()
657+
),
658+
);
659+
}
660+
if cls.has_qname("builtins", "bool") {
661+
match self.first_arg_type(args, errors) {
662+
None => (),
663+
Some(ty) => self.check_dunder_bool_is_callable(&ty, range, errors),
664+
}
665+
};
666+
let result =
667+
self.construct_class(cls.clone(), args, keywords, range, errors, context, hint);
668+
// Handle custom __new__
669+
match &result {
670+
Type::ClassType(result_cls)
671+
if result_cls.class_object() == cls.class_object() =>
672+
{
673+
Type::SelfType(result_cls.clone())
674+
}
675+
_ => result,
676+
}
677+
}
636678
CallTarget::TypedDict(td) => {
637679
self.construct_typed_dict(td, args, keywords, range, errors, context, hint)
638680
}

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)