From 554e39366adb1db0710bc24bee6b11a95b79759e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 15:28:40 +0000 Subject: [PATCH 01/23] Add failing test cases --- mypyc/test-data/run-classes.test | 154 +++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 2af519dc7aa8..ac7014f62a55 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -1968,6 +1968,160 @@ import other_interpreted [out] +[case testAttributeOverridesProperty1] +from typing import Any +from mypy_extensions import trait + +@trait +class T1: + @property + def x(self) -> int: ... + @property + def y(self) -> int: ... + +class C(T1): + x: int + y: int = 4 + +def test_read_only_property_implemented_as_attribute() -> None: + c = C() + c.x = 5 + assert c.x == 5 + assert c.y == 4 + c.y = 6 + assert c.y == 6 + t: T1 = C() + assert t.y == 4 + t = c + assert t.x == 5 + assert t.y == 6 + a: Any = c + assert c.x == 5 + assert c.y == 6 + c.x = 7 + c.y = 8 + assert c.x == 7 + assert c.y == 8 + +[case testAttributeOverridesProperty2] +from typing import Any +from mypy_extensions import trait + +class B: + @property + def x(self) -> int: + return 11 + + @property + def y(self) -> int: + return 22 + +class C(B): + x: int + y: int = 4 + +def test_read_only_property_implemented_as_attribute() -> None: + c = C() + c.x = 5 + assert c.x == 5 + assert c.y == 4 + c.y = 6 + assert c.y == 6 + b: B = C() + assert b.y == 4 + b = c + assert b.x == 5 + assert b.y == 6 + a: Any = c + assert c.x == 5 + assert c.y == 6 + c.x = 7 + c.y = 8 + assert c.x == 7 + assert c.y == 8 + +[case testAttributeOverridesProperty3] +from typing import Any +from mypy_extensions import trait + +@trait +class T1: + @property + def x(self) -> int: ... + @property + def y(self) -> int: ... + +class B: + x: int + y: int = 4 + +class C(B, T1): + pass + +def test_read_only_property_implemented_as_attribute() -> None: + c = C() + c.x = 5 + assert c.x == 5 + assert c.y == 4 + c.y = 6 + assert c.y == 6 + t: T1 = C() + assert t.y == 4 + t = c + assert t.x == 5 + assert t.y == 6 + a: Any = c + assert c.x == 5 + assert c.y == 6 + c.x = 7 + c.y = 8 + assert c.x == 7 + assert c.y == 8 + + +[case testAttributeOverridesProperty4] +from typing import Any +from mypy_extensions import trait + +@trait +class T1: + @property + def x(self) -> int: ... + @x.setter + def x(self, v: int) -> None: ... + + @property + def y(self) -> int: ... + @y.setter + def y(self, v: int) -> None: ... + +class C(T1): + x: int + y: int = 4 + +def test_read_only_property_implemented_as_attribute() -> None: + c = C() + c.x = 5 + assert c.x == 5 + assert c.y == 4 + c.y = 6 + assert c.y == 6 + t: T1 = C() + assert t.y == 4 + t.x = 5 + assert t.x == 5 + t.y = 6 + assert t.y == 6 + a: Any = c + assert c.x == 5 + assert c.y == 6 + c.x = 7 + c.y = 8 + assert c.x == 7 + assert c.y == 8 + + + [case testSubclassAttributeAccess] from mypy_extensions import trait From 96b27679f1b5df3f0b45019e297d2292a068963c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 16:09:53 +0000 Subject: [PATCH 02/23] Fix accessing attribute that overrides a property --- mypyc/ir/class_ir.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index f0f772306e60..5ee97ebe00cb 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -282,6 +282,8 @@ def get_method_and_class(self, name: str) -> tuple[FuncIR, ClassIR] | None: for ir in self.mro: if name in ir.methods: return ir.methods[name], ir + if name in ir.attributes: + return None return None From f8f65cd0df3e2c1e28fcbbea2f99e4b23114f507 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 16:10:15 +0000 Subject: [PATCH 03/23] WIP partially enable test case --- mypyc/test-data/run-classes.test | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index ac7014f62a55..7c9664072e1e 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2027,18 +2027,18 @@ def test_read_only_property_implemented_as_attribute() -> None: assert c.y == 4 c.y = 6 assert c.y == 6 - b: B = C() - assert b.y == 4 - b = c - assert b.x == 5 - assert b.y == 6 - a: Any = c - assert c.x == 5 - assert c.y == 6 - c.x = 7 - c.y = 8 - assert c.x == 7 - assert c.y == 8 + #b: B = C() + #assert b.y == 4 + #b = c + #assert b.x == 5 + #assert b.y == 6 + #a: Any = c + #assert c.x == 5 + #assert c.y == 6 + #c.x = 7 + #c.y = 8 + #assert c.x == 7 + #assert c.y == 8 [case testAttributeOverridesProperty3] from typing import Any From c9f9ae82b7e68e5486273d2adca981cc0d15cac6 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 16:29:14 +0000 Subject: [PATCH 04/23] Refactor mypyc.irbuild.prepare a little --- mypyc/irbuild/prepare.py | 67 ++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 2399647374c0..205d8b3da8b8 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -212,6 +212,11 @@ def can_subclass_builtin(builtin_base: str) -> bool: def prepare_class_def( path: str, module_name: str, cdef: ClassDef, errors: Errors, mapper: Mapper ) -> None: + """Populate the interface-level information in a class IR. + + This includes attribute and method declarations, and the MRO, among other things, but + method bodies are generated in a later pass. + """ ir = mapper.type_to_ir[cdef.info] info = cdef.info @@ -223,31 +228,7 @@ def prepare_class_def( # Supports copy.copy and pickle (including subclasses) ir._serializable = True - # We sort the table for determinism here on Python 3.5 - for name, node in sorted(info.names.items()): - # Currently all plugin generated methods are dummies and not included. - if node.plugin_generated: - continue - - if isinstance(node.node, Var): - assert node.node.type, "Class member %s missing type" % name - if not node.node.is_classvar and name not in ("__slots__", "__deletable__"): - ir.attributes[name] = mapper.type_to_rtype(node.node.type) - elif isinstance(node.node, (FuncDef, Decorator)): - prepare_method_def(ir, module_name, cdef, mapper, node.node) - elif isinstance(node.node, OverloadedFuncDef): - # Handle case for property with both a getter and a setter - if node.node.is_property: - if is_valid_multipart_property_def(node.node): - for item in node.node.items: - prepare_method_def(ir, module_name, cdef, mapper, item) - else: - errors.error("Unsupported property decorator semantics", path, cdef.line) - - # Handle case for regular function overload - else: - assert node.node.impl - prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) + populate_methods_and_attributes(cdef, ir, path, module_name, errors, mapper) # Check for subclassing from builtin types for cls in info.mro: @@ -304,8 +285,8 @@ def prepare_class_def( errors.error("Non-trait bases must appear first in parent list", path, cdef.line) ir.traits = [c for c in bases if c.is_trait] - mro = [] - base_mro = [] + mro = [] # All mypyc base classes + base_mro = [] # Non-trait mypyc base classes for cls in info.mro: if cls not in mapper.type_to_ir: if cls.fullname != "builtins.object": @@ -333,6 +314,38 @@ def prepare_class_def( ir.is_augmented = True +def populate_methods_and_attributes( + cdef: ClassDef, ir: ClassIR, path: str, module_name: str, errors: Errors, mapper: Mapper +) -> None: + """Populate attribute and method declarations.""" + info = cdef.info + # We sort the table for determinism here on Python 3.5. + for name, node in sorted(info.names.items()): + # Currently all plugin generated methods are dummies and not included. + if node.plugin_generated: + continue + + if isinstance(node.node, Var): + assert node.node.type, "Class member %s missing type" % name + if not node.node.is_classvar and name not in ("__slots__", "__deletable__"): + ir.attributes[name] = mapper.type_to_rtype(node.node.type) + elif isinstance(node.node, (FuncDef, Decorator)): + prepare_method_def(ir, module_name, cdef, mapper, node.node) + elif isinstance(node.node, OverloadedFuncDef): + # Handle case for property with both a getter and a setter + if node.node.is_property: + if is_valid_multipart_property_def(node.node): + for item in node.node.items: + prepare_method_def(ir, module_name, cdef, mapper, item) + else: + errors.error("Unsupported property decorator semantics", path, cdef.line) + + # Handle case for regular function overload + else: + assert node.node.impl + prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) + + def prepare_non_ext_class_def( path: str, module_name: str, cdef: ClassDef, errors: Errors, mapper: Mapper ) -> None: From 147ccdeb62052c72cc4a84fa2d63202df2d4f7f8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 16:37:36 +0000 Subject: [PATCH 05/23] More refactoring --- mypyc/irbuild/prepare.py | 72 +++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 205d8b3da8b8..d5ee9ebf7d3e 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -228,8 +228,6 @@ def prepare_class_def( # Supports copy.copy and pickle (including subclasses) ir._serializable = True - populate_methods_and_attributes(cdef, ir, path, module_name, errors, mapper) - # Check for subclassing from builtin types for cls in info.mro: # Special case exceptions and dicts @@ -248,37 +246,6 @@ def prepare_class_def( "Inheriting from most builtin types is unimplemented", path, cdef.line ) - if ir.builtin_base: - ir.attributes.clear() - - # Set up a constructor decl - init_node = cdef.info["__init__"].node - if not ir.is_trait and not ir.builtin_base and isinstance(init_node, FuncDef): - init_sig = mapper.fdef_to_sig(init_node) - - defining_ir = mapper.type_to_ir.get(init_node.info) - # If there is a nontrivial __init__ that wasn't defined in an - # extension class, we need to make the constructor take *args, - # **kwargs so it can call tp_init. - if ( - defining_ir is None - or not defining_ir.is_ext_class - or cdef.info["__init__"].plugin_generated - ) and init_node.info.fullname != "builtins.object": - init_sig = FuncSignature( - [ - init_sig.args[0], - RuntimeArg("args", tuple_rprimitive, ARG_STAR), - RuntimeArg("kwargs", dict_rprimitive, ARG_STAR2), - ], - init_sig.ret_type, - ) - - last_arg = len(init_sig.args) - init_sig.num_bitmap_args - ctor_sig = FuncSignature(init_sig.args[1:last_arg], RInstance(ir)) - ir.ctor = FuncDecl(cdef.name, None, module_name, ctor_sig) - mapper.func_to_decl[cdef.info] = ir.ctor - # Set up the parent class bases = [mapper.type_to_ir[base.type] for base in info.bases if base.type in mapper.type_to_ir] if not all(c.is_trait for c in bases[1:]): @@ -306,6 +273,13 @@ def prepare_class_def( ir.mro = mro ir.base_mro = base_mro + prepare_methods_and_attributes(cdef, ir, path, module_name, errors, mapper) + + if ir.builtin_base: + ir.attributes.clear() + + prepare_init_method(cdef, ir, module_name, mapper) + for base in bases: if base.children is not None: base.children.append(ir) @@ -314,7 +288,7 @@ def prepare_class_def( ir.is_augmented = True -def populate_methods_and_attributes( +def prepare_methods_and_attributes( cdef: ClassDef, ir: ClassIR, path: str, module_name: str, errors: Errors, mapper: Mapper ) -> None: """Populate attribute and method declarations.""" @@ -346,6 +320,36 @@ def populate_methods_and_attributes( prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) +def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: Mapper) -> None: + # Set up a constructor decl + init_node = cdef.info["__init__"].node + if not ir.is_trait and not ir.builtin_base and isinstance(init_node, FuncDef): + init_sig = mapper.fdef_to_sig(init_node) + + defining_ir = mapper.type_to_ir.get(init_node.info) + # If there is a nontrivial __init__ that wasn't defined in an + # extension class, we need to make the constructor take *args, + # **kwargs so it can call tp_init. + if ( + defining_ir is None + or not defining_ir.is_ext_class + or cdef.info["__init__"].plugin_generated + ) and init_node.info.fullname != "builtins.object": + init_sig = FuncSignature( + [ + init_sig.args[0], + RuntimeArg("args", tuple_rprimitive, ARG_STAR), + RuntimeArg("kwargs", dict_rprimitive, ARG_STAR2), + ], + init_sig.ret_type, + ) + + last_arg = len(init_sig.args) - init_sig.num_bitmap_args + ctor_sig = FuncSignature(init_sig.args[1:last_arg], RInstance(ir)) + ir.ctor = FuncDecl(cdef.name, None, module_name, ctor_sig) + mapper.func_to_decl[cdef.info] = ir.ctor + + def prepare_non_ext_class_def( path: str, module_name: str, cdef: ClassDef, errors: Errors, mapper: Mapper ) -> None: From e5076450e466bfa396cae5d656beef6b25a4349d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 17:49:40 +0000 Subject: [PATCH 06/23] Prefer attributes over methods, if both are defined --- mypyc/ir/class_ir.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 5ee97ebe00cb..aa2334287bad 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -280,10 +280,13 @@ def struct_name(self, names: NameGenerator) -> str: def get_method_and_class(self, name: str) -> tuple[FuncIR, ClassIR] | None: for ir in self.mro: - if name in ir.methods: - return ir.methods[name], ir if name in ir.attributes: + # Prefer attributes over methods, if we have an attribute with + # an accessor. This happens if a base class defined the attribute + # as a property. return None + if name in ir.methods: + return ir.methods[name], ir return None From 0243a1e97d4b89c258ad13ce79a7668266bc2593 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 17:50:10 +0000 Subject: [PATCH 07/23] Generate method decl for implicit property getters --- mypyc/ir/func_ir.py | 6 +++++- mypyc/irbuild/prepare.py | 27 +++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index 933230a853a8..008ca8d44fb3 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -155,10 +155,14 @@ def __init__( else: self.bound_sig = sig.bound_sig() - # this is optional because this will be set to the line number when the corresponding + # This is optional because this will be set to the line number when the corresponding # FuncIR is created self._line: int | None = None + # If True, not present in the mypy AST and must be synthesized during irbuild + # Currently only supported for property getters/setters + self.implicit = False + @property def line(self) -> int: assert self._line is not None diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index d5ee9ebf7d3e..80c6969293b1 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -50,7 +50,7 @@ RuntimeArg, ) from mypyc.ir.ops import DeserMaps -from mypyc.ir.rtypes import RInstance, dict_rprimitive, tuple_rprimitive +from mypyc.ir.rtypes import RInstance, dict_rprimitive, tuple_rprimitive, RType from mypyc.irbuild.mapper import Mapper from mypyc.irbuild.util import ( get_func_def, @@ -302,7 +302,10 @@ def prepare_methods_and_attributes( if isinstance(node.node, Var): assert node.node.type, "Class member %s missing type" % name if not node.node.is_classvar and name not in ("__slots__", "__deletable__"): - ir.attributes[name] = mapper.type_to_rtype(node.node.type) + attr_rtype = mapper.type_to_rtype(node.node.type) + ir.attributes[name] = attr_rtype + add_property_methods_for_attribute_if_needed( + info, ir, name, attr_rtype, module_name, mapper) elif isinstance(node.node, (FuncDef, Decorator)): prepare_method_def(ir, module_name, cdef, mapper, node.node) elif isinstance(node.node, OverloadedFuncDef): @@ -320,6 +323,26 @@ def prepare_methods_and_attributes( prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) +def add_property_methods_for_attribute_if_needed( + info: TypeInfo, ir: ClassIR, name: str, attr_rtype: RType, + module_name: str, mapper: Mapper) -> None: + """Add getter and/or setter for attribute if defined as property in a base class.""" + for base in info.mro[1:]: + if base in mapper.type_to_ir: + n = base.names.get(name) + # TODO: Also handle OverlodedFuncDef (setter) + if n and isinstance(n.node, Decorator): + # Defined as a property in a base class/trait. Generate an implicit + # accessor method that will be synthesized during irbuild. + self_arg = RuntimeArg("self", RInstance(ir), pos_only=True) + sig = FuncSignature([self_arg], attr_rtype) + decl = FuncDecl(name, info.name, module_name, sig, FUNC_NORMAL) + decl.is_prop_getter = True + decl.implicit = True # Triggers synthesization + ir.method_decls[name] = decl + ir.property_types[name] = attr_rtype # TODO: Needed?? + + def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: Mapper) -> None: # Set up a constructor decl init_node = cdef.info["__init__"].node From 15b7b30df62712849683e802110a7b1e91602847 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 18:27:52 +0000 Subject: [PATCH 08/23] Fix serialization of the "implicit" attribute --- mypyc/ir/func_ir.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index 008ca8d44fb3..dbb45fc7ec29 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -139,6 +139,7 @@ def __init__( kind: int = FUNC_NORMAL, is_prop_setter: bool = False, is_prop_getter: bool = False, + implicit: bool = False, ) -> None: self.name = name self.class_name = class_name @@ -155,14 +156,14 @@ def __init__( else: self.bound_sig = sig.bound_sig() + # If True, not present in the mypy AST and must be synthesized during irbuild + # Currently only supported for property getters/setters + self.implicit = implicit + # This is optional because this will be set to the line number when the corresponding # FuncIR is created self._line: int | None = None - # If True, not present in the mypy AST and must be synthesized during irbuild - # Currently only supported for property getters/setters - self.implicit = False - @property def line(self) -> int: assert self._line is not None @@ -202,6 +203,7 @@ def serialize(self) -> JsonDict: "kind": self.kind, "is_prop_setter": self.is_prop_setter, "is_prop_getter": self.is_prop_getter, + "implicit": self.implicit, } # TODO: move this to FuncIR? @@ -223,6 +225,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> FuncDecl: data["kind"], data["is_prop_setter"], data["is_prop_getter"], + data["implicit"], ) From 8e638b06d232ce6d48de3ffdfea26f2143d260b7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 18:30:27 +0000 Subject: [PATCH 09/23] Support simple use cases where an attribute overrides a property --- mypyc/codegen/emitclass.py | 16 ++++++++++++---- mypyc/ir/class_ir.py | 4 ++-- mypyc/irbuild/classdef.py | 11 ++++++++++- mypyc/irbuild/function.py | 10 ++++++++++ mypyc/irbuild/vtable.py | 2 +- mypyc/test-data/run-classes.test | 26 +++++++++++++------------- 6 files changed, 48 insertions(+), 21 deletions(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 1e774bbd0185..72e16345a325 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -824,7 +824,10 @@ def generate_getseter_declarations(cl: ClassIR, emitter: Emitter) -> None: ) ) - for prop in cl.properties: + for prop, (getter, setter) in cl.properties.items(): + if getter.decl.implicit: + continue + # Generate getter declaration emitter.emit_line("static PyObject *") emitter.emit_line( @@ -834,7 +837,7 @@ def generate_getseter_declarations(cl: ClassIR, emitter: Emitter) -> None: ) # Generate property setter declaration if a setter exists - if cl.properties[prop][1]: + if setter: emitter.emit_line("static int") emitter.emit_line( "{}({} *self, PyObject *value, void *closure);".format( @@ -854,11 +857,13 @@ def generate_getseters_table(cl: ClassIR, name: str, emitter: Emitter) -> None: ) ) emitter.emit_line(" NULL, NULL},") - for prop in cl.properties: + for prop, (getter, setter) in cl.properties.items(): + if getter.decl.implicit: + continue + emitter.emit_line(f'{{"{prop}",') emitter.emit_line(f" (getter){getter_name(cl, prop, emitter.names)},") - setter = cl.properties[prop][1] if setter: emitter.emit_line(f" (setter){setter_name(cl, prop, emitter.names)},") emitter.emit_line("NULL, NULL},") @@ -878,6 +883,9 @@ def generate_getseters(cl: ClassIR, emitter: Emitter) -> None: if i < len(cl.attributes) - 1: emitter.emit_line("") for prop, (getter, setter) in cl.properties.items(): + if getter.decl.implicit: + continue + rtype = getter.sig.ret_type emitter.emit_line("") generate_readonly_getter(cl, prop, rtype, getter, emitter) diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index aa2334287bad..5a2020bdf6e6 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -278,9 +278,9 @@ def name_prefix(self, names: NameGenerator) -> str: def struct_name(self, names: NameGenerator) -> str: return f"{exported_name(self.fullname)}Object" - def get_method_and_class(self, name: str) -> tuple[FuncIR, ClassIR] | None: + def get_method_and_class(self, name: str, *, prefer_method: bool = False) -> tuple[FuncIR, ClassIR] | None: for ir in self.mro: - if name in ir.attributes: + if not prefer_method and name in ir.attributes: # Prefer attributes over methods, if we have an attribute with # an accessor. This happens if a base class defined the attribute # as a property. diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index b1f2ed1a1a65..ed09f1f6c2b7 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -53,7 +53,7 @@ object_rprimitive, ) from mypyc.irbuild.builder import IRBuilder -from mypyc.irbuild.function import handle_ext_method, handle_non_ext_method, load_type +from mypyc.irbuild.function import handle_ext_method, handle_non_ext_method, load_type, gen_property_getter_ir from mypyc.irbuild.util import dataclass_type, get_func_def, is_constant, is_dataclass_decorator from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op from mypyc.primitives.generic_ops import py_hasattr_op, py_setattr_op @@ -151,6 +151,15 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: else: builder.error("Unsupported statement in class body", stmt.line) + for name, decl in ir.method_decls.items(): + if decl.implicit: + func_ir = gen_property_getter_ir(builder, decl, cdef) + builder.functions.append(func_ir) + ir.properties[name] = (func_ir, None) + ir.methods[func_ir.decl.name] = func_ir + # TODO: Generate glue method if needed + # TODO: Do we need interpreted glue methods? Maybe not? + cls_builder.finalize(ir) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 237088791bc9..2cfacd02fad1 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -1026,3 +1026,13 @@ def get_native_impl_ids(builder: IRBuilder, singledispatch_func: FuncDef) -> dic """ impls = builder.singledispatch_impls[singledispatch_func] return {impl: i for i, (typ, impl) in enumerate(impls) if not is_decorated(builder, impl)} + + +def gen_property_getter_ir(builder: IRBuilder, func_decl: FuncDecl, cdef: ClassDef) -> FuncIR: + name = func_decl.name + builder.enter(name) + self_reg = builder.add_argument("self", func_decl.sig.args[0].type) + value = builder.builder.get_attr(self_reg, name, func_decl.sig.ret_type, -1) + builder.add(Return(value)) + args, _, blocks, ret_type, fn_info = builder.leave() + return FuncIR(func_decl, args, blocks) diff --git a/mypyc/irbuild/vtable.py b/mypyc/irbuild/vtable.py index a02cd622cee1..13bc4d46e15d 100644 --- a/mypyc/irbuild/vtable.py +++ b/mypyc/irbuild/vtable.py @@ -62,7 +62,7 @@ def specialize_parent_vtable(cls: ClassIR, parent: ClassIR) -> VTableEntries: # (This may not be the method in the entry, if it was overridden.) orig_parent_method = entry.cls.get_method(entry.name) assert orig_parent_method - method_cls = cls.get_method_and_class(entry.name) + method_cls = cls.get_method_and_class(entry.name, prefer_method=True) if method_cls: child_method, defining_cls = method_cls # TODO: emit a wrapper for __init__ that raises or something diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 7c9664072e1e..c9869777c726 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2014,7 +2014,7 @@ class B: @property def y(self) -> int: - return 22 + return 25 class C(B): x: int @@ -2027,18 +2027,18 @@ def test_read_only_property_implemented_as_attribute() -> None: assert c.y == 4 c.y = 6 assert c.y == 6 - #b: B = C() - #assert b.y == 4 - #b = c - #assert b.x == 5 - #assert b.y == 6 - #a: Any = c - #assert c.x == 5 - #assert c.y == 6 - #c.x = 7 - #c.y = 8 - #assert c.x == 7 - #assert c.y == 8 + b: B = C() + assert b.y == 4 + b = c + assert b.x == 5 + assert b.y == 6 + a: Any = c + assert c.x == 5 + assert c.y == 6 + c.x = 7 + c.y = 8 + assert c.x == 7 + assert c.y == 8 [case testAttributeOverridesProperty3] from typing import Any From 44075a9a937ff6154151ebeb14ddb4c77bb69cbc Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Dec 2022 20:15:11 +0000 Subject: [PATCH 10/23] Fix case where both attribute and property are inherited --- mypyc/ir/class_ir.py | 16 +++++++++------- mypyc/irbuild/prepare.py | 28 ++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 5a2020bdf6e6..5af4643d6644 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -278,15 +278,17 @@ def name_prefix(self, names: NameGenerator) -> str: def struct_name(self, names: NameGenerator) -> str: return f"{exported_name(self.fullname)}Object" - def get_method_and_class(self, name: str, *, prefer_method: bool = False) -> tuple[FuncIR, ClassIR] | None: + def get_method_and_class(self, name: str, *, + prefer_method: bool = False) -> tuple[FuncIR, ClassIR] | None: for ir in self.mro: - if not prefer_method and name in ir.attributes: - # Prefer attributes over methods, if we have an attribute with - # an accessor. This happens if a base class defined the attribute - # as a property. - return None if name in ir.methods: - return ir.methods[name], ir + func_ir = ir.methods[name] + if not prefer_method and func_ir.decl.implicit: + # This is an implicit accessor, so there is also an attribute definition + # which the caller prefers. This happens if an attribute overrides a + # property. + return None + return func_ir, ir return None diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 80c6969293b1..796351b23e39 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -98,6 +98,12 @@ def build_type_map( else: prepare_non_ext_class_def(module.path, module.fullname, cdef, errors, mapper) + # Prepare implicit attribute accessors as needed if an attribute overrides a property. + for module, cdef in classes: + class_ir = mapper.type_to_ir[cdef.info] + if class_ir.is_ext_class: + prepare_implicit_property_accessors(cdef.info, class_ir, module.fullname, mapper) + # Collect all the functions also. We collect from the symbol table # so that we can easily pick out the right copy of a function that # is conditionally defined. @@ -304,8 +310,6 @@ def prepare_methods_and_attributes( if not node.node.is_classvar and name not in ("__slots__", "__deletable__"): attr_rtype = mapper.type_to_rtype(node.node.type) ir.attributes[name] = attr_rtype - add_property_methods_for_attribute_if_needed( - info, ir, name, attr_rtype, module_name, mapper) elif isinstance(node.node, (FuncDef, Decorator)): prepare_method_def(ir, module_name, cdef, mapper, node.node) elif isinstance(node.node, OverloadedFuncDef): @@ -323,24 +327,32 @@ def prepare_methods_and_attributes( prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) +def prepare_implicit_property_accessors(info: TypeInfo, ir: ClassIR, module_name: str, + mapper: Mapper) -> None: + for base in ir.base_mro: + for name, attr_rtype in base.attributes.items(): + add_property_methods_for_attribute_if_needed( + info, ir, name, attr_rtype, module_name, mapper) + + def add_property_methods_for_attribute_if_needed( - info: TypeInfo, ir: ClassIR, name: str, attr_rtype: RType, + info: TypeInfo, ir: ClassIR, attr_name: str, attr_rtype: RType, module_name: str, mapper: Mapper) -> None: """Add getter and/or setter for attribute if defined as property in a base class.""" for base in info.mro[1:]: if base in mapper.type_to_ir: - n = base.names.get(name) + n = base.names.get(attr_name) # TODO: Also handle OverlodedFuncDef (setter) - if n and isinstance(n.node, Decorator): + if n and isinstance(n.node, Decorator) and n.node.name not in ir.method_decls: # Defined as a property in a base class/trait. Generate an implicit # accessor method that will be synthesized during irbuild. self_arg = RuntimeArg("self", RInstance(ir), pos_only=True) sig = FuncSignature([self_arg], attr_rtype) - decl = FuncDecl(name, info.name, module_name, sig, FUNC_NORMAL) + decl = FuncDecl(attr_name, info.name, module_name, sig, FUNC_NORMAL) decl.is_prop_getter = True decl.implicit = True # Triggers synthesization - ir.method_decls[name] = decl - ir.property_types[name] = attr_rtype # TODO: Needed?? + ir.method_decls[attr_name] = decl + ir.property_types[attr_name] = attr_rtype # TODO: Needed?? def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: Mapper) -> None: From c756c365e4f796e8c1796f178e1e3352101fcdf0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 30 Dec 2022 15:12:44 +0000 Subject: [PATCH 11/23] Support settable properties --- mypyc/irbuild/classdef.py | 21 ++++++++---- mypyc/irbuild/function.py | 15 +++++++- mypyc/irbuild/prepare.py | 59 +++++++++++++++++++++++++------- mypyc/sametype.py | 2 +- mypyc/test-data/run-classes.test | 4 +-- 5 files changed, 78 insertions(+), 23 deletions(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index ed09f1f6c2b7..d9a2953ab3df 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -6,6 +6,7 @@ from typing import Callable from typing_extensions import Final +from mypyc.common import PROPSET_PREFIX from mypy.nodes import ( AssignmentStmt, CallExpr, @@ -53,7 +54,7 @@ object_rprimitive, ) from mypyc.irbuild.builder import IRBuilder -from mypyc.irbuild.function import handle_ext_method, handle_non_ext_method, load_type, gen_property_getter_ir +from mypyc.irbuild.function import handle_ext_method, handle_non_ext_method, load_type, gen_property_getter_ir, gen_property_setter_ir from mypyc.irbuild.util import dataclass_type, get_func_def, is_constant, is_dataclass_decorator from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op from mypyc.primitives.generic_ops import py_hasattr_op, py_setattr_op @@ -152,11 +153,19 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: builder.error("Unsupported statement in class body", stmt.line) for name, decl in ir.method_decls.items(): - if decl.implicit: - func_ir = gen_property_getter_ir(builder, decl, cdef) - builder.functions.append(func_ir) - ir.properties[name] = (func_ir, None) - ir.methods[func_ir.decl.name] = func_ir + if decl.implicit and decl.is_prop_getter: + getter_ir = gen_property_getter_ir(builder, decl, cdef) + builder.functions.append(getter_ir) + ir.methods[getter_ir.decl.name] = getter_ir + + setter_ir = None + setter_name = PROPSET_PREFIX + name + if setter_name in ir.method_decls: + setter_ir = gen_property_setter_ir(builder, ir.method_decls[setter_name], cdef) + builder.functions.append(setter_ir) + ir.methods[setter_name] = setter_ir + + ir.properties[name] = (getter_ir, setter_ir) # TODO: Generate glue method if needed # TODO: Do we need interpreted glue methods? Maybe not? diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 2cfacd02fad1..6236b8f44ceb 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -28,7 +28,7 @@ Var, ) from mypy.types import CallableType, get_proper_type -from mypyc.common import LAMBDA_NAME, SELF_NAME +from mypyc.common import LAMBDA_NAME, SELF_NAME, PROPSET_PREFIX from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import ( FUNC_CLASSMETHOD, @@ -1036,3 +1036,16 @@ def gen_property_getter_ir(builder: IRBuilder, func_decl: FuncDecl, cdef: ClassD builder.add(Return(value)) args, _, blocks, ret_type, fn_info = builder.leave() return FuncIR(func_decl, args, blocks) + + +def gen_property_setter_ir(builder: IRBuilder, func_decl: FuncDecl, cdef: ClassDef) -> FuncIR: + name = func_decl.name + builder.enter(name) + self_reg = builder.add_argument("self", func_decl.sig.args[0].type) + value_reg = builder.add_argument("value", func_decl.sig.args[1].type) + assert name.startswith(PROPSET_PREFIX) + attr_name = name[len(PROPSET_PREFIX):] + value = builder.add(SetAttr(self_reg, attr_name, value_reg, -1)) + builder.add(Return(builder.none())) + args, _, blocks, ret_type, fn_info = builder.leave() + return FuncIR(func_decl, args, blocks) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 796351b23e39..0e969de26fdc 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -50,7 +50,7 @@ RuntimeArg, ) from mypyc.ir.ops import DeserMaps -from mypyc.ir.rtypes import RInstance, dict_rprimitive, tuple_rprimitive, RType +from mypyc.ir.rtypes import RInstance, dict_rprimitive, tuple_rprimitive, RType, none_rprimitive from mypyc.irbuild.mapper import Mapper from mypyc.irbuild.util import ( get_func_def, @@ -174,6 +174,8 @@ def prepare_method_def( # works correctly. decl.name = PROPSET_PREFIX + decl.name decl.is_prop_setter = True + # Making the argument positional-only avoids needless glue method generation + decl.sig.args[1].pos_only = True ir.method_decls[PROPSET_PREFIX + node.name] = decl if node.func.is_property: @@ -338,21 +340,52 @@ def prepare_implicit_property_accessors(info: TypeInfo, ir: ClassIR, module_name def add_property_methods_for_attribute_if_needed( info: TypeInfo, ir: ClassIR, attr_name: str, attr_rtype: RType, module_name: str, mapper: Mapper) -> None: - """Add getter and/or setter for attribute if defined as property in a base class.""" + """Add getter and/or setter for attribute if defined as property in a base class. + + Only add declarations. The body IR will be synthesized later during irbuild. + """ for base in info.mro[1:]: if base in mapper.type_to_ir: n = base.names.get(attr_name) - # TODO: Also handle OverlodedFuncDef (setter) - if n and isinstance(n.node, Decorator) and n.node.name not in ir.method_decls: - # Defined as a property in a base class/trait. Generate an implicit - # accessor method that will be synthesized during irbuild. - self_arg = RuntimeArg("self", RInstance(ir), pos_only=True) - sig = FuncSignature([self_arg], attr_rtype) - decl = FuncDecl(attr_name, info.name, module_name, sig, FUNC_NORMAL) - decl.is_prop_getter = True - decl.implicit = True # Triggers synthesization - ir.method_decls[attr_name] = decl - ir.property_types[attr_name] = attr_rtype # TODO: Needed?? + if n is None: + continue + node = n.node + if isinstance(node, Decorator) and node.name not in ir.method_decls: + # Defined as a read-only property in base class/trait + add_getter_declaration(ir, attr_name, attr_rtype, module_name) + elif isinstance(node, OverloadedFuncDef) and is_property_with_setter(node): + # Defined as a read-write property in base class/trait + add_getter_declaration(ir, attr_name, attr_rtype, module_name) + add_setter_declaration(ir, attr_name, attr_rtype, module_name) + + +def add_getter_declaration(ir: ClassIR, attr_name: str, attr_rtype: RType, module_name: str) -> None: + self_arg = RuntimeArg("self", RInstance(ir), pos_only=True) + sig = FuncSignature([self_arg], attr_rtype) + decl = FuncDecl(attr_name, ir.name, module_name, sig, FUNC_NORMAL) + decl.is_prop_getter = True + decl.implicit = True # Triggers synthesization + ir.method_decls[attr_name] = decl + ir.property_types[attr_name] = attr_rtype # TODO: Needed?? + + +def add_setter_declaration(ir: ClassIR, attr_name: str, attr_rtype: RType, module_name: str) -> None: + self_arg = RuntimeArg("self", RInstance(ir), pos_only=True) + value_arg = RuntimeArg("value", attr_rtype, pos_only=True) + sig = FuncSignature([self_arg, value_arg], none_rprimitive) + setter_name = PROPSET_PREFIX + attr_name + decl = FuncDecl(setter_name, ir.name, module_name, sig, FUNC_NORMAL) + decl.is_prop_setter = True + decl.implicit = True # Triggers synthesization + ir.method_decls[setter_name] = decl + + +def is_property_with_setter(node: OverloadedFuncDef) -> bool: + return ( + len(node.items) == 2 + and isinstance(node.items[0], Decorator) + and isinstance(node.items[1], Decorator) + and node.items[0].func.is_property) def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: Mapper) -> None: diff --git a/mypyc/sametype.py b/mypyc/sametype.py index 056ed683e5b8..8b863a08ceb9 100644 --- a/mypyc/sametype.py +++ b/mypyc/sametype.py @@ -35,7 +35,7 @@ def is_same_method_signature(a: FuncSignature, b: FuncSignature) -> bool: len(a.args) == len(b.args) and is_same_type(a.ret_type, b.ret_type) and all( - is_same_type(t1.type, t2.type) and t1.name == t2.name and t1.optional == t2.optional + is_same_type(t1.type, t2.type) and ((t1.pos_only and t2.pos_only) or t1.name == t2.name) and t1.optional == t2.optional for t1, t2 in zip(a.args[1:], b.args[1:]) ) ) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index c9869777c726..801768a2d696 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2088,12 +2088,12 @@ class T1: @property def x(self) -> int: ... @x.setter - def x(self, v: int) -> None: ... + def x(self, v1: int) -> None: ... @property def y(self) -> int: ... @y.setter - def y(self, v: int) -> None: ... + def y(self, v2: int) -> None: ... class C(T1): x: int From de7e436d3762b81f897f233cd6ec6f6a634e1afc Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 30 Dec 2022 15:13:59 +0000 Subject: [PATCH 12/23] Black + isort --- mypyc/ir/class_ir.py | 5 +++-- mypyc/irbuild/classdef.py | 10 ++++++++-- mypyc/irbuild/function.py | 6 +++--- mypyc/irbuild/prepare.py | 30 +++++++++++++++++++++--------- mypyc/sametype.py | 4 +++- 5 files changed, 38 insertions(+), 17 deletions(-) diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 5af4643d6644..71d61c3f0efa 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -278,8 +278,9 @@ def name_prefix(self, names: NameGenerator) -> str: def struct_name(self, names: NameGenerator) -> str: return f"{exported_name(self.fullname)}Object" - def get_method_and_class(self, name: str, *, - prefer_method: bool = False) -> tuple[FuncIR, ClassIR] | None: + def get_method_and_class( + self, name: str, *, prefer_method: bool = False + ) -> tuple[FuncIR, ClassIR] | None: for ir in self.mro: if name in ir.methods: func_ir = ir.methods[name] diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index d9a2953ab3df..e01f2b675ee1 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -6,7 +6,6 @@ from typing import Callable from typing_extensions import Final -from mypyc.common import PROPSET_PREFIX from mypy.nodes import ( AssignmentStmt, CallExpr, @@ -26,6 +25,7 @@ is_class_var, ) from mypy.types import ENUM_REMOVED_PROPS, Instance, get_proper_type +from mypyc.common import PROPSET_PREFIX from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.ops import ( @@ -54,7 +54,13 @@ object_rprimitive, ) from mypyc.irbuild.builder import IRBuilder -from mypyc.irbuild.function import handle_ext_method, handle_non_ext_method, load_type, gen_property_getter_ir, gen_property_setter_ir +from mypyc.irbuild.function import ( + gen_property_getter_ir, + gen_property_setter_ir, + handle_ext_method, + handle_non_ext_method, + load_type, +) from mypyc.irbuild.util import dataclass_type, get_func_def, is_constant, is_dataclass_decorator from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op from mypyc.primitives.generic_ops import py_hasattr_op, py_setattr_op diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 6236b8f44ceb..f4f4409dd3a7 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -28,7 +28,7 @@ Var, ) from mypy.types import CallableType, get_proper_type -from mypyc.common import LAMBDA_NAME, SELF_NAME, PROPSET_PREFIX +from mypyc.common import LAMBDA_NAME, PROPSET_PREFIX, SELF_NAME from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import ( FUNC_CLASSMETHOD, @@ -1044,8 +1044,8 @@ def gen_property_setter_ir(builder: IRBuilder, func_decl: FuncDecl, cdef: ClassD self_reg = builder.add_argument("self", func_decl.sig.args[0].type) value_reg = builder.add_argument("value", func_decl.sig.args[1].type) assert name.startswith(PROPSET_PREFIX) - attr_name = name[len(PROPSET_PREFIX):] - value = builder.add(SetAttr(self_reg, attr_name, value_reg, -1)) + attr_name = name[len(PROPSET_PREFIX) :] + builder.add(SetAttr(self_reg, attr_name, value_reg, -1)) builder.add(Return(builder.none())) args, _, blocks, ret_type, fn_info = builder.leave() return FuncIR(func_decl, args, blocks) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 0e969de26fdc..8301879d66f5 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -50,7 +50,7 @@ RuntimeArg, ) from mypyc.ir.ops import DeserMaps -from mypyc.ir.rtypes import RInstance, dict_rprimitive, tuple_rprimitive, RType, none_rprimitive +from mypyc.ir.rtypes import RInstance, RType, dict_rprimitive, none_rprimitive, tuple_rprimitive from mypyc.irbuild.mapper import Mapper from mypyc.irbuild.util import ( get_func_def, @@ -329,17 +329,24 @@ def prepare_methods_and_attributes( prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) -def prepare_implicit_property_accessors(info: TypeInfo, ir: ClassIR, module_name: str, - mapper: Mapper) -> None: +def prepare_implicit_property_accessors( + info: TypeInfo, ir: ClassIR, module_name: str, mapper: Mapper +) -> None: for base in ir.base_mro: for name, attr_rtype in base.attributes.items(): add_property_methods_for_attribute_if_needed( - info, ir, name, attr_rtype, module_name, mapper) + info, ir, name, attr_rtype, module_name, mapper + ) def add_property_methods_for_attribute_if_needed( - info: TypeInfo, ir: ClassIR, attr_name: str, attr_rtype: RType, - module_name: str, mapper: Mapper) -> None: + info: TypeInfo, + ir: ClassIR, + attr_name: str, + attr_rtype: RType, + module_name: str, + mapper: Mapper, +) -> None: """Add getter and/or setter for attribute if defined as property in a base class. Only add declarations. The body IR will be synthesized later during irbuild. @@ -359,7 +366,9 @@ def add_property_methods_for_attribute_if_needed( add_setter_declaration(ir, attr_name, attr_rtype, module_name) -def add_getter_declaration(ir: ClassIR, attr_name: str, attr_rtype: RType, module_name: str) -> None: +def add_getter_declaration( + ir: ClassIR, attr_name: str, attr_rtype: RType, module_name: str +) -> None: self_arg = RuntimeArg("self", RInstance(ir), pos_only=True) sig = FuncSignature([self_arg], attr_rtype) decl = FuncDecl(attr_name, ir.name, module_name, sig, FUNC_NORMAL) @@ -369,7 +378,9 @@ def add_getter_declaration(ir: ClassIR, attr_name: str, attr_rtype: RType, modul ir.property_types[attr_name] = attr_rtype # TODO: Needed?? -def add_setter_declaration(ir: ClassIR, attr_name: str, attr_rtype: RType, module_name: str) -> None: +def add_setter_declaration( + ir: ClassIR, attr_name: str, attr_rtype: RType, module_name: str +) -> None: self_arg = RuntimeArg("self", RInstance(ir), pos_only=True) value_arg = RuntimeArg("value", attr_rtype, pos_only=True) sig = FuncSignature([self_arg, value_arg], none_rprimitive) @@ -385,7 +396,8 @@ def is_property_with_setter(node: OverloadedFuncDef) -> bool: len(node.items) == 2 and isinstance(node.items[0], Decorator) and isinstance(node.items[1], Decorator) - and node.items[0].func.is_property) + and node.items[0].func.is_property + ) def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: Mapper) -> None: diff --git a/mypyc/sametype.py b/mypyc/sametype.py index 8b863a08ceb9..1b811d4e9041 100644 --- a/mypyc/sametype.py +++ b/mypyc/sametype.py @@ -35,7 +35,9 @@ def is_same_method_signature(a: FuncSignature, b: FuncSignature) -> bool: len(a.args) == len(b.args) and is_same_type(a.ret_type, b.ret_type) and all( - is_same_type(t1.type, t2.type) and ((t1.pos_only and t2.pos_only) or t1.name == t2.name) and t1.optional == t2.optional + is_same_type(t1.type, t2.type) + and ((t1.pos_only and t2.pos_only) or t1.name == t2.name) + and t1.optional == t2.optional for t1, t2 in zip(a.args[1:], b.args[1:]) ) ) From d92905dc0d4c76321e97e2eeab30afa1b54c96ec Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 30 Dec 2022 15:16:34 +0000 Subject: [PATCH 13/23] Merge test cases --- mypyc/test-data/run-classes.test | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 801768a2d696..69f6fc6e53d3 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -1968,7 +1968,7 @@ import other_interpreted [out] -[case testAttributeOverridesProperty1] +[case testAttributeOverridesProperty] from typing import Any from mypy_extensions import trait @@ -1979,18 +1979,18 @@ class T1: @property def y(self) -> int: ... -class C(T1): +class C1(T1): x: int y: int = 4 -def test_read_only_property_implemented_as_attribute() -> None: - c = C() +def test_read_only_property_in_trait_implemented_as_attribute() -> None: + c = C1() c.x = 5 assert c.x == 5 assert c.y == 4 c.y = 6 assert c.y == 6 - t: T1 = C() + t: T1 = C1() assert t.y == 4 t = c assert t.x == 5 @@ -2003,11 +2003,7 @@ def test_read_only_property_implemented_as_attribute() -> None: assert c.x == 7 assert c.y == 8 -[case testAttributeOverridesProperty2] -from typing import Any -from mypy_extensions import trait - -class B: +class B2: @property def x(self) -> int: return 11 @@ -2016,18 +2012,18 @@ class B: def y(self) -> int: return 25 -class C(B): +class C2(B2): x: int y: int = 4 -def test_read_only_property_implemented_as_attribute() -> None: - c = C() +def test_read_only_property_in_class_implemented_as_attribute() -> None: + c = C2() c.x = 5 assert c.x == 5 assert c.y == 4 c.y = 6 assert c.y == 6 - b: B = C() + b: B2 = C2() assert b.y == 4 b = c assert b.x == 5 @@ -2078,7 +2074,6 @@ def test_read_only_property_implemented_as_attribute() -> None: assert c.x == 7 assert c.y == 8 - [case testAttributeOverridesProperty4] from typing import Any from mypy_extensions import trait @@ -2120,8 +2115,6 @@ def test_read_only_property_implemented_as_attribute() -> None: assert c.x == 7 assert c.y == 8 - - [case testSubclassAttributeAccess] from mypy_extensions import trait From 1fd4bc534a99c6c4d18dd94924aeea6aa4ea46ee Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 30 Dec 2022 15:18:33 +0000 Subject: [PATCH 14/23] Merge more test cases --- mypyc/test-data/run-classes.test | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 69f6fc6e53d3..3fe1f0f21f0d 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2036,32 +2036,28 @@ def test_read_only_property_in_class_implemented_as_attribute() -> None: assert c.x == 7 assert c.y == 8 -[case testAttributeOverridesProperty3] -from typing import Any -from mypy_extensions import trait - @trait -class T1: +class T3: @property def x(self) -> int: ... @property def y(self) -> int: ... -class B: +class B3: x: int y: int = 4 -class C(B, T1): +class C3(B3, T3): pass -def test_read_only_property_implemented_as_attribute() -> None: - c = C() +def test_read_only_property_implemented_as_attribute_indirectly() -> None: + c = C3() c.x = 5 assert c.x == 5 assert c.y == 4 c.y = 6 assert c.y == 6 - t: T1 = C() + t: T3 = C3() assert t.y == 4 t = c assert t.x == 5 @@ -2074,12 +2070,8 @@ def test_read_only_property_implemented_as_attribute() -> None: assert c.x == 7 assert c.y == 8 -[case testAttributeOverridesProperty4] -from typing import Any -from mypy_extensions import trait - @trait -class T1: +class T4: @property def x(self) -> int: ... @x.setter @@ -2090,18 +2082,18 @@ class T1: @y.setter def y(self, v2: int) -> None: ... -class C(T1): +class C4(T4): x: int y: int = 4 -def test_read_only_property_implemented_as_attribute() -> None: - c = C() +def test_read_write_property_implemented_as_attribute() -> None: + c = C4() c.x = 5 assert c.x == 5 assert c.y == 4 c.y = 6 assert c.y == 6 - t: T1 = C() + t: T4 = C4() assert t.y == 4 t.x = 5 assert t.x == 5 From d80842aa52e11cc569b0b4773789036db29fea82 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 30 Dec 2022 17:04:41 +0000 Subject: [PATCH 15/23] Fix edge case --- mypy/checker.py | 5 ++-- mypyc/test-data/run-classes.test | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c265ac4905fb..1cd8368a082b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2498,8 +2498,9 @@ class C(B, A[int]): ... # this is unsafe because... ok = is_equivalent(first_type, second_type) if not ok: second_node = base2[name].node - if isinstance(second_node, Decorator) and second_node.func.is_property: - ok = is_subtype(first_type, cast(CallableType, second_type).ret_type) + if isinstance(second_type, FunctionLike) and is_property(second_node): + second_type = get_property_type(second_type) + ok = is_subtype(first_type, second_type) else: if first_type is None: self.msg.cannot_determine_type_in_base(name, base1.name, ctx) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 3fe1f0f21f0d..29d11ea492f6 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2107,6 +2107,49 @@ def test_read_write_property_implemented_as_attribute() -> None: assert c.x == 7 assert c.y == 8 +@trait +class T5: + @property + def x(self) -> int: ... + @x.setter + def x(self, v1: int) -> None: ... + + @property + def y(self) -> int: ... + @y.setter + def y(self, v2: int) -> None: ... + +class B5: + x: int + y: int = 4 + +class BB5(B5): + pass + +class C5(BB5, T4): + pass + +def test_read_write_property_indirectly_implemented_as_attribute() -> None: + c = C5() + c.x = 5 + assert c.x == 5 + assert c.y == 4 + c.y = 6 + assert c.y == 6 + t: T4 = C5() + assert t.y == 4 + t.x = 5 + assert t.x == 5 + t.y = 6 + assert t.y == 6 + a: Any = c + assert c.x == 5 + assert c.y == 6 + c.x = 7 + c.y = 8 + assert c.x == 7 + assert c.y == 8 + [case testSubclassAttributeAccess] from mypy_extensions import trait From 02749695c2f446951ffff5acc97f5d5bf5fdb7d9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 30 Dec 2022 17:14:15 +0000 Subject: [PATCH 16/23] Test case fixes --- mypyc/test-data/run-classes.test | 62 ++++++++++++++++---------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 29d11ea492f6..92e2af3b5306 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -1996,12 +1996,12 @@ def test_read_only_property_in_trait_implemented_as_attribute() -> None: assert t.x == 5 assert t.y == 6 a: Any = c - assert c.x == 5 - assert c.y == 6 - c.x = 7 - c.y = 8 - assert c.x == 7 - assert c.y == 8 + assert a.x == 5 + assert a.y == 6 + a.x = 7 + a.y = 8 + assert a.x == 7 + assert a.y == 8 class B2: @property @@ -2029,12 +2029,12 @@ def test_read_only_property_in_class_implemented_as_attribute() -> None: assert b.x == 5 assert b.y == 6 a: Any = c - assert c.x == 5 - assert c.y == 6 - c.x = 7 - c.y = 8 - assert c.x == 7 - assert c.y == 8 + assert a.x == 5 + assert a.y == 6 + a.x = 7 + a.y = 8 + assert a.x == 7 + assert a.y == 8 @trait class T3: @@ -2063,12 +2063,12 @@ def test_read_only_property_implemented_as_attribute_indirectly() -> None: assert t.x == 5 assert t.y == 6 a: Any = c - assert c.x == 5 - assert c.y == 6 - c.x = 7 - c.y = 8 - assert c.x == 7 - assert c.y == 8 + assert a.x == 5 + assert a.y == 6 + a.x = 7 + a.y = 8 + assert a.x == 7 + assert a.y == 8 @trait class T4: @@ -2100,12 +2100,12 @@ def test_read_write_property_implemented_as_attribute() -> None: t.y = 6 assert t.y == 6 a: Any = c - assert c.x == 5 - assert c.y == 6 - c.x = 7 - c.y = 8 - assert c.x == 7 - assert c.y == 8 + assert a.x == 5 + assert a.y == 6 + a.x = 7 + a.y = 8 + assert a.x == 7 + assert a.y == 8 @trait class T5: @@ -2126,7 +2126,7 @@ class B5: class BB5(B5): pass -class C5(BB5, T4): +class C5(BB5, T5): pass def test_read_write_property_indirectly_implemented_as_attribute() -> None: @@ -2143,12 +2143,12 @@ def test_read_write_property_indirectly_implemented_as_attribute() -> None: t.y = 6 assert t.y == 6 a: Any = c - assert c.x == 5 - assert c.y == 6 - c.x = 7 - c.y = 8 - assert c.x == 7 - assert c.y == 8 + assert a.x == 5 + assert a.y == 6 + a.x = 7 + a.y = 8 + assert a.x == 7 + assert a.y == 8 [case testSubclassAttributeAccess] from mypy_extensions import trait From 810c4e1484d5012e61359b6e96df1a9eae1e88f9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 30 Dec 2022 17:14:21 +0000 Subject: [PATCH 17/23] Add native int test cases --- mypyc/test-data/run-i64.test | 68 ++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index 893b3f808f24..644b270a55f2 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -1223,12 +1223,15 @@ def test_many_locals() -> None: assert a32 == 55 assert a33 == 20 -[case testI64GlueMethods] +[case testI64GlueMethodsAndInheritance] +from typing import Any from typing_extensions import Final MYPY = False if MYPY: - from mypy_extensions import i64 + from mypy_extensions import i64, trait + +from testutil import assertRaises MAGIC: Final = -113 @@ -1266,3 +1269,64 @@ def test_derived_switches_arg_to_have_default() -> None: b: Base = Derived() assert b.hoho(5) == 3 assert b.hoho(MAGIC) == MAGIC - 2 + +@trait +class T: + @property + def x(self) -> i64: ... + @property + def y(self) -> i64: ... + +class C(T): + x: i64 + y: i64 = 4 + +def test_read_only_property_in_trait_implemented_as_attribute() -> None: + c = C() + c.x = 5 + assert c.x == 5 + c.x = MAGIC + assert c.x == MAGIC + assert c.y == 4 + c.y = 6 + assert c.y == 6 + t: T = C() + assert t.y == 4 + t = c + assert t.x == MAGIC + c.x = 55 + assert t.x == 55 + assert t.y == 6 + a: Any = c + assert a.x == 55 + assert a.y == 6 + a.x = 7 + a.y = 8 + assert a.x == 7 + assert a.y == 8 + +class D(T): + xx: i64 + + @property + def x(self) -> i64: + return self.xx + + @property + def y(self) -> i64: + raise TypeError + +def test_read_only_property_in_trait_implemented_as_property() -> None: + d = D() + d.xx = 5 + assert d.x == 5 + d.xx = MAGIC + assert d.x == MAGIC + with assertRaises(TypeError): + d.y + t: T = d + assert t.x == MAGIC + d.xx = 6 + assert t.x == 6 + with assertRaises(TypeError): + t.y From b0ee424765d02361cbd5e2b08fbb5a9b5aaf8239 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 2 Jan 2023 11:47:21 +0000 Subject: [PATCH 18/23] Fix test case --- mypyc/test-data/run-classes.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 92e2af3b5306..4a082e535897 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2136,7 +2136,7 @@ def test_read_write_property_indirectly_implemented_as_attribute() -> None: assert c.y == 4 c.y = 6 assert c.y == 6 - t: T4 = C5() + t: T5 = C5() assert t.y == 4 t.x = 5 assert t.x == 5 From 479ffcde7b073d0525264f61eae7df41602ceee0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 2 Jan 2023 11:49:34 +0000 Subject: [PATCH 19/23] Minor refactoring --- mypyc/irbuild/prepare.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 8301879d66f5..6955480921be 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -360,7 +360,7 @@ def add_property_methods_for_attribute_if_needed( if isinstance(node, Decorator) and node.name not in ir.method_decls: # Defined as a read-only property in base class/trait add_getter_declaration(ir, attr_name, attr_rtype, module_name) - elif isinstance(node, OverloadedFuncDef) and is_property_with_setter(node): + elif isinstance(node, OverloadedFuncDef) and is_valid_multipart_property_def(node): # Defined as a read-write property in base class/trait add_getter_declaration(ir, attr_name, attr_rtype, module_name) add_setter_declaration(ir, attr_name, attr_rtype, module_name) @@ -391,15 +391,6 @@ def add_setter_declaration( ir.method_decls[setter_name] = decl -def is_property_with_setter(node: OverloadedFuncDef) -> bool: - return ( - len(node.items) == 2 - and isinstance(node.items[0], Decorator) - and isinstance(node.items[1], Decorator) - and node.items[0].func.is_property - ) - - def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: Mapper) -> None: # Set up a constructor decl init_node = cdef.info["__init__"].node From 039fe10cd940592ad72183b89faaeb93555253a6 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 2 Jan 2023 11:50:42 +0000 Subject: [PATCH 20/23] Fix self check --- mypy/checker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 1cd8368a082b..065758cd2be9 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2498,7 +2498,11 @@ class C(B, A[int]): ... # this is unsafe because... ok = is_equivalent(first_type, second_type) if not ok: second_node = base2[name].node - if isinstance(second_type, FunctionLike) and is_property(second_node): + if ( + isinstance(second_type, FunctionLike) + and second_node is not None + and is_property(second_node) + ): second_type = get_property_type(second_type) ok = is_subtype(first_type, second_type) else: From 1996b8adcaec8a0ec294907f4045e56a9f860cdb Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 2 Jan 2023 11:58:14 +0000 Subject: [PATCH 21/23] Improve comments and minor cleanup --- mypyc/irbuild/classdef.py | 3 ++- mypyc/irbuild/function.py | 8 ++++++++ mypyc/irbuild/prepare.py | 5 ++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index e01f2b675ee1..c6be541cfcd2 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -158,6 +158,7 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: else: builder.error("Unsupported statement in class body", stmt.line) + # Generate implicit property setters/getters for name, decl in ir.method_decls.items(): if decl.implicit and decl.is_prop_getter: getter_ir = gen_property_getter_ir(builder, decl, cdef) @@ -172,7 +173,7 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: ir.methods[setter_name] = setter_ir ir.properties[name] = (getter_ir, setter_ir) - # TODO: Generate glue method if needed + # TODO: Generate glue method if needed? # TODO: Do we need interpreted glue methods? Maybe not? cls_builder.finalize(ir) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index f4f4409dd3a7..5447f945db25 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -1029,6 +1029,10 @@ def get_native_impl_ids(builder: IRBuilder, singledispatch_func: FuncDef) -> dic def gen_property_getter_ir(builder: IRBuilder, func_decl: FuncDecl, cdef: ClassDef) -> FuncIR: + """Generate an implicit trivial property getter for an attribute. + + These are used if an attribute can also be accessed as a property. + """ name = func_decl.name builder.enter(name) self_reg = builder.add_argument("self", func_decl.sig.args[0].type) @@ -1039,6 +1043,10 @@ def gen_property_getter_ir(builder: IRBuilder, func_decl: FuncDecl, cdef: ClassD def gen_property_setter_ir(builder: IRBuilder, func_decl: FuncDecl, cdef: ClassDef) -> FuncIR: + """Generate an implicit trivial property setter for an attribute. + + These are used if an attribute can also be accessed as a property. + """ name = func_decl.name builder.enter(name) self_reg = builder.add_argument("self", func_decl.sig.args[0].type) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 6955480921be..d5a358592599 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -174,7 +174,7 @@ def prepare_method_def( # works correctly. decl.name = PROPSET_PREFIX + decl.name decl.is_prop_setter = True - # Making the argument positional-only avoids needless glue method generation + # Making the argument implicitly positional-only avoids unnecessary glue methods decl.sig.args[1].pos_only = True ir.method_decls[PROPSET_PREFIX + node.name] = decl @@ -310,8 +310,7 @@ def prepare_methods_and_attributes( if isinstance(node.node, Var): assert node.node.type, "Class member %s missing type" % name if not node.node.is_classvar and name not in ("__slots__", "__deletable__"): - attr_rtype = mapper.type_to_rtype(node.node.type) - ir.attributes[name] = attr_rtype + ir.attributes[name] = mapper.type_to_rtype(node.node.type) elif isinstance(node.node, (FuncDef, Decorator)): prepare_method_def(ir, module_name, cdef, mapper, node.node) elif isinstance(node.node, OverloadedFuncDef): From f5d45602b5aba2c7a3f0292d7f415322c9024a38 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 2 Jan 2023 12:01:07 +0000 Subject: [PATCH 22/23] Minor refactoring --- mypyc/irbuild/prepare.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index d5a358592599..eb8288b84818 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -282,10 +282,6 @@ def prepare_class_def( ir.base_mro = base_mro prepare_methods_and_attributes(cdef, ir, path, module_name, errors, mapper) - - if ir.builtin_base: - ir.attributes.clear() - prepare_init_method(cdef, ir, module_name, mapper) for base in bases: @@ -301,8 +297,7 @@ def prepare_methods_and_attributes( ) -> None: """Populate attribute and method declarations.""" info = cdef.info - # We sort the table for determinism here on Python 3.5. - for name, node in sorted(info.names.items()): + for name, node in info.names.items(): # Currently all plugin generated methods are dummies and not included. if node.plugin_generated: continue @@ -327,6 +322,9 @@ def prepare_methods_and_attributes( assert node.node.impl prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) + if ir.builtin_base: + ir.attributes.clear() + def prepare_implicit_property_accessors( info: TypeInfo, ir: ClassIR, module_name: str, mapper: Mapper From 8fc012e3ea2de588182fb263115baeedf1e1b449 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 10 Jan 2023 11:57:15 +0000 Subject: [PATCH 23/23] Make test cases compatible with CPython To override a property with an attribute, an initializer is needed. --- mypyc/test-data/run-classes.test | 10 +++++----- mypyc/test-data/run-i64.test | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 4a082e535897..92ec3873bf38 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -1980,7 +1980,7 @@ class T1: def y(self) -> int: ... class C1(T1): - x: int + x: int = 1 y: int = 4 def test_read_only_property_in_trait_implemented_as_attribute() -> None: @@ -2013,7 +2013,7 @@ class B2: return 25 class C2(B2): - x: int + x: int = 1 y: int = 4 def test_read_only_property_in_class_implemented_as_attribute() -> None: @@ -2044,7 +2044,7 @@ class T3: def y(self) -> int: ... class B3: - x: int + x: int = 1 y: int = 4 class C3(B3, T3): @@ -2083,7 +2083,7 @@ class T4: def y(self, v2: int) -> None: ... class C4(T4): - x: int + x: int = 1 y: int = 4 def test_read_write_property_implemented_as_attribute() -> None: @@ -2120,7 +2120,7 @@ class T5: def y(self, v2: int) -> None: ... class B5: - x: int + x: int = 1 y: int = 4 class BB5(B5): diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index 644b270a55f2..f0c664385d7b 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -1278,7 +1278,7 @@ class T: def y(self) -> i64: ... class C(T): - x: i64 + x: i64 = 1 y: i64 = 4 def test_read_only_property_in_trait_implemented_as_attribute() -> None: