Skip to content

Commit 32ebabc

Browse files
fangyi-zhoumeta-codesync[bot]
authored andcommitted
Implement multiple inheritance checks for instance methods (#1452)
Summary: Follow up from #1196. Closes #943. When getting the field type from the parent field, we now check for instance method types first before using the raw types. When using the raw types, the type parameters are not initialised, hence causing issues with functions that use type variables during subtyping checks. This diff adds a regression test for these cases. Pull Request resolved: #1452 Reviewed By: stroxler Differential Revision: D86374052 Pulled By: yangdanny97 fbshipit-source-id: 32bb6f9cdfcedcde0a6c310804ddbb6df4be18bb
1 parent 8076f31 commit 32ebabc

File tree

2 files changed

+23
-16
lines changed

2 files changed

+23
-16
lines changed

pyrefly/lib/alt/class/class_field.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,14 +2185,22 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
21852185
};
21862186
if let Some(parent_member) = self.get_class_member(class_object, field) {
21872187
let parent_field = Arc::unwrap_or_clone(parent_member.value.clone());
2188+
// Get instance method types for fields that are methods, otherwise use the field type directly.
2189+
let ty = self
2190+
.as_instance_attribute(
2191+
&parent_field,
2192+
&Instance::of_protocol(parent_cls, self.instantiate(cls)),
2193+
)
2194+
.as_instance_method()
2195+
.unwrap_or(field_entry.ty());
21882196
inherited_fields
21892197
.entry(field)
21902198
.or_default()
21912199
.push(InheritedFieldInfo {
21922200
class: class_object.dupe(),
21932201
metadata: parent_metadata.clone(),
21942202
field: parent_field,
2195-
ty: field_entry.ty(),
2203+
ty,
21962204
});
21972205
}
21982206
}
@@ -2206,19 +2214,6 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
22062214
.iter()
22072215
.map(|info| info.ty.clone())
22082216
.collect();
2209-
if types.iter().any(|ty| {
2210-
matches!(
2211-
ty,
2212-
Type::BoundMethod(..)
2213-
| Type::Function(..)
2214-
| Type::Forall(_)
2215-
| Type::Overload(_)
2216-
)
2217-
}) {
2218-
// TODO(fangyizhou): Handle bound methods and functions properly
2219-
// This is a leftover from https://github.com/facebook/pyrefly/pull/1196
2220-
continue;
2221-
}
22222217
let intersect = self.intersects(&types);
22232218
if matches!(intersect, Type::Never(_)) {
22242219
let mut error_msg = vec1![

pyrefly/lib/test/class_subtyping.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,15 +312,27 @@ class E(C, D): # E: Field `x` has inconsistent types inherited from multiple bas
312312
);
313313

314314
testcase!(
315-
bug = "This is currently not handled",
316315
test_multiple_inheritance_incompatible_methods,
317316
r#"
318317
class Foo:
319318
def foo(self) -> int: ...
320319
class Bar:
321320
def foo(self) -> str: ...
322321
323-
class Both(Foo, Bar): # Expect error here
322+
class Both(Foo, Bar): # E: Field `foo` has inconsistent types inherited from multiple base classes
323+
...
324+
"#,
325+
);
326+
327+
testcase!(
328+
test_multiple_inheritance_compatible_generic_methods,
329+
r#"
330+
class Foo[T1]:
331+
def foo(self) -> T1: ...
332+
class Bar[T2]:
333+
def foo(self) -> T2: ...
334+
335+
class Both[T](Foo[T], Bar[T]): # Should have no error here
324336
...
325337
"#,
326338
);

0 commit comments

Comments
 (0)