Skip to content

Commit

Permalink
pythongh-109118: Fix runtime crash when NameError happens in PEP 695 …
Browse files Browse the repository at this point in the history
…function (python#109123)
  • Loading branch information
JelleZijlstra committed Sep 9, 2023
1 parent e9e2ca7 commit 17f9941
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 106 deletions.
90 changes: 38 additions & 52 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions Lib/test/test_type_params.py
Expand Up @@ -956,3 +956,43 @@ class NewStyle[T]:
for case in cases:
with self.subTest(case=case):
weakref.ref(case)


class TypeParamsRuntimeTest(unittest.TestCase):
def test_name_error(self):
# gh-109118: This crashed the interpreter due to a refcounting bug
code = """
class name_2[name_5]:
class name_4[name_5](name_0):
pass
"""
with self.assertRaises(NameError):
run_code(code)

# Crashed with a slightly different stack trace
code = """
class name_2[name_5]:
class name_4[name_5: name_5](name_0):
pass
"""
with self.assertRaises(NameError):
run_code(code)

def test_broken_class_namespace(self):
code = """
class WeirdMapping(dict):
def __missing__(self, key):
if key == "T":
raise RuntimeError
raise KeyError(key)
class Meta(type):
def __prepare__(name, bases):
return WeirdMapping()
class MyClass[V](metaclass=Meta):
class Inner[U](T):
pass
"""
with self.assertRaises(RuntimeError):
run_code(code)
@@ -0,0 +1,2 @@
Fix interpreter crash when a NameError is raised inside the type parameters
of a generic class.
10 changes: 8 additions & 2 deletions Python/abstract_interp_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 35 additions & 9 deletions Python/bytecodes.c
Expand Up @@ -1286,7 +1286,7 @@ dummy_func(
}
}

op(_LOAD_LOCALS, ( -- locals)) {
inst(LOAD_LOCALS, ( -- locals)) {
locals = LOCALS();
if (locals == NULL) {
_PyErr_SetString(tstate, PyExc_SystemError,
Expand All @@ -1296,15 +1296,11 @@ dummy_func(
Py_INCREF(locals);
}

macro(LOAD_LOCALS) = _LOAD_LOCALS;

op(_LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) {
inst(LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) {
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) {
Py_DECREF(mod_or_class_dict);
goto error;
}
Py_DECREF(mod_or_class_dict);
if (v == NULL) {
v = PyDict_GetItemWithError(GLOBALS(), name);
if (v != NULL) {
Expand All @@ -1325,11 +1321,41 @@ dummy_func(
}
}
}
DECREF_INPUTS();
}

macro(LOAD_NAME) = _LOAD_LOCALS + _LOAD_FROM_DICT_OR_GLOBALS;

macro(LOAD_FROM_DICT_OR_GLOBALS) = _LOAD_FROM_DICT_OR_GLOBALS;
inst(LOAD_NAME, (-- v)) {
PyObject *mod_or_class_dict = LOCALS();
if (mod_or_class_dict == NULL) {
_PyErr_SetString(tstate, PyExc_SystemError,
"no locals found");
ERROR_IF(true, error);
}
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) {
goto error;
}
if (v == NULL) {
v = PyDict_GetItemWithError(GLOBALS(), name);
if (v != NULL) {
Py_INCREF(v);
}
else if (_PyErr_Occurred(tstate)) {
goto error;
}
else {
if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) {
goto error;
}
if (v == NULL) {
_PyEval_FormatExcCheckArg(
tstate, PyExc_NameError,
NAME_ERROR_MSG, name);
goto error;
}
}
}
}

family(LOAD_GLOBAL, INLINE_CACHE_ENTRIES_LOAD_GLOBAL) = {
LOAD_GLOBAL_MODULE,
Expand Down
42 changes: 39 additions & 3 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 17f9941

Please sign in to comment.