Skip to content

Commit c07f74f

Browse files
committed
Address review comments
1 parent 4344e50 commit c07f74f

File tree

8 files changed

+93
-55
lines changed

8 files changed

+93
-55
lines changed

mypyc/ir/ops.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,7 @@ def __init__(
12211221
var_arg_idx: int = -1,
12221222
*,
12231223
is_pure: bool = False,
1224+
returns_null: bool = False,
12241225
) -> None:
12251226
self.error_kind = error_kind
12261227
super().__init__(line)
@@ -1235,7 +1236,10 @@ def __init__(
12351236
# and all the arguments are immutable. Pure functions support
12361237
# additional optimizations. Pure functions never fail.
12371238
self.is_pure = is_pure
1238-
if is_pure:
1239+
# The function might return a null value that does not indicate
1240+
# an error.
1241+
self.returns_null = returns_null
1242+
if is_pure or returns_null:
12391243
assert error_kind == ERR_NEVER
12401244

12411245
def sources(self) -> list[Value]:

mypyc/irbuild/function.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,10 +375,10 @@ def generate_getattr_wrapper(builder: IRBuilder, cdef: ClassDef, getattr: FuncDe
375375
__getattr__ is not supported in classes that allow interpreted subclasses because the
376376
tp_getattro slot is inherited by subclasses and if the subclass overrides __getattr__,
377377
the override would be ignored in our wrapper. TODO: To support this, the wrapper would
378-
have to resolve "__getattr__" against the type at runtime and call the returned method,
379-
like _Py_slot_tp_getattr_hook in cpython.
378+
have to check type of self and if it's not the compiled class, resolve "__getattr__" against
379+
the type at runtime and call the returned method, like _Py_slot_tp_getattr_hook in cpython.
380380
381-
__getattr__ is not supported in classes which inherit from python classes because those
381+
__getattr__ is not supported in classes which inherit from non-native classes because those
382382
have __dict__ which currently has some strange interactions when class attributes and
383383
variables are assigned through __dict__ vs. through regular attribute access. Allowing
384384
__getattr__ on top of that could be problematic.
@@ -391,7 +391,7 @@ def generate_getattr_wrapper(builder: IRBuilder, cdef: ClassDef, getattr: FuncDe
391391
if ir.allow_interpreted_subclasses:
392392
builder.error(error_base + "it allows interpreted subclasses", line)
393393
if ir.inherits_python:
394-
builder.error(error_base + "it inherits from an interpreted class", line)
394+
builder.error(error_base + "it inherits from a non-native class", line)
395395

396396
with builder.enter_method(ir, name, object_rprimitive, internal=True):
397397
attr_arg = builder.add_argument("attr", object_rprimitive)

mypyc/irbuild/ll_builder.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,6 +2048,7 @@ def call_c(
20482048
line,
20492049
var_arg_idx,
20502050
is_pure=desc.is_pure,
2051+
returns_null=desc.returns_null,
20512052
)
20522053
)
20532054
if desc.is_borrowed:
@@ -2131,6 +2132,7 @@ def primitive_op(
21312132
desc.extra_int_constants,
21322133
desc.priority,
21332134
is_pure=desc.is_pure,
2135+
returns_null=False,
21342136
)
21352137
return self.call_c(c_desc, args, line, result_type=result_type)
21362138

mypyc/primitives/generic_ops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,5 +408,5 @@
408408
return_type=object_rprimitive,
409409
c_function_name="CPyObject_GenericGetAttr",
410410
error_kind=ERR_NEVER,
411-
is_borrowed=True,
411+
returns_null=True,
412412
)

mypyc/primitives/registry.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class CFunctionDescription(NamedTuple):
6161
extra_int_constants: list[tuple[int, RType]]
6262
priority: int
6363
is_pure: bool
64+
returns_null: bool
6465

6566

6667
# A description for C load operations including LoadGlobal and LoadAddress
@@ -253,6 +254,7 @@ def custom_op(
253254
is_borrowed: bool = False,
254255
*,
255256
is_pure: bool = False,
257+
returns_null: bool = False,
256258
) -> CFunctionDescription:
257259
"""Create a one-off CallC op that can't be automatically generated from the AST.
258260
@@ -274,6 +276,7 @@ def custom_op(
274276
extra_int_constants,
275277
0,
276278
is_pure=is_pure,
279+
returns_null=returns_null,
277280
)
278281

279282

mypyc/test-data/irbuild-classes.test

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2091,35 +2091,48 @@ class InterpSub:
20912091

20922092
[case testUnsupportedGetAttr]
20932093
from mypy_extensions import mypyc_attr
2094-
from typing import Optional
20952094

20962095
@mypyc_attr(allow_interpreted_subclasses=True)
20972096
class AllowsInterpreted:
2098-
def __getattr__(self, attr: str) -> Optional[object]: # E: "__getattr__" not supported in class "AllowsInterpreted" because it allows interpreted subclasses
2097+
def __getattr__(self, attr: str) -> object: # E: "__getattr__" not supported in class "AllowsInterpreted" because it allows interpreted subclasses
20992098
return 0
21002099

21012100
class InheritsInterpreted(dict):
2102-
def __getattr__(self, attr: str) -> Optional[object]: # E: "__getattr__" not supported in class "InheritsInterpreted" because it inherits from an interpreted class
2101+
def __getattr__(self, attr: str) -> object: # E: "__getattr__" not supported in class "InheritsInterpreted" because it inherits from a non-native class
2102+
return 0
2103+
2104+
@mypyc_attr(native_class=False)
2105+
class NonNative:
2106+
pass
2107+
2108+
class InheritsNonNative(NonNative):
2109+
def __getattr__(self, attr: str) -> object: # E: "__getattr__" not supported in class "InheritsNonNative" because it inherits from a non-native class
21032110
return 0
21042111

21052112
[case testGetAttr]
2106-
from typing import Optional, Tuple
2113+
from typing import ClassVar
21072114

21082115
class GetAttr:
21092116
class_var = "x"
2117+
class_var_annotated: ClassVar[int] = 99
21102118

21112119
def __init__(self, regular_attr: int):
21122120
self.regular_attr = regular_attr
21132121

2114-
def __getattr__(self, attr: str) -> Optional[object]:
2122+
def __getattr__(self, attr: str) -> object:
21152123
return attr
21162124

2117-
def test_getattr() -> Tuple[object, object, object]:
2125+
def method(self) -> int:
2126+
return 0
2127+
2128+
def test_getattr() -> list[object]:
21182129
i = GetAttr(42)
21192130
one = i.one
21202131
two = i.regular_attr
21212132
three = i.class_var
2122-
return (one, two, three)
2133+
four = i.class_var_annotated
2134+
five = i.method()
2135+
return [one, two, three, four, five]
21232136

21242137
[typing fixtures/typing-full.pyi]
21252138
[out]
@@ -2139,7 +2152,7 @@ def GetAttr.__getattr____wrapper(__mypyc_self__, attr):
21392152
attr, r0 :: object
21402153
r1 :: bit
21412154
r2 :: str
2142-
r3 :: union[object, None]
2155+
r3 :: object
21432156
L0:
21442157
r0 = CPyObject_GenericGetAttr(__mypyc_self__, attr)
21452158
r1 = r0 != 0
@@ -2150,6 +2163,10 @@ L2:
21502163
r2 = cast(str, attr)
21512164
r3 = __mypyc_self__.__getattr__(r2)
21522165
return r3
2166+
def GetAttr.method(self):
2167+
self :: __main__.GetAttr
2168+
L0:
2169+
return 0
21532170
def GetAttr.__mypyc_defaults_setup(__mypyc_self__):
21542171
__mypyc_self__ :: __main__.GetAttr
21552172
r0 :: str
@@ -2160,16 +2177,14 @@ L0:
21602177
def test_getattr():
21612178
r0, i :: __main__.GetAttr
21622179
r1 :: str
2163-
r2 :: object
2164-
one :: union[object, None]
2180+
r2, one :: object
21652181
r3, two :: int
2166-
r4, three :: str
2167-
r5 :: tuple[union[object, None], int, str]
2168-
r6 :: union[object, None]
2169-
r7 :: int
2170-
r8 :: str
2171-
r9 :: object
2172-
r10 :: tuple[union[object, None], object, str]
2182+
r4, three, r5 :: str
2183+
r6 :: object
2184+
r7, four, r8, five :: int
2185+
r9 :: list
2186+
r10, r11, r12 :: object
2187+
r13 :: ptr
21732188
L0:
21742189
r0 = GetAttr(84)
21752190
i = r0
@@ -2180,10 +2195,21 @@ L0:
21802195
two = r3
21812196
r4 = i.class_var
21822197
three = r4
2183-
r5 = (one, two, three)
2184-
r6 = r5[0]
2185-
r7 = r5[1]
2186-
r8 = r5[2]
2187-
r9 = box(int, r7)
2188-
r10 = (r6, r9, r8)
2189-
return r10
2198+
r5 = 'class_var_annotated'
2199+
r6 = CPyObject_GetAttr(i, r5)
2200+
r7 = unbox(int, r6)
2201+
four = r7
2202+
r8 = i.method()
2203+
five = r8
2204+
r9 = PyList_New(5)
2205+
r10 = box(int, two)
2206+
r11 = box(int, four)
2207+
r12 = box(int, five)
2208+
r13 = list_items r9
2209+
buf_init_item r13, 0, one
2210+
buf_init_item r13, 1, r10
2211+
buf_init_item r13, 2, three
2212+
buf_init_item r13, 3, r11
2213+
buf_init_item r13, 4, r12
2214+
keep_alive r9
2215+
return r9

mypyc/test-data/run-classes.test

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4091,54 +4091,54 @@ def test_inheritance_2() -> None:
40914091

40924092
[case testDunderGetAttr]
40934093
from mypy_extensions import mypyc_attr
4094-
from typing import Dict, Optional
4094+
from typing import ClassVar
40954095

40964096
class GetAttr:
40974097
class_var = "x"
40984098

4099-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int):
4099+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
41004100
self.extra_attrs = extra_attrs
41014101
self.regular_attr = regular_attr
41024102

4103-
def __getattr__(self, attr: str) -> Optional[object]:
4103+
def __getattr__(self, attr: str) -> object:
41044104
return self.extra_attrs.get(attr)
41054105

41064106
class GetAttrDefault:
4107-
class_var = "x"
4107+
class_var: ClassVar[str] = "x"
41084108

4109-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int):
4109+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
41104110
self.extra_attrs = extra_attrs
41114111
self.regular_attr = regular_attr
41124112

4113-
def __getattr__(self, attr: str, default: int = 8, mult: int = 1) -> Optional[object]:
4113+
def __getattr__(self, attr: str, default: int = 8, mult: int = 1) -> object:
41144114
return self.extra_attrs.get(attr, default * mult)
41154115

41164116
class GetAttrInherited(GetAttr):
41174117
subclass_var = "y"
41184118

4119-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int, sub_attr: int):
4119+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int):
41204120
super().__init__(extra_attrs, regular_attr)
41214121
self.sub_attr = sub_attr
41224122

41234123
class GetAttrOverridden(GetAttr):
4124-
subclass_var = "y"
4124+
subclass_var: ClassVar[str] = "y"
41254125

4126-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int, sub_attr: int):
4126+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int):
41274127
super().__init__(extra_attrs, regular_attr)
41284128
self.sub_attr = sub_attr
41294129

4130-
def __getattr__(self, attr: str) -> Optional[object]:
4130+
def __getattr__(self, attr: str) -> str:
41314131
return attr
41324132

41334133
@mypyc_attr(native_class=False)
41344134
class GetAttrNonNative:
41354135
class_var = "x"
41364136

4137-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int):
4137+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
41384138
self.extra_attrs = extra_attrs
41394139
self.regular_attr = regular_attr
41404140

4141-
def __getattr__(self, attr: str) -> Optional[object]:
4141+
def __getattr__(self, attr: str) -> object:
41424142
return self.extra_attrs.get(attr)
41434143

41444144
def test_getattr() -> None:
@@ -4302,54 +4302,54 @@ def test_getattr_nonnative() -> None:
43024302

43034303
[case testDunderGetAttrInterpreted]
43044304
from mypy_extensions import mypyc_attr
4305-
from typing import Dict, Optional
4305+
from typing import ClassVar
43064306

43074307
class GetAttr:
43084308
class_var = "x"
43094309

4310-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int):
4310+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
43114311
self.extra_attrs = extra_attrs
43124312
self.regular_attr = regular_attr
43134313

4314-
def __getattr__(self, attr: str) -> Optional[object]:
4314+
def __getattr__(self, attr: str) -> object:
43154315
return self.extra_attrs.get(attr)
43164316

43174317
class GetAttrDefault:
4318-
class_var = "x"
4318+
class_var: ClassVar[str] = "x"
43194319

4320-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int):
4320+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
43214321
self.extra_attrs = extra_attrs
43224322
self.regular_attr = regular_attr
43234323

4324-
def __getattr__(self, attr: str, default: int = 8, mult: int = 1) -> Optional[object]:
4324+
def __getattr__(self, attr: str, default: int = 8, mult: int = 1) -> object:
43254325
return self.extra_attrs.get(attr, default * mult)
43264326

43274327
class GetAttrInherited(GetAttr):
43284328
subclass_var = "y"
43294329

4330-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int, sub_attr: int):
4330+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int):
43314331
super().__init__(extra_attrs, regular_attr)
43324332
self.sub_attr = sub_attr
43334333

43344334
class GetAttrOverridden(GetAttr):
4335-
subclass_var = "y"
4335+
subclass_var: ClassVar[str] = "y"
43364336

4337-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int, sub_attr: int):
4337+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int):
43384338
super().__init__(extra_attrs, regular_attr)
43394339
self.sub_attr = sub_attr
43404340

4341-
def __getattr__(self, attr: str) -> Optional[object]:
4341+
def __getattr__(self, attr: str) -> str:
43424342
return attr
43434343

43444344
@mypyc_attr(native_class=False)
43454345
class GetAttrNonNative:
43464346
class_var = "x"
43474347

4348-
def __init__(self, extra_attrs: Dict[str, object], regular_attr: int):
4348+
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
43494349
self.extra_attrs = extra_attrs
43504350
self.regular_attr = regular_attr
43514351

4352-
def __getattr__(self, attr: str) -> Optional[object]:
4352+
def __getattr__(self, attr: str) -> object:
43534353
return self.extra_attrs.get(attr)
43544354

43554355
[file driver.py]

mypyc/transform/refcount.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
Assign,
3434
BasicBlock,
3535
Branch,
36+
CallC,
3637
ControlOp,
3738
DecRef,
3839
Goto,
@@ -89,7 +90,9 @@ def insert_ref_count_opcodes(ir: FuncIR) -> None:
8990

9091

9192
def is_maybe_undefined(post_must_defined: set[Value], src: Value) -> bool:
92-
return isinstance(src, Register) and src not in post_must_defined
93+
return (isinstance(src, Register) and src not in post_must_defined) or (
94+
isinstance(src, CallC) and src.returns_null
95+
)
9396

9497

9598
def maybe_append_dec_ref(

0 commit comments

Comments
 (0)