Skip to content

Commit 40fd1ba

Browse files
samwgoldmanfacebook-github-bot
authored andcommitted
Add Self and type[Self] attribute base
Summary: When we look up a method on `self`, we need to create a bound method with the bound receiver of `self`. We determine the bound receiver based on the attribute base, so this diff introduces new attribute bases. Reviewed By: stroxler Differential Revision: D80893392 fbshipit-source-id: 01fec4ef3aef7ccb61accce566f31ca8e5e95616
1 parent 099d990 commit 40fd1ba

File tree

5 files changed

+50
-33
lines changed

5 files changed

+50
-33
lines changed

conformance/third_party/conformance.exp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5527,16 +5527,6 @@
55275527
}
55285528
],
55295529
"generics_self_advanced.py": [
5530-
{
5531-
"code": -2,
5532-
"column": 20,
5533-
"concise_description": "assert_type(ChildB, Self) failed",
5534-
"description": "assert_type(ChildB, Self) failed",
5535-
"line": 38,
5536-
"name": "assert-type",
5537-
"stop_column": 42,
5538-
"stop_line": 38
5539-
},
55405530
{
55415531
"code": -2,
55425532
"column": 20,
@@ -5576,16 +5566,6 @@
55765566
"name": "missing-attribute",
55775567
"stop_column": 26,
55785568
"stop_line": 44
5579-
},
5580-
{
5581-
"code": -2,
5582-
"column": 20,
5583-
"concise_description": "assert_type(ChildB, Self) failed",
5584-
"description": "assert_type(ChildB, Self) failed",
5585-
"line": 45,
5586-
"name": "assert-type",
5587-
"stop_column": 41,
5588-
"stop_line": 45
55895569
}
55905570
],
55915571
"generics_self_attributes.py": [

conformance/third_party/conformance.result

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,8 @@
204204
"Line 96: Expected 1 errors"
205205
],
206206
"generics_self_advanced.py": [
207-
"Line 38: Unexpected errors ['assert_type(ChildB, Self) failed']",
208207
"Line 43: Unexpected errors ['assert_type(Any, list[Self]) failed', 'Instance-only attribute `a` of class `ChildB` is not visible on the class']",
209-
"Line 44: Unexpected errors ['assert_type(Any, Self) failed', 'Instance-only attribute `a` of class `ChildB` is not visible on the class']",
210-
"Line 45: Unexpected errors ['assert_type(ChildB, Self) failed']"
208+
"Line 44: Unexpected errors ['assert_type(Any, Self) failed', 'Instance-only attribute `a` of class `ChildB` is not visible on the class']"
211209
],
212210
"generics_self_attributes.py": [
213211
"Line 32: Expected 1 errors"

conformance/third_party/results.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"pass": 81,
44
"fail": 57,
55
"pass_rate": 0.59,
6-
"differences": 262,
6+
"differences": 260,
77
"passing": [
88
"aliases_explicit.py",
99
"aliases_newtype.py",
@@ -112,7 +112,7 @@
112112
"generics_defaults_referential.py": 3,
113113
"generics_paramspec_components.py": 1,
114114
"generics_scoping.py": 8,
115-
"generics_self_advanced.py": 4,
115+
"generics_self_advanced.py": 2,
116116
"generics_self_attributes.py": 1,
117117
"generics_self_basic.py": 2,
118118
"generics_self_protocols.py": 1,

pyrefly/lib/alt/attr.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,8 @@ enum AttributeBase {
509509
/// Properties are handled via a special case so that we can understand
510510
/// setter decorators.
511511
Property(Type),
512+
/// Attribute access on `Self` from inside a class
513+
SelfType(ClassType),
512514
/// Result of a super() call. See Type::SuperInstance for details on what these fields are.
513515
SuperInstance(ClassType, SuperObj),
514516
/// Typed dictionaries have similar properties to dict and Mapping, with some exceptions
@@ -524,6 +526,7 @@ pub enum ClassBase {
524526
ClassDef(Class),
525527
ClassType(ClassType),
526528
Quantified(Quantified, ClassType),
529+
SelfType(ClassType),
527530
}
528531

529532
impl ClassBase {
@@ -532,14 +535,16 @@ impl ClassBase {
532535
ClassBase::ClassDef(c) => c,
533536
ClassBase::ClassType(c) => c.class_object(),
534537
ClassBase::Quantified(_, c) => c.class_object(),
538+
ClassBase::SelfType(c) => c.class_object(),
535539
}
536540
}
537541

538542
pub fn targs(&self) -> Option<&TArgs> {
539543
match self {
540544
ClassBase::ClassDef(..) => None,
541-
ClassBase::ClassType(c) => Some(c.targs()),
542-
ClassBase::Quantified(_, c) => Some(c.targs()),
545+
ClassBase::ClassType(c) | ClassBase::Quantified(_, c) | ClassBase::SelfType(c) => {
546+
Some(c.targs())
547+
}
543548
}
544549
}
545550

@@ -548,6 +553,7 @@ impl ClassBase {
548553
ClassBase::ClassDef(c) => Type::ClassDef(c),
549554
ClassBase::ClassType(c) => Type::Type(Box::new(c.to_type())),
550555
ClassBase::Quantified(q, _) => Type::type_form(q.to_type()),
556+
ClassBase::SelfType(c) => Type::type_form(Type::SelfType(c)),
551557
}
552558
}
553559
}
@@ -1550,6 +1556,13 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
15501556
)),
15511557
}
15521558
}
1559+
AttributeBase::SelfType(cls) => match self.get_self_attribute(&cls, attr_name) {
1560+
Some(attr) => LookupResult::found(attr),
1561+
None => LookupResult::not_found(NotFoundOn::ClassInstance(
1562+
cls.class_object().dupe(),
1563+
base_copy,
1564+
)),
1565+
},
15531566
}
15541567
}
15551568

@@ -1579,6 +1592,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
15791592
}
15801593
}
15811594
AttributeBase::ClassInstance(cls)
1595+
| AttributeBase::SelfType(cls)
15821596
| AttributeBase::EnumLiteral(LitEnum { class: cls, .. })
15831597
| AttributeBase::Quantified(_, Some(cls))
15841598
| AttributeBase::SuperInstance(cls, _)
@@ -1755,10 +1769,10 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
17551769
match ty {
17561770
Type::ClassType(class_type) => Some(AttributeBase::ClassInstance(class_type)),
17571771
Type::ClassDef(cls) => Some(AttributeBase::ClassObject(ClassBase::ClassDef(cls))),
1758-
Type::SelfType(class_type) => Some(AttributeBase::ClassInstance(class_type)),
1759-
Type::Type(box Type::SelfType(class_type)) => Some(AttributeBase::ClassObject(
1760-
ClassBase::ClassType(class_type.clone()),
1761-
)),
1772+
Type::SelfType(class_type) => Some(AttributeBase::SelfType(class_type)),
1773+
Type::Type(box Type::SelfType(class_type)) => {
1774+
Some(AttributeBase::ClassObject(ClassBase::SelfType(class_type)))
1775+
}
17621776
Type::TypedDict(td) | Type::PartialTypedDict(td) => {
17631777
Some(AttributeBase::TypedDict(td.clone()))
17641778
}
@@ -2210,6 +2224,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
22102224
) {
22112225
match &base {
22122226
AttributeBase::ClassInstance(class)
2227+
| AttributeBase::SelfType(class)
22132228
| AttributeBase::EnumLiteral(LitEnum { class, .. })
22142229
| AttributeBase::Quantified(_, Some(class)) => {
22152230
self.completions_class_type(class, expected_attribute_name, res)

pyrefly/lib/alt/class/class_field.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ enum InstanceKind {
527527
ClassType,
528528
TypedDict,
529529
TypeVar(Quantified),
530+
SelfType,
530531
}
531532

532533
/// Wrapper to hold a specialized instance of a class , unifying ClassType and TypedDict.
@@ -561,6 +562,14 @@ impl<'a> Instance<'a> {
561562
}
562563
}
563564

565+
fn of_self_type(cls: &'a ClassType) -> Self {
566+
Self {
567+
kind: InstanceKind::SelfType,
568+
class: cls.class_object(),
569+
targs: cls.targs(),
570+
}
571+
}
572+
564573
/// Instantiate a type that is relative to the class type parameters
565574
/// by substituting in the type arguments.
566575
fn instantiate_member(&self, raw_member: Type) -> Type {
@@ -576,13 +585,21 @@ impl<'a> Instance<'a> {
576585
Type::TypedDict(TypedDict::new(self.class.dupe(), self.targs.clone()))
577586
}
578587
InstanceKind::TypeVar(q) => q.clone().to_type(),
588+
InstanceKind::SelfType => {
589+
Type::SelfType(ClassType::new(self.class.dupe(), self.targs.clone()))
590+
}
579591
}
580592
}
581593

582594
/// Looking up a classmethod/staticmethod from an instance base has class-like
583595
/// lookup behavior. When this happens, we convert from an instance base to a class base.
584596
fn to_class_base(&self) -> ClassBase {
585-
ClassBase::ClassType(ClassType::new(self.class.dupe(), self.targs.clone()))
597+
match self.kind {
598+
InstanceKind::SelfType => {
599+
ClassBase::SelfType(ClassType::new(self.class.dupe(), self.targs.clone()))
600+
}
601+
_ => ClassBase::ClassType(ClassType::new(self.class.dupe(), self.targs.clone())),
602+
}
586603
}
587604
}
588605

@@ -1381,7 +1398,8 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
13811398
} if (descriptor_getter.is_some() || descriptor_setter.is_some())
13821399
// There's no situation in which you can stick a usable descriptor in a TypedDict.
13831400
// TODO(rechen): a descriptor in a TypedDict should be an error at class creation time.
1384-
&& instance.kind == InstanceKind::ClassType =>
1401+
&& (instance.kind == InstanceKind::ClassType
1402+
|| instance.kind == InstanceKind::SelfType) =>
13851403
{
13861404
Attribute::descriptor(
13871405
ty,
@@ -1535,6 +1553,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
15351553
ClassBase::ClassDef(c) => self.instantiate(c),
15361554
ClassBase::ClassType(c) => c.clone().to_type(),
15371555
ClassBase::Quantified(q, _) => q.clone().to_type(),
1556+
ClassBase::SelfType(c) => Type::SelfType(c.clone()),
15381557
};
15391558
foralled.subst_self_type_mut(&replacement, &|a, b| self.is_subset_eq(a, b));
15401559
Some(bind_class_attribute(cls, foralled, &None))
@@ -1831,6 +1850,11 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
18311850
.map(|member| self.as_instance_attribute(&member.value, &Instance::of_class(cls)))
18321851
}
18331852

1853+
pub fn get_self_attribute(&self, cls: &ClassType, name: &Name) -> Option<Attribute> {
1854+
self.get_class_member(cls.class_object(), name)
1855+
.map(|member| self.as_instance_attribute(&member.value, &Instance::of_self_type(cls)))
1856+
}
1857+
18341858
pub fn get_bounded_quantified_attribute(
18351859
&self,
18361860
quantified: Quantified,

0 commit comments

Comments
 (0)