Skip to content

Commit ac9cb56

Browse files
grievejiameta-codesync[bot]
authored andcommitted
Fix Self type not bound in class body expressions
Summary: When `Self` was used in a class body expression (not an annotation), it was not being properly bound to the current class. For example: ```python class SomeClass: cache = dict[int, Self]() # Self was unbound here def get_instance(self) -> Self: x = self.cache[0] if x: return x # Error: Self not assignable to Self@SomeClass raise RuntimeError() ``` The root cause was that `ensure_expr` (used for regular expressions) did not call `track_potential_typing_self()`, while `ensure_type_internal` (used for annotations) did. This meant that `Self` usages in expressions never got a `SelfTypeLiteral` binding created, so during solving the `Self` remained as an unbound `SpecialForm::SelfType` instead of being resolved to `Type::SelfType(ClassType)`. The fix adds the call to `track_potential_typing_self()` in `ensure_expr`. Fixes #2250 Reviewed By: migeed-z Differential Revision: D91745916 fbshipit-source-id: d6e6aee90b451784de33a29fe0391edc5807f44a
1 parent c7f3338 commit ac9cb56

File tree

2 files changed

+24
-0
lines changed

2 files changed

+24
-0
lines changed

pyrefly/lib/binding/expr.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,10 @@ impl<'a> BindingsBuilder<'a> {
538538
pub fn ensure_expr(&mut self, x: &mut Expr, usage: &mut Usage) {
539539
self.with_semantic_checker(|semantic, context| semantic.visit_expr(x, context));
540540

541+
// Track uses of `typing.Self` in class bodies so they can be properly bound
542+
// to the current class during the solving phase.
543+
self.track_potential_typing_self(x);
544+
541545
match x {
542546
Expr::Attribute(attr) => {
543547
self.check_private_attribute_usage(attr);

pyrefly/lib/test/typing_self.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,23 @@ class Shape:
209209
return Shape() # should error: returns Shape, not Self
210210
"#,
211211
);
212+
213+
testcase!(
214+
test_self_in_class_body_expression,
215+
r#"
216+
from typing import Self, assert_type
217+
218+
class SomeClass:
219+
# Self in inferred class variable type
220+
cache = dict[int, Self]()
221+
222+
def get_instance(self) -> Self:
223+
x = self.cache[0]
224+
if x:
225+
return x
226+
raise RuntimeError()
227+
228+
assert_type(SomeClass().cache, dict[int, SomeClass])
229+
assert_type(SomeClass().get_instance(), SomeClass)
230+
"#,
231+
);

0 commit comments

Comments
 (0)