Skip to content

Commit

Permalink
[mypyc] Raise AttributeError also for non-refcounted types (python#11940
Browse files Browse the repository at this point in the history
)

Previously we only raised it for refcounted types, potentially resulting in
errors like this:

```
SystemError: initialization of m failed without raising an exception
```

Unfortunately, this slows down the richards benchmark by about 15%. We
can get the lost performance back once we have support for always
defined attributes, at the latest.
  • Loading branch information
JukkaL authored Jan 11, 2022
1 parent 48d810d commit 6c1eb5b
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 9 deletions.
18 changes: 9 additions & 9 deletions mypyc/codegen/emitfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,17 +292,17 @@ def visit_get_attr(self, op: GetAttr) -> None:
# Otherwise, use direct or offset struct access.
attr_expr = self.get_attr_expr(obj, op, decl_cl)
self.emitter.emit_line('{} = {};'.format(dest, attr_expr))
self.emitter.emit_undefined_attr_check(
attr_rtype, attr_expr, '==', unlikely=True
)
exc_class = 'PyExc_AttributeError'
self.emitter.emit_line(
'PyErr_SetString({}, "attribute {} of {} undefined");'.format(
exc_class, repr(op.attr), repr(cl.name)))
if attr_rtype.is_refcounted:
self.emitter.emit_undefined_attr_check(
attr_rtype, attr_expr, '==', unlikely=True
)
exc_class = 'PyExc_AttributeError'
self.emitter.emit_lines(
'PyErr_SetString({}, "attribute {} of {} undefined");'.format(
exc_class, repr(op.attr), repr(cl.name)),
'} else {')
self.emitter.emit_line('} else {')
self.emitter.emit_inc_ref(attr_expr, attr_rtype)
self.emitter.emit_line('}')
self.emitter.emit_line('}')

def visit_set_attr(self, op: SetAttr) -> None:
dest = self.reg(op)
Expand Down
15 changes: 15 additions & 0 deletions mypyc/test-data/run-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ class C:
b: bool
c: C
d: object
e: int

def setattrs(o: C, a: List[int], b: bool, c: C) -> None:
o.a = a
Expand All @@ -346,6 +347,8 @@ def getattrs(o: C) -> Tuple[List[int], bool, C]:
return o.a, o.b, o.c
[file driver.py]
from native import C, setattrs, getattrs
from testutil import assertRaises

c1 = C()
c2 = C()
aa = [2]
Expand All @@ -359,6 +362,18 @@ o = object()
c1.d = o
assert c1.d is o

c3 = C()
with assertRaises(AttributeError, "attribute 'a' of 'C' undefined"):
c3.a
with assertRaises(AttributeError, "attribute 'b' of 'C' undefined"):
c3.b
with assertRaises(AttributeError, "attribute 'c' of 'C' undefined"):
c3.c
with assertRaises(AttributeError, "attribute 'd' of 'C' undefined"):
c3.d
with assertRaises(AttributeError, "attribute 'e' of 'C' undefined"):
c3.e

[case testInitMethodWithMissingNoneReturnAnnotation]
class C:
def __init__(self):
Expand Down
9 changes: 9 additions & 0 deletions mypyc/test/test_emitfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,15 @@ def test_get_attr(self) -> None:
}
""")

def test_get_attr_non_refcounted(self) -> None:
self.assert_emit(
GetAttr(self.r, 'x', 1),
"""cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_x;
if (unlikely(((mod___AObject *)cpy_r_r)->_x == 2)) {
PyErr_SetString(PyExc_AttributeError, "attribute 'x' of 'A' undefined");
}
""")

def test_set_attr(self) -> None:
self.assert_emit(
SetAttr(self.r, 'y', self.m, 1),
Expand Down

0 comments on commit 6c1eb5b

Please sign in to comment.