Skip to content

Commit 6c1eb5b

Browse files
authored
[mypyc] Raise AttributeError also for non-refcounted types (#11940)
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.
1 parent 48d810d commit 6c1eb5b

File tree

3 files changed

+33
-9
lines changed

3 files changed

+33
-9
lines changed

mypyc/codegen/emitfunc.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -292,17 +292,17 @@ def visit_get_attr(self, op: GetAttr) -> None:
292292
# Otherwise, use direct or offset struct access.
293293
attr_expr = self.get_attr_expr(obj, op, decl_cl)
294294
self.emitter.emit_line('{} = {};'.format(dest, attr_expr))
295+
self.emitter.emit_undefined_attr_check(
296+
attr_rtype, attr_expr, '==', unlikely=True
297+
)
298+
exc_class = 'PyExc_AttributeError'
299+
self.emitter.emit_line(
300+
'PyErr_SetString({}, "attribute {} of {} undefined");'.format(
301+
exc_class, repr(op.attr), repr(cl.name)))
295302
if attr_rtype.is_refcounted:
296-
self.emitter.emit_undefined_attr_check(
297-
attr_rtype, attr_expr, '==', unlikely=True
298-
)
299-
exc_class = 'PyExc_AttributeError'
300-
self.emitter.emit_lines(
301-
'PyErr_SetString({}, "attribute {} of {} undefined");'.format(
302-
exc_class, repr(op.attr), repr(cl.name)),
303-
'} else {')
303+
self.emitter.emit_line('} else {')
304304
self.emitter.emit_inc_ref(attr_expr, attr_rtype)
305-
self.emitter.emit_line('}')
305+
self.emitter.emit_line('}')
306306

307307
def visit_set_attr(self, op: SetAttr) -> None:
308308
dest = self.reg(op)

mypyc/test-data/run-classes.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ class C:
336336
b: bool
337337
c: C
338338
d: object
339+
e: int
339340

340341
def setattrs(o: C, a: List[int], b: bool, c: C) -> None:
341342
o.a = a
@@ -346,6 +347,8 @@ def getattrs(o: C) -> Tuple[List[int], bool, C]:
346347
return o.a, o.b, o.c
347348
[file driver.py]
348349
from native import C, setattrs, getattrs
350+
from testutil import assertRaises
351+
349352
c1 = C()
350353
c2 = C()
351354
aa = [2]
@@ -359,6 +362,18 @@ o = object()
359362
c1.d = o
360363
assert c1.d is o
361364

365+
c3 = C()
366+
with assertRaises(AttributeError, "attribute 'a' of 'C' undefined"):
367+
c3.a
368+
with assertRaises(AttributeError, "attribute 'b' of 'C' undefined"):
369+
c3.b
370+
with assertRaises(AttributeError, "attribute 'c' of 'C' undefined"):
371+
c3.c
372+
with assertRaises(AttributeError, "attribute 'd' of 'C' undefined"):
373+
c3.d
374+
with assertRaises(AttributeError, "attribute 'e' of 'C' undefined"):
375+
c3.e
376+
362377
[case testInitMethodWithMissingNoneReturnAnnotation]
363378
class C:
364379
def __init__(self):

mypyc/test/test_emitfunc.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ def test_get_attr(self) -> None:
288288
}
289289
""")
290290

291+
def test_get_attr_non_refcounted(self) -> None:
292+
self.assert_emit(
293+
GetAttr(self.r, 'x', 1),
294+
"""cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_x;
295+
if (unlikely(((mod___AObject *)cpy_r_r)->_x == 2)) {
296+
PyErr_SetString(PyExc_AttributeError, "attribute 'x' of 'A' undefined");
297+
}
298+
""")
299+
291300
def test_set_attr(self) -> None:
292301
self.assert_emit(
293302
SetAttr(self.r, 'y', self.m, 1),

0 commit comments

Comments
 (0)