@@ -140,6 +140,7 @@ static PyObject* frozendict_new(PyTypeObject *type, PyObject *args,
140140 PyObject * kwds );
141141static PyObject * frozendict_new_untracked (PyTypeObject * type );
142142static PyObject * dict_new (PyTypeObject * type , PyObject * args , PyObject * kwds );
143+ static PyObject * dict_new_untracked (PyTypeObject * type );
143144static int dict_merge (PyObject * a , PyObject * b , int override , PyObject * * dupkey );
144145static int dict_contains (PyObject * op , PyObject * key );
145146static int dict_merge_from_seq2 (PyObject * d , PyObject * seq2 , int override );
@@ -3387,40 +3388,47 @@ dict_set_fromkeys(PyDictObject *mp, PyObject *iterable, PyObject *value)
33873388PyObject *
33883389_PyDict_FromKeys (PyObject * cls , PyObject * iterable , PyObject * value )
33893390{
3390- PyObject * it ; /* iter(iterable) */
3391- PyObject * key ;
3391+ PyObject * it = NULL ; /* iter(iterable) */
33923392 PyObject * d ;
3393- int status ;
3393+ int need_copy = 0 ;
33943394
3395- d = _PyObject_CallNoArgs (cls );
3395+ if (cls == (PyObject * )& PyFrozenDict_Type ) {
3396+ // gh-151722: Create a frozendict which is not tracked by the GC.
3397+ d = frozendict_new_untracked (& PyFrozenDict_Type );
3398+ }
3399+ else {
3400+ // Dict subclass, or frozendict subclass which overrides
3401+ // the constructor.
3402+ d = _PyObject_CallNoArgs (cls );
3403+ }
33963404 if (d == NULL ) {
33973405 return NULL ;
33983406 }
33993407
3400- // If cls is a dict or frozendict subclass with overridden constructor,
3401- // copy the frozendict.
3402- PyTypeObject * cls_type = _PyType_CAST (cls );
3403- if (PyFrozenDict_Check (d ) && cls_type -> tp_new != frozendict_new ) {
3404- // Subclass-friendly copy
3405- PyObject * copy ;
3406- if (PyObject_IsSubclass (cls , (PyObject * )& PyFrozenDict_Type )) {
3407- copy = frozendict_new (cls_type , NULL , NULL );
3408- }
3409- else {
3410- copy = dict_new (cls_type , NULL , NULL );
3411- }
3408+ // gh-151722: If cls constructor returns a frozendict which is tracked by
3409+ // the GC, create a frozendict copy which is not tracked by the GC.
3410+ //
3411+ // At the function exit, return cls(fd) where fd is a frozendict.
3412+ //
3413+ // Untracking the frozendict requires tracking again the frozendict on
3414+ // error which is more complicated. It's easier to work on a copy.
3415+ if (PyFrozenDict_Check (d ) && _PyObject_GC_IS_TRACKED (d )) {
3416+ need_copy = 1 ;
3417+
3418+ PyObject * copy = frozendict_new_untracked (& PyFrozenDict_Type );
34123419 if (copy == NULL ) {
3413- Py_DECREF (d );
3414- return NULL ;
3420+ goto Fail ;
34153421 }
34163422 if (dict_merge (copy , d , 1 , NULL ) < 0 ) {
3417- Py_DECREF (d );
34183423 Py_DECREF (copy );
3419- return NULL ;
3424+ goto Fail ;
34203425 }
34213426 Py_SETREF (d , copy );
34223427 }
3423- assert (!PyFrozenDict_Check (d ) || can_modify_dict ((PyDictObject * )d ));
3428+ if (PyFrozenDict_Check (d )) {
3429+ assert (can_modify_dict ((PyDictObject * )d ));
3430+ assert (!_PyObject_GC_IS_TRACKED (d ));
3431+ }
34243432
34253433 if (PyDict_CheckExact (d )) {
34263434 if (PyDict_CheckExact (iterable )) {
@@ -3429,23 +3437,23 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
34293437 Py_BEGIN_CRITICAL_SECTION2 (d , iterable );
34303438 d = (PyObject * )dict_dict_fromkeys (mp , iterable , value );
34313439 Py_END_CRITICAL_SECTION2 ();
3432- return d ;
3440+ goto Done ;
34333441 }
34343442 else if (PyFrozenDict_CheckExact (iterable )) {
34353443 PyDictObject * mp = (PyDictObject * )d ;
34363444
34373445 Py_BEGIN_CRITICAL_SECTION (d );
34383446 d = (PyObject * )dict_dict_fromkeys (mp , iterable , value );
34393447 Py_END_CRITICAL_SECTION ();
3440- return d ;
3448+ goto Done ;
34413449 }
34423450 else if (PyAnySet_CheckExact (iterable )) {
34433451 PyDictObject * mp = (PyDictObject * )d ;
34443452
34453453 Py_BEGIN_CRITICAL_SECTION2 (d , iterable );
34463454 d = (PyObject * )dict_set_fromkeys (mp , iterable , value );
34473455 Py_END_CRITICAL_SECTION2 ();
3448- return d ;
3456+ goto Done ;
34493457 }
34503458 }
34513459 else if (PyFrozenDict_CheckExact (d )) {
@@ -3455,71 +3463,113 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
34553463 Py_BEGIN_CRITICAL_SECTION (iterable );
34563464 d = (PyObject * )dict_dict_fromkeys (mp , iterable , value );
34573465 Py_END_CRITICAL_SECTION ();
3458- return d ;
3466+ goto Done ;
34593467 }
34603468 else if (PyFrozenDict_CheckExact (iterable )) {
34613469 PyDictObject * mp = (PyDictObject * )d ;
34623470 d = (PyObject * )dict_dict_fromkeys (mp , iterable , value );
3463- return d ;
3471+ goto Done ;
34643472 }
34653473 else if (PyAnySet_CheckExact (iterable )) {
34663474 PyDictObject * mp = (PyDictObject * )d ;
34673475
34683476 Py_BEGIN_CRITICAL_SECTION (iterable );
34693477 d = (PyObject * )dict_set_fromkeys (mp , iterable , value );
34703478 Py_END_CRITICAL_SECTION ();
3471- return d ;
3479+ goto Done ;
34723480 }
34733481 }
34743482
34753483 it = PyObject_GetIter (iterable );
34763484 if (it == NULL ){
3477- Py_DECREF (d );
3478- return NULL ;
3485+ goto Fail ;
34793486 }
34803487
34813488 if (PyDict_CheckExact (d )) {
3489+ int status = 0 ;
3490+
34823491 Py_BEGIN_CRITICAL_SECTION (d );
3483- while ((key = PyIter_Next (it )) != NULL ) {
3492+ while (1 ) {
3493+ PyObject * key ;
3494+ status = PyIter_NextItem (it , & key );
3495+ if (status <= 0 ) {
3496+ break ;
3497+ }
3498+
34843499 status = setitem_lock_held ((PyDictObject * )d , key , value );
34853500 Py_DECREF (key );
34863501 if (status < 0 ) {
3487- assert (PyErr_Occurred ());
3488- goto dict_iter_exit ;
3502+ break ;
34893503 }
34903504 }
3491- dict_iter_exit :;
34923505 Py_END_CRITICAL_SECTION ();
3506+
3507+ if (status < 0 ) {
3508+ goto Fail ;
3509+ }
34933510 }
34943511 else if (PyFrozenDict_Check (d )) {
3495- while ((key = PyIter_Next (it )) != NULL ) {
3512+ while (1 ) {
3513+ PyObject * key ;
3514+ int status = PyIter_NextItem (it , & key );
3515+ if (status < 0 ) {
3516+ goto Fail ;
3517+ }
3518+ if (status == 0 ) {
3519+ break ;
3520+ }
3521+
34963522 // setitem_take2_lock_held consumes a reference to key
34973523 status = setitem_take2_lock_held ((PyDictObject * )d ,
34983524 key , Py_NewRef (value ));
34993525 if (status < 0 ) {
3500- assert (PyErr_Occurred ());
35013526 goto Fail ;
35023527 }
35033528 }
35043529 }
35053530 else {
3506- while ((key = PyIter_Next (it )) != NULL ) {
3531+ while (1 ) {
3532+ PyObject * key ;
3533+ int status = PyIter_NextItem (it , & key );
3534+ if (status < 0 ) {
3535+ goto Fail ;
3536+ }
3537+ if (status == 0 ) {
3538+ break ;
3539+ }
3540+
35073541 status = PyObject_SetItem (d , key , value );
35083542 Py_DECREF (key );
3509- if (status < 0 )
3543+ if (status < 0 ) {
35103544 goto Fail ;
3545+ }
35113546 }
3547+
35123548 }
35133549
3514- if (PyErr_Occurred ())
3515- goto Fail ;
3550+ assert (!PyErr_Occurred ());
35163551 Py_DECREF (it );
3517- return d ;
3552+ goto Done ;
35183553
35193554Fail :
3520- Py_DECREF (it );
3555+ assert (PyErr_Occurred ());
3556+ Py_XDECREF (it );
35213557 Py_DECREF (d );
35223558 return NULL ;
3559+
3560+ Done :
3561+ if (d == NULL ) {
3562+ return NULL ;
3563+ }
3564+
3565+ if (need_copy ) {
3566+ PyObject * copy = _PyObject_CallOneArg (cls , d );
3567+ Py_SETREF (d , copy );
3568+ }
3569+ else if (!_PyObject_GC_IS_TRACKED (d )) {
3570+ _PyObject_GC_TRACK (d );
3571+ }
3572+ return d ;
35233573}
35243574
35253575/* Methods */
@@ -4120,9 +4170,6 @@ dict_dict_merge(PyDictObject *mp, PyDictObject *other, int override, PyObject **
41204170 set_keys (mp , keys );
41214171 STORE_USED (mp , other -> ma_used );
41224172 ASSERT_CONSISTENT (mp );
4123- if (PyDict_Check (mp )) {
4124- assert (_PyObject_GC_IS_TRACKED (mp ));
4125- }
41264173 return 0 ;
41274174 }
41284175 }
@@ -4289,7 +4336,12 @@ dict_merge_api(PyObject *a, PyObject *b, int override, PyObject **dupkey)
42894336 }
42904337 return -1 ;
42914338 }
4292- return dict_merge (a , b , override , dupkey );
4339+
4340+ int res = dict_merge (a , b , override , dupkey );
4341+ if (PyDict_Check (a )) {
4342+ assert (_PyObject_GC_IS_TRACKED (a ));
4343+ }
4344+ return res ;
42934345}
42944346
42954347int
@@ -4448,10 +4500,15 @@ copy_lock_held(PyObject *o, int as_frozendict)
44484500 }
44494501 if (copy == NULL )
44504502 return NULL ;
4451- if (dict_merge (copy , o , 1 , NULL ) == 0 )
4452- return copy ;
4453- Py_DECREF (copy );
4454- return NULL ;
4503+ if (dict_merge (copy , o , 1 , NULL ) < 0 ) {
4504+ Py_DECREF (copy );
4505+ return NULL ;
4506+ }
4507+
4508+ if (PyDict_Check (copy )) {
4509+ assert (_PyObject_GC_IS_TRACKED (copy ));
4510+ }
4511+ return copy ;
44554512}
44564513
44574514PyObject *
@@ -5212,11 +5269,11 @@ static PyNumberMethods dict_as_number = {
52125269 .nb_inplace_or = _PyDict_IOr ,
52135270};
52145271
5215- static PyObject *
5216- dict_new_untracked (PyTypeObject * type )
5272+ static PyObject *
5273+ anydict_new_untracked (PyTypeObject * type )
52175274{
52185275 assert (type != NULL );
5219- // dict subclasses must implement the GC protocol
5276+ // dict and frozendict subclasses must implement the GC protocol
52205277 assert (_PyType_IS_GC (type ));
52215278
52225279 PyObject * self = _PyType_AllocNoTrack (type , 0 );
@@ -5235,6 +5292,14 @@ dict_new_untracked(PyTypeObject *type)
52355292 return self ;
52365293}
52375294
5295+ static PyObject *
5296+ dict_new_untracked (PyTypeObject * type )
5297+ {
5298+ assert (PyObject_IsSubclass ((PyObject * )type , (PyObject * )& PyDict_Type ));
5299+
5300+ return anydict_new_untracked (type );
5301+ }
5302+
52385303static PyObject *
52395304dict_new (PyTypeObject * type , PyObject * Py_UNUSED (args ), PyObject * Py_UNUSED (kwds ))
52405305{
@@ -8323,7 +8388,9 @@ frozendict_hash(PyObject *op)
83238388static PyObject *
83248389frozendict_new_untracked (PyTypeObject * type )
83258390{
8326- PyObject * d = dict_new_untracked (type );
8391+ assert (PyObject_IsSubclass ((PyObject * )type , (PyObject * )& PyFrozenDict_Type ));
8392+
8393+ PyObject * d = anydict_new_untracked (type );
83278394 if (d == NULL ) {
83288395 return NULL ;
83298396 }
0 commit comments