@@ -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 );
@@ -3431,40 +3432,47 @@ dict_set_fromkeys(PyDictObject *mp, PyObject *iterable, PyObject *value)
34313432PyObject *
34323433_PyDict_FromKeys (PyObject * cls , PyObject * iterable , PyObject * value )
34333434{
3434- PyObject * it ; /* iter(iterable) */
3435- PyObject * key ;
3435+ PyObject * it = NULL ; /* iter(iterable) */
34363436 PyObject * d ;
3437- int status ;
3437+ int need_copy = 0 ;
34383438
3439- d = _PyObject_CallNoArgs (cls );
3439+ if (cls == (PyObject * )& PyFrozenDict_Type ) {
3440+ // gh-151722: Create a frozendict which is not tracked by the GC.
3441+ d = frozendict_new_untracked (& PyFrozenDict_Type );
3442+ }
3443+ else {
3444+ // Dict subclass, or frozendict subclass which overrides
3445+ // the constructor.
3446+ d = _PyObject_CallNoArgs (cls );
3447+ }
34403448 if (d == NULL ) {
34413449 return NULL ;
34423450 }
34433451
3444- // If cls is a dict or frozendict subclass with overridden constructor,
3445- // copy the frozendict.
3446- PyTypeObject * cls_type = _PyType_CAST (cls );
3447- if (PyFrozenDict_Check (d ) && cls_type -> tp_new != frozendict_new ) {
3448- // Subclass-friendly copy
3449- PyObject * copy ;
3450- if (PyObject_IsSubclass (cls , (PyObject * )& PyFrozenDict_Type )) {
3451- copy = frozendict_new (cls_type , NULL , NULL );
3452- }
3453- else {
3454- copy = dict_new (cls_type , NULL , NULL );
3455- }
3452+ // gh-151722: If cls constructor returns a frozendict which is tracked by
3453+ // the GC, create a frozendict copy which is not tracked by the GC.
3454+ //
3455+ // At the function exit, return cls(fd) where fd is a frozendict.
3456+ //
3457+ // Untracking the frozendict requires tracking again the frozendict on
3458+ // error which is more complicated. It's easier to work on a copy.
3459+ if (PyFrozenDict_Check (d ) && _PyObject_GC_IS_TRACKED (d )) {
3460+ need_copy = 1 ;
3461+
3462+ PyObject * copy = frozendict_new_untracked (& PyFrozenDict_Type );
34563463 if (copy == NULL ) {
3457- Py_DECREF (d );
3458- return NULL ;
3464+ goto Fail ;
34593465 }
34603466 if (dict_merge (copy , d , 1 , NULL ) < 0 ) {
3461- Py_DECREF (d );
34623467 Py_DECREF (copy );
3463- return NULL ;
3468+ goto Fail ;
34643469 }
34653470 Py_SETREF (d , copy );
34663471 }
3467- assert (!PyFrozenDict_Check (d ) || can_modify_dict ((PyDictObject * )d ));
3472+ if (PyFrozenDict_Check (d )) {
3473+ assert (can_modify_dict ((PyDictObject * )d ));
3474+ assert (!_PyObject_GC_IS_TRACKED (d ));
3475+ }
34683476
34693477 if (PyDict_CheckExact (d )) {
34703478 if (PyDict_CheckExact (iterable )) {
@@ -3473,23 +3481,23 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
34733481 Py_BEGIN_CRITICAL_SECTION2 (d , iterable );
34743482 d = (PyObject * )dict_dict_fromkeys (mp , iterable , value );
34753483 Py_END_CRITICAL_SECTION2 ();
3476- return d ;
3484+ goto Done ;
34773485 }
34783486 else if (PyFrozenDict_CheckExact (iterable )) {
34793487 PyDictObject * mp = (PyDictObject * )d ;
34803488
34813489 Py_BEGIN_CRITICAL_SECTION (d );
34823490 d = (PyObject * )dict_dict_fromkeys (mp , iterable , value );
34833491 Py_END_CRITICAL_SECTION ();
3484- return d ;
3492+ goto Done ;
34853493 }
34863494 else if (PyAnySet_CheckExact (iterable )) {
34873495 PyDictObject * mp = (PyDictObject * )d ;
34883496
34893497 Py_BEGIN_CRITICAL_SECTION2 (d , iterable );
34903498 d = (PyObject * )dict_set_fromkeys (mp , iterable , value );
34913499 Py_END_CRITICAL_SECTION2 ();
3492- return d ;
3500+ goto Done ;
34933501 }
34943502 }
34953503 else if (PyFrozenDict_CheckExact (d )) {
@@ -3499,71 +3507,113 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
34993507 Py_BEGIN_CRITICAL_SECTION (iterable );
35003508 d = (PyObject * )dict_dict_fromkeys (mp , iterable , value );
35013509 Py_END_CRITICAL_SECTION ();
3502- return d ;
3510+ goto Done ;
35033511 }
35043512 else if (PyFrozenDict_CheckExact (iterable )) {
35053513 PyDictObject * mp = (PyDictObject * )d ;
35063514 d = (PyObject * )dict_dict_fromkeys (mp , iterable , value );
3507- return d ;
3515+ goto Done ;
35083516 }
35093517 else if (PyAnySet_CheckExact (iterable )) {
35103518 PyDictObject * mp = (PyDictObject * )d ;
35113519
35123520 Py_BEGIN_CRITICAL_SECTION (iterable );
35133521 d = (PyObject * )dict_set_fromkeys (mp , iterable , value );
35143522 Py_END_CRITICAL_SECTION ();
3515- return d ;
3523+ goto Done ;
35163524 }
35173525 }
35183526
35193527 it = PyObject_GetIter (iterable );
35203528 if (it == NULL ){
3521- Py_DECREF (d );
3522- return NULL ;
3529+ goto Fail ;
35233530 }
35243531
35253532 if (PyDict_CheckExact (d )) {
3533+ int status = 0 ;
3534+
35263535 Py_BEGIN_CRITICAL_SECTION (d );
3527- while ((key = PyIter_Next (it )) != NULL ) {
3536+ while (1 ) {
3537+ PyObject * key ;
3538+ status = PyIter_NextItem (it , & key );
3539+ if (status <= 0 ) {
3540+ break ;
3541+ }
3542+
35283543 status = setitem_lock_held ((PyDictObject * )d , key , value );
35293544 Py_DECREF (key );
35303545 if (status < 0 ) {
3531- assert (PyErr_Occurred ());
3532- goto dict_iter_exit ;
3546+ break ;
35333547 }
35343548 }
3535- dict_iter_exit :;
35363549 Py_END_CRITICAL_SECTION ();
3550+
3551+ if (status < 0 ) {
3552+ goto Fail ;
3553+ }
35373554 }
35383555 else if (PyFrozenDict_Check (d )) {
3539- while ((key = PyIter_Next (it )) != NULL ) {
3556+ while (1 ) {
3557+ PyObject * key ;
3558+ int status = PyIter_NextItem (it , & key );
3559+ if (status < 0 ) {
3560+ goto Fail ;
3561+ }
3562+ if (status == 0 ) {
3563+ break ;
3564+ }
3565+
35403566 // setitem_take2_lock_held consumes a reference to key
35413567 status = setitem_take2_lock_held ((PyDictObject * )d ,
35423568 key , Py_NewRef (value ));
35433569 if (status < 0 ) {
3544- assert (PyErr_Occurred ());
35453570 goto Fail ;
35463571 }
35473572 }
35483573 }
35493574 else {
3550- while ((key = PyIter_Next (it )) != NULL ) {
3575+ while (1 ) {
3576+ PyObject * key ;
3577+ int status = PyIter_NextItem (it , & key );
3578+ if (status < 0 ) {
3579+ goto Fail ;
3580+ }
3581+ if (status == 0 ) {
3582+ break ;
3583+ }
3584+
35513585 status = PyObject_SetItem (d , key , value );
35523586 Py_DECREF (key );
3553- if (status < 0 )
3587+ if (status < 0 ) {
35543588 goto Fail ;
3589+ }
35553590 }
3591+
35563592 }
35573593
3558- if (PyErr_Occurred ())
3559- goto Fail ;
3594+ assert (!PyErr_Occurred ());
35603595 Py_DECREF (it );
3561- return d ;
3596+ goto Done ;
35623597
35633598Fail :
3564- Py_DECREF (it );
3599+ assert (PyErr_Occurred ());
3600+ Py_XDECREF (it );
35653601 Py_DECREF (d );
35663602 return NULL ;
3603+
3604+ Done :
3605+ if (d == NULL ) {
3606+ return NULL ;
3607+ }
3608+
3609+ if (need_copy ) {
3610+ PyObject * copy = _PyObject_CallOneArg (cls , d );
3611+ Py_SETREF (d , copy );
3612+ }
3613+ else if (!_PyObject_GC_IS_TRACKED (d )) {
3614+ _PyObject_GC_TRACK (d );
3615+ }
3616+ return d ;
35673617}
35683618
35693619/* Methods */
@@ -4164,9 +4214,6 @@ dict_dict_merge(PyDictObject *mp, PyDictObject *other, int override, PyObject **
41644214 set_keys (mp , keys );
41654215 STORE_USED (mp , other -> ma_used );
41664216 ASSERT_CONSISTENT (mp );
4167- if (PyDict_Check (mp )) {
4168- assert (_PyObject_GC_IS_TRACKED (mp ));
4169- }
41704217 return 0 ;
41714218 }
41724219 }
@@ -4333,7 +4380,12 @@ dict_merge_api(PyObject *a, PyObject *b, int override, PyObject **dupkey)
43334380 }
43344381 return -1 ;
43354382 }
4336- return dict_merge (a , b , override , dupkey );
4383+
4384+ int res = dict_merge (a , b , override , dupkey );
4385+ if (PyDict_Check (a )) {
4386+ assert (_PyObject_GC_IS_TRACKED (a ));
4387+ }
4388+ return res ;
43374389}
43384390
43394391int
@@ -4492,10 +4544,15 @@ copy_lock_held(PyObject *o, int as_frozendict)
44924544 }
44934545 if (copy == NULL )
44944546 return NULL ;
4495- if (dict_merge (copy , o , 1 , NULL ) == 0 )
4496- return copy ;
4497- Py_DECREF (copy );
4498- return NULL ;
4547+ if (dict_merge (copy , o , 1 , NULL ) < 0 ) {
4548+ Py_DECREF (copy );
4549+ return NULL ;
4550+ }
4551+
4552+ if (PyDict_Check (copy )) {
4553+ assert (_PyObject_GC_IS_TRACKED (copy ));
4554+ }
4555+ return copy ;
44994556}
45004557
45014558PyObject *
@@ -5256,11 +5313,11 @@ static PyNumberMethods dict_as_number = {
52565313 .nb_inplace_or = _PyDict_IOr ,
52575314};
52585315
5259- static PyObject *
5260- dict_new_untracked (PyTypeObject * type )
5316+ static PyObject *
5317+ anydict_new_untracked (PyTypeObject * type )
52615318{
52625319 assert (type != NULL );
5263- // dict subclasses must implement the GC protocol
5320+ // dict and frozendict subclasses must implement the GC protocol
52645321 assert (_PyType_IS_GC (type ));
52655322
52665323 PyObject * self = _PyType_AllocNoTrack (type , 0 );
@@ -5279,6 +5336,14 @@ dict_new_untracked(PyTypeObject *type)
52795336 return self ;
52805337}
52815338
5339+ static PyObject *
5340+ dict_new_untracked (PyTypeObject * type )
5341+ {
5342+ assert (PyObject_IsSubclass ((PyObject * )type , (PyObject * )& PyDict_Type ));
5343+
5344+ return anydict_new_untracked (type );
5345+ }
5346+
52825347static PyObject *
52835348dict_new (PyTypeObject * type , PyObject * Py_UNUSED (args ), PyObject * Py_UNUSED (kwds ))
52845349{
@@ -8394,7 +8459,9 @@ frozendict_hash(PyObject *op)
83948459static PyObject *
83958460frozendict_new_untracked (PyTypeObject * type )
83968461{
8397- PyObject * d = dict_new_untracked (type );
8462+ assert (PyObject_IsSubclass ((PyObject * )type , (PyObject * )& PyFrozenDict_Type ));
8463+
8464+ PyObject * d = anydict_new_untracked (type );
83988465 if (d == NULL ) {
83998466 return NULL ;
84008467 }
0 commit comments