diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 0a5b708a2aefc8..014df211e6e424 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1256,6 +1256,12 @@ def test_gh_32092_oob(self): def test_gh_32092_kw_pass(self): ac_tester.gh_32092_kw_pass(1, 2, 3) + def test_gh_99233_refcount(self): + arg = '*A unique string is not referenced by anywhere else.*' + arg_refcount_origin = sys.getrefcount(arg) + ac_tester.gh_99233_refcount(arg) + arg_refcount_after = sys.getrefcount(arg) + self.assertEqual(arg_refcount_origin, arg_refcount_after) if __name__ == "__main__": unittest.main() diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 5e30db20a22d86..7c421164701d4b 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -1029,6 +1029,29 @@ gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args, } +/*[clinic input] +gh_99233_refcount + + *args: object + / + +Proof-of-concept of GH-99233 refcount error bug. + +While AC-generated code is packing varargs to a tuple, the arguments' refcounts are not increased. +So all the packed arguments‘ refcounts are decreased 1 improperly when the tuple is released later. + +Call this function with whatever arguments and check if the arguments' refcount is correct. + +[clinic start generated code]*/ + +static PyObject * +gh_99233_refcount_impl(PyObject *module, PyObject *args) +/*[clinic end generated code: output=585855abfbca9a7f input=574e697575fafec5]*/ +{ + Py_RETURN_NONE; +} + + static PyMethodDef tester_methods[] = { TEST_EMPTY_FUNCTION_METHODDEF OBJECTS_CONVERTER_METHODDEF @@ -1077,6 +1100,7 @@ static PyMethodDef tester_methods[] = { VARARG_WITH_ONLY_DEFAULTS_METHODDEF GH_32092_OOB_METHODDEF GH_32092_KW_PASS_METHODDEF + GH_99233_REFCOUNT_METHODDEF {NULL, NULL} }; diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index b3190fb7d67a54..0a7223d20d530b 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2706,4 +2706,41 @@ gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb Py_XDECREF(__clinic_args); return return_value; } -/*[clinic end generated code: output=e3773f2b7cac0719 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(gh_99233_refcount__doc__, +"gh_99233_refcount($module, /, *args)\n" +"--\n" +"\n" +"Proof-of-concept of GH-99233 refcount error bug.\n" +"\n" +"While AC-generated code is packing varargs to a tuple, the arguments\' refcounts are not increased.\n" +"So all the packed arguments‘ refcounts are decreased 1 improperly when the tuple is released later.\n" +"\n" +"Call this function with whatever arguments and check if the arguments\' refcount is correct."); + +#define GH_99233_REFCOUNT_METHODDEF \ + {"gh_99233_refcount", _PyCFunction_CAST(gh_99233_refcount), METH_FASTCALL, gh_99233_refcount__doc__}, + +static PyObject * +gh_99233_refcount_impl(PyObject *module, PyObject *args); + +static PyObject * +gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("gh_99233_refcount", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = PyTuple_New(nargs - 0); + for (Py_ssize_t i = 0; i < nargs - 0; ++i) { + PyTuple_SET_ITEM(__clinic_args, i, args[0 + i]); + } + return_value = gh_99233_refcount_impl(module, __clinic_args); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} +/*[clinic end generated code: output=ad9e0740f86bd28b input=a9049054013a1b77]*/