@@ -1714,7 +1714,7 @@ static int
17141714identify_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
19511971finally :
0 commit comments