8000 [3.14] gh-135437: Account For Duplicate Names in _PyCode_SetUnboundVa… · python/cpython@f77a911 · GitHub
[go: up one dir, main page]

Skip to content

Commit f77a911

Browse files
[3.14] gh-135437: Account For Duplicate Names in _PyCode_SetUnboundVarCounts() (gh-135493)
(cherry picked from commit 56eabea, AKA gh-135438) Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
1 parent d851f8e commit f77a911

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

Lib/test/_code_definitions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ def spam_with_globals_and_builtins():
5757
print(res)
5858

5959

60+
def spam_with_global_and_attr_same_name():
61+
try:
62+
spam_minimal.spam_minimal
63+
except AttributeError:
64+
pass
65+
66+
6067
def spam_full_args(a, b, /, c, d, *args, e, f, **kwargs):
6168
return (a, b, c, d, e, f, args, kwargs)
6269

@@ -190,6 +197,7 @@ def ham_C_closure(z):
190197
spam_minimal,
191198
spam_with_builtins,
192199
spam_with_globals_and_builtins,
200+
spam_with_global_and_attr_same_name,
193201
spam_full_args,
194202
spam_full_args_with_defaults,
195203
spam_args_attrs_and_builtins,
@@ -258,6 +266,7 @@ def ham_C_closure(z):
258266
script_with_globals,
259267
spam_full_args_with_defaults,
260268
spam_with_globals_and_builtins,
269+
spam_with_global_and_attr_same_name,
261270
spam_full,
262271
]
263272

@@ -275,6 +284,7 @@ def ham_C_closure(z):
275284
*PURE_SCRIPT_FUNCTIONS,
276285
script_with_globals,
277286
spam_with_globals_and_builtins,
287+
spam_with_global_and_attr_same_name,
278288
]
279289

280290

Lib/test/test_code.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ def test_local_kinds(self):
701701
'checks': CO_FAST_LOCAL,
702702
'res': CO_FAST_LOCAL,
703703
},
704+
defs.spam_with_global_and_attr_same_name: {},
704705
defs.spam_full_args: {
705706
'a': POSONLY,
706707
'b': POSONLY,
@@ -955,6 +956,10 @@ def new_var_counts(*,
955956
purelocals=5,
956957
globalvars=6,
957958
),
959+
defs.spam_with_global_and_attr_same_name: new_var_counts(
960+
globalvars=2,
961+
attrs=1,
962+
),
958963
defs.spam_full_args: new_var_counts(
959964
posonly=2,
960965
posorkw=2,

Objects/codeobject.c

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,7 +1714,7 @@ static int
17141714
identify_unbound_names(PyThreadState *tstate, PyCodeObject *co,
17151715
PyObject *globalnames, PyObject *attrnames,
17161716
PyObject *globalsns, PyObject *builtinsns,
1717-
struct co_unbound_counts *counts)
1717+
struct co_unbound_counts *counts, int *p_numdupes)
17181718
{
17191719
// This function is inspired by inspect.getclosurevars().
17201720
// It would be nicer if we had something similar to co_localspluskinds,
@@ -1729,6 +1729,7 @@ identify_unbound_names(PyThreadState *tstate, PyCodeObject *co,
17291729
assert(builtinsns == NULL || PyDict_Check(builtinsns));
17301730
assert(counts == NULL || counts->total == 0);
17311731
struct co_unbound_counts unbound = {0};
1732+
int numdupes = 0;
17321733
Py_ssize_t len = Py_SIZE(co);
17331734
for (int i = 0; i < len; i += _PyInstruction_GetLength(co, i)) {
17341735
_Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i);
@@ -1747,6 +1748,12 @@ identify_unbound_names(PyThreadState *tstate, PyCodeObject *co,
17471748
if (PySet_Add(attrnames, name) < 0) {
17481749
return -1;
17491750
}
1751+
if (PySet_Contains(globalnames, name)) {
1752+
if (_PyErr_Occurred(tstate)) {
1753+
return -1;
1754+
}
1755+
numdupes += 1;
1756+
}
17501757
}
17511758
else if (inst.op.code == LOAD_GLOBAL) {
17521759
int oparg = GET_OPARG(co, i, inst.op.arg);
@@ -1778,11 +1785,20 @@ identify_unbound_names(PyThreadState *tstate, PyCodeObject *co,
17781785
if (PySet_Add(globalnames, name) < 0) {
17791786
return -1;
17801787
}
1788+
if (PySet_Contains(attrnames, name)) {
1789+
if (_PyErr_Occurred(tstate)) {
1790+
return -1;
1791+
}
1792+
numdupes += 1;
1793+
}
17811794
}
17821795
}
17831796
if (counts != NULL) {
17841797
*counts = unbound;
17851798
}
1799+
if (p_numdupes != NULL) {
1800+
*p_numdupes = numdupes;
1801+
}
17861802
return 0;
17871803
}
17881804

@@ -1932,20 +1948,24 @@ _PyCode_SetUnboundVarCounts(PyThreadState *tstate,
19321948

19331949
// Fill in unbound.globals and unbound.numattrs.
19341950
struct co_unbound_counts unbound = {0};
1951+
int numdupes = 0;
19351952
Py_BEGIN_CRITICAL_SECTION(co);
19361953
res = identify_unbound_names(
19371954
tstate, co, globalnames, attrnames, globalsns, builtinsns,
1938-
&unbound);
1955+
&unbound, &numdupes);
19391956
Py_END_CRITICAL_SECTION();
19401957
if (res < 0) {
19411958
goto finally;
19421959
}
19431960
assert(unbound.numunknown == 0);
1944-
assert(unbound.total <= counts->unbound.total);
1961+
assert(unbound.total - numdupes <= counts->unbound.total);
19451962
assert(counts->unbound.numunknown == counts->unbound.total);
1946-
unbound.numunknown = counts->unbound.total - unbound.total;
1947-
unbound.total = counts->unbound.total;
1963+
// There may be a name that is both a global and an attr.
1964+
int totalunbound = counts->unbound.total + numdupes;
1965+
unbound.numunknown = totalunbound - unbound.total;
1966+
unbound.total = totalunbound;
19481967
counts->unbound = unbound;
1968+
counts->total += numdupes;
19491969
res = 0;
19501970

19511971
finally:

0 commit comments

Comments
 (0)
0