Skip to content

Commit 2a1ae0e

Browse files
Fix the case where a function only raises.
1 parent a489e13 commit 2a1ae0e

File tree

2 files changed

+41
-17
lines changed

2 files changed

+41
-17
lines changed

Include/internal/pycore_opcode_utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ extern "C" {
5656

5757
#define IS_RETURN_OPCODE(opcode) \
5858
(opcode == RETURN_VALUE)
59+
#define IS_RAISE_OPCODE(opcode) \
60+
(opcode == RAISE_VARARGS || opcode == RERAISE)
5961

6062

6163
/* Flags used in the oparg for MAKE_FUNCTION */

Objects/codeobject.c

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,12 @@ _PyCode_ReturnsOnlyNone(PyCodeObject *co)
20412041
if (!_PyCode_CheckPureFunction(co, NULL)) {
20422042
return 0;
20432043
}
2044+
Py_ssize_t len = Py_SIZE(co);
2045+
assert(len > 0);
2046+
2047+
// The last instruction either returns or raises. We can take advantage
2048+
// of that for a quick exit.
2049+
_Py_CODEUNIT final = _Py_GetBaseCodeUnit(co, len-1);
20442050

20452051
// Look up None in co_consts.
20462052
Py_ssize_t nconsts = PyTuple_Size(co->co_consts);
@@ -2052,26 +2058,42 @@ _PyCode_ReturnsOnlyNone(PyCodeObject *co)
20522058
}
20532059
if (none_index == nconsts) {
20542060
// None wasn't there, which means there was no implicit return,
2055-
// "return", or "return None". That means there must be
2056-
// an explicit return (non-None).
2057-
return 0;
2058-
}
2061+
// "return", or "return None".
20592062

2060-
// Walk the bytecode, looking for RETURN_VALUE.
2061-
Py_ssize_t len = Py_SIZE(co);
2062-
for (int i = 0; i < len; i++) {
2063-
_Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i);
2064-
if (IS_RETURN_OPCODE(inst.op.code)) {
2065-
assert(i != 0);
2066-
// Ignore it if it returns None.
2067-
_Py_CODEUNIT prev = _Py_GetBaseCodeUnit(co, i-1);
2068-
if (prev.op.code == LOAD_CONST) {
2069-
// We don't worry about EXTENDED_ARG for now.
2070-
if (prev.op.arg == none_index) {
2071-
continue;
2063+
// That means there must be
2064+
// an explicit return (non-None), or it only raises.
2065+
if (IS_RETURN_OPCODE(final.op.code)) {
2066+
// It was an explicit return (non-None).
2067+
return 0;
2068+
}
2069+
// It must end with a raise then. We still have to walk the
2070+
// bytecode to see if there's any explicit return (non-None).
2071+
assert(IS_RAISE_OPCODE(final.op.code));
2072+
for (int i = 0; i < len; i++) {
2073+
_Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i);
2074+
if (IS_RETURN_OPCODE(inst.op.code)) {
2075+
// We alraedy know it isn't returning None.
2076+
return 0;
2077+
}
2078+
}
2079+
// It must only raise.
2080+
}
2081+
else {
2082+
// Walk the bytecode, looking for RETURN_VALUE.
2083+
for (int i = 0; i < len; i++) {
2084+
_Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i);
2085+
if (IS_RETURN_OPCODE(inst.op.code)) {
2086+
assert(i != 0);
2087+
// Ignore it if it returns None.
2088+
_Py_CODEUNIT prev = _Py_GetBaseCodeUnit(co, i-1);
2089+
if (prev.op.code == LOAD_CONST) {
2090+
// We don't worry about EXTENDED_ARG for now.
2091+
if (prev.op.arg == none_index) {
2092+
continue;
2093+
}
20722094
}
2095+
return 0;
20732096
}
2074-
return 0;
20752097
}
20762098
}
20772099
return 1;

0 commit comments

Comments
 (0)