Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
554e393
Add failing test cases
JukkaL Dec 18, 2022
96b2767
Fix accessing attribute that overrides a property
JukkaL Dec 18, 2022
f8f65cd
WIP partially enable test case
JukkaL Dec 18, 2022
c9f9ae8
Refactor mypyc.irbuild.prepare a little
JukkaL Dec 18, 2022
147ccde
More refactoring
JukkaL Dec 18, 2022
e507645
Prefer attributes over methods, if both are defined
JukkaL Dec 18, 2022
0243a1e
Generate method decl for implicit property getters
JukkaL Dec 18, 2022
15b7b30
Fix serialization of the "implicit" attribute
JukkaL Dec 18, 2022
8e638b0
Support simple use cases where an attribute overrides a property
JukkaL Dec 18, 2022
44075a9
Fix case where both attribute and property are inherited
JukkaL Dec 18, 2022
c756c36
Support settable properties
JukkaL Dec 30, 2022
de7e436
Black + isort
JukkaL Dec 30, 2022
d92905d
Merge test cases
JukkaL Dec 30, 2022
1fd4bc5
Merge more test cases
JukkaL Dec 30, 2022
d80842a
Fix edge case
JukkaL Dec 30, 2022
0274969
Test case fixes
JukkaL Dec 30, 2022
810c4e1
Add native int test cases
JukkaL Dec 30, 2022
b0ee424
Fix test case
JukkaL Jan 2, 2023
479ffcd
Minor refactoring
JukkaL Jan 2, 2023
039fe10
Fix self check
JukkaL Jan 2, 2023
1996b8a
Improve comments and minor cleanup
JukkaL Jan 2, 2023
f5d4560
Minor refactoring
JukkaL Jan 2, 2023
8fc012e
Make test cases compatible with CPython
JukkaL Jan 10, 2023
d948ce8
Merge branch 'master' into mypyc-property-inheritance
JukkaL Jan 10, 2023
158ee54
Merge remote-tracking branch 'origin/master' into mypyc-property-inhe…
JukkaL Jan 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Support settable properties
  • Loading branch information
JukkaL committed Dec 30, 2022
commit c756c365e4f796e8c1796f178e1e3352101fcdf0
21 changes: 15 additions & 6 deletions mypyc/irbuild/classdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?

Expand Down
15 changes: 14 additions & 1 deletion mypyc/irbuild/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
59 changes: 46 additions & 13 deletions mypyc/irbuild/prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion mypyc/sametype.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:])
)
)
Expand Down
4 changes: 2 additions & 2 deletions mypyc/test-data/run-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down