public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v4] Add support for creating new types from the Python API
@ 2024-01-16  4:54 Matheus Branco Borella
  2024-01-16 12:45 ` Eli Zaretskii
  2024-02-06 18:20 ` Tom Tromey
  0 siblings, 2 replies; 10+ messages in thread
From: Matheus Branco Borella @ 2024-01-16  4:54 UTC (permalink / raw)
  To: gdb-patches; +Cc: eli, Matheus Branco Borella

This patch adds support for creating types from within the Python API. It does
so by exposing the `init_*_type` family of functions, defined in `gdbtypes.h` to
Python and having them return `gdb.Type` objects connected to the newly minted
types.

These functions are accessible in the root of the gdb module and all require
a reference to either a `gdb.Objfile` or a `gdb.Architecture`. Types created
from them will be owned by the object passed to the function.

This patch also adds an extra type - `gdb.FloatFormat` - to support creation of
floating point types by letting users control the format from within Python. It
is missing, however, a way to specify half formats and validation functions.

It is important to note that types created using this interface are not
automatically registered as a symbol, and so, types will become unreachable
unless used to create a value that otherwise references it or saved in some way.

The main drawback of using the `init_*_type` family over implementing type
initialization by hand is that any type that's created gets immediately
allocated on its owner's obstack, regardless of what its real lifetime
requirements are. The main implication of this is that types that become
unreachable will remain live for the lifetime of the owner.

Keeping track of the initialization of the type by hand would require a
deeper change to the existing type object infrastructure. A bit too ambitious
for a first patch, I'd say.

If it were to be done though, we would gain the ability to only keep in the
obstack types that are known to be referenced in some other way - by allocating
and copying the data to the obstack as other objects are created that reference
it (eg. symbols).
---
 gdb/Makefile.in                           |   2 +
 gdb/NEWS                                  |  16 +
 gdb/doc/python.texi                       | 161 +++++++
 gdb/python/py-float-format.c              | 307 +++++++++++++
 gdb/python/py-objfile.c                   |  17 +
 gdb/python/py-type-init.c                 | 520 ++++++++++++++++++++++
 gdb/python/python-internal.h              |  34 ++
 gdb/python/python.c                       |  50 +++
 gdb/testsuite/gdb.python/py-type-init.c   |  21 +
 gdb/testsuite/gdb.python/py-type-init.exp | 132 ++++++
 10 files changed, 1260 insertions(+)
 create mode 100644 gdb/python/py-float-format.c
 create mode 100644 gdb/python/py-type-init.c
 create mode 100644 gdb/testsuite/gdb.python/py-type-init.c
 create mode 100644 gdb/testsuite/gdb.python/py-type-init.exp

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 195f3a2e2d1..50a758c802b 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -432,6 +432,8 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-threadevent.c \
 	python/py-tui.c \
 	python/py-type.c \
+	python/py-type-init.c \
+	python/py-float-format.c \
 	python/py-unwind.c \
 	python/py-utils.c \
 	python/py-value.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index 11cd6c0663e..e541544a027 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -87,6 +87,22 @@ show remote thread-options-packet
   ** New function gdb.interrupt(), that interrupts GDB as if the user
      typed control-c.
 
+  ** Functions that allow creation of instances of gdb.Type, and a new
+     class gdb.FloatFormat that may be used to create floating point
+     types.  The functions that allow new type creation are:
+      - gdb.init_type: Create a new type given a type code.
+      - gdb.init_integer_type: Create a new integer type.
+      - gdb.init_character_type: Create a new character type.
+      - gdb.init_boolean_type: Create a new boolean type.
+      - gdb.init_float_type: Create a new floating point type.
+      - gdb.init_decfloat_type: Create a new decimal floating point type.
+      - gdb.can_create_complex_type: Whether a type can be used to create a
+          new complex type.
+      - gdb.init_complex_type: Create a new complex type.
+      - gdb.init_pointer_type: Create a new pointer type.
+          * This allows creating pointers of arbitrary size.
+      - gdb.init_fixed_point_type: Create a new fixed point type.
+
 * Debugger Adapter Protocol changes
 
   ** GDB now emits the "process" event.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index d74defeec0c..e79e4b1ac89 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1743,6 +1743,167 @@ A Fortran namelist.
 Further support for types is provided in the @code{gdb.types}
 Python module (@pxref{gdb.types}).
 
+
+
+@node Creating Types In Python
+@subsubsection Creating Types In Python
+@cindex creating types in Python
+@cindex Python, working with types
+
+@value{GDBN} allows creation of new types from Python extensions.
+
+The following functions available in the @code{gdb} module create
+new types.
+
+They all return an instance of @code{gdb.Type}, and will throw an
+exception in case of an error, unless stated otherwise.  Arguments that
+have the same name behave the same for all functions.
+
+@findex gdb.init_type
+@defun gdb.init_type (owner, type_code, bit_size, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+type owned by the given @var{owner}, with the given @var{type_code},
+@var{name} and size.
+
+@var{owner} must be a reference to either a @code{gdb.Objfile} or a
+@code{gdb.Architecture} object.  These correspond to objfile and
+architecture-owned types, respectively.
+
+@var{type_code} is one of the @code{TYPE_CODE_} constants defined in
+@ref{Types In Python}.
+
+@var{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+@end defun
+
+@findex gdb.init_integer_type
+@defun gdb.init_integer_type (owner, bit_size, unsigned, name)
+This function creates a new @code{gdb.Type} instance corresponding to an
+integer type owned by the given @var{owner}, with the given
+@var{name}, size and signedness.
+
+@var{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+@end defun
+
+@findex gdb.init_character_type
+@defun gdb.init_character_type (owner, bit_size, unsigned, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+character type owned by the given @var{owner}, with the given
+@var{name}, size and signedness.
+
+This function 
+@end defun
+
+@findex gdb.init_boolean_type
+@defun gdb.init_boolean_type (owner, bit_size, unsigned, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+boolean type owned by the given @var{owner}, with the given
+@var{name}, size and signedness.
+@end defun
+
+@findex gdb.init_float_type
+@defun gdb.init_float_type (owner, format, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+floating point type owned by the given @var{owner}, with the given
+@var{name} and @var{format}.
+
+@var{format} is an reference to a @code{gdb.FloatFormat} object, as
+described below.
+@end defun
+
+@findex gdb.init_decfloat_type
+@defun gdb.init_decfloat_type (owner, bit_size, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+decimal floating point type owned by the given @var{owner}, with the
+given @var{name} and size.
+@end defun
+
+@findex gdb.can_create_complex_type
+@defun gdb.can_create_complex_type (type)
+This function returns a boolean indicating whether @var{type} can be
+used to create a new complex type using the @code{gdb.init_complex_type}
+function.
+@end defun
+
+@findex gdb.init_complex_type
+@defun gdb.init_complex_type (type, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+complex type with the given @var{name} based on the given base
+@var{type}.
+
+The newly created type will be owned by the same object as the base
+type that was used to create it.
+@end defun
+
+@findex gdb.init_pointer_type
+@defun gdb.init_pointer_type (owner, target, bit_size, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+pointer type that points to @var{target} and is owned by the given
+@var{owner}, with the given @var{name} and size.
+
+@var{target} is a @code{gdb.Type} object, corresponding to the type
+that will be pointed to by the newly created pointer type.
+@end defun
+
+@findex gdb.init_fixed_point_type
+@defun gdb.init_fixed_point_type (owner, bit_size, unsigned, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+fixed point type owned by the given @var{owner}, with the given
+@var{name}, size and signedness.
+@end defun
+
+When creating a floating point type through @code{gdb.init_float_type},
+one has to use a @code{gdb.FloatFormat} object. These objects may be
+created with no arguments, and the following attributes may be used to
+defined the format of the desired floating point format:
+
+@defvar FloatFormat.totalsize
+The size of the floating point number, in bits. Currently, accepted
+values are limited to multiples of 8.
+@end defvar
+
+@defvar FloatFormat.sign_start
+The bit offset of the sign bit.
+@end defvar
+
+@defvar FloatFormt.exp_start
+The bit offset of the start of the exponent.
+@end defvar
+
+@defvar FloatFormat.exp_len
+The size of the exponent, in bits.
+@end defvar
+
+@defvar FloatFormat.exp_bias
+Bias added to the written exponent to form the biased exponent.
+@end defvar
+
+@defvar FloatFormat.exp_nan
+Exponent value which indicates NaN.
+@end defvar
+
+@defvar FloatFormat.man_start
+The bit offset of the start of the mantissa.
+@end defvar
+
+@defvar FloatFormat.man_len
+The size of the mantissa, in bits.
+@end defvar
+
+@defvar FloatFormat.intbit
+This is a boolean values that indicates whether the integer bit is part
+of the value or if it is determined implicitly. A value of true
+indicates the former, while a value of false indicates the latter.
+@end defvar
+
+@defvar FloatFormat.name
+The name of the float format. Used internally, for debugging purposes.
+@end defvar
+
+
+
 @node Pretty Printing API
 @subsubsection Pretty Printing API
 @cindex python pretty printing api
diff --git a/gdb/python/py-float-format.c b/gdb/python/py-float-format.c
new file mode 100644
index 00000000000..984b96361a7
--- /dev/null
+++ b/gdb/python/py-float-format.c
@@ -0,0 +1,307 @@
+/* Accessibility of float format controls from inside the Python API
+
+   Copyright (C) 2008-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "python-internal.h"
+#include "floatformat.h"
+
+/* Structure backing the float format Python interface. */
+
+struct float_format_object
+{
+  PyObject_HEAD
+  struct floatformat format;
+
+  struct floatformat *float_format ()
+  {
+    return &this->format;
+  }
+};
+
+/* Initializes the float format type and registers it with the Python
+ * interpreter. */
+
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
+gdbpy_initialize_float_format (void)
+{
+  if (PyType_Ready (&float_format_object_type) < 0)
+    return -1;
+
+  if (gdb_pymodule_addobject (gdb_module, "FloatFormat",
+			      (PyObject *) &float_format_object_type) < 0)
+    return -1;
+
+  return 0;
+}
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_float_format);
+
+/* Creates a function that gets the value of a field of a given name from the
+ * underliying float_format structure in the Python object. */
+
+#define INSTANCE_FIELD_GETTER(getter_name, field_name, field_type, field_conv)\
+  static PyObject *							      \
+  getter_name (PyObject *self, void *closure)				      \
+  {									      \
+    float_format_object *ff = (float_format_object*) self;		      \
+    field_type value = ff->float_format ()->field_name;			      \
+    return field_conv (value);						      \
+  }
+
+/* Creates a function that sets the value of a field of a given name from the
+ * underliying float_format structure in the Python object. */
+
+#define INSTANCE_FIELD_SETTER(setter_name, field_name, field_type, field_conv)\
+  static int								      \
+  setter_name (PyObject *self, PyObject* value, void *closure)		      \
+  {									      \
+    field_type native_value;						      \
+    if (!field_conv (value, &native_value))				      \
+      return -1;							      \
+    float_format_object *ff = (float_format_object*) self;		      \
+    ff->float_format ()->field_name = native_value;			      \
+    return 0;								      \
+  }
+
+/* Converts from the intbit enum to a Python boolean. */
+
+static PyObject *
+intbit_to_py (enum floatformat_intbit intbit)
+{
+  gdb_assert (intbit == floatformat_intbit_yes
+	      || intbit == floatformat_intbit_no);
+
+  if (intbit == floatformat_intbit_no)
+    Py_RETURN_FALSE;
+  else
+    Py_RETURN_TRUE;
+}
+
+/* Converts from a Python boolean to the intbit enum. */
+
+static bool
+py_to_intbit (PyObject *object, enum floatformat_intbit *intbit)
+{
+  if (!PyObject_IsInstance (object, (PyObject*) &PyBool_Type))
+    {
+      PyErr_SetString (PyExc_TypeError, "intbit must be True or False");
+      return false;
+    }
+
+  *intbit = PyObject_IsTrue (object) ? floatformat_intbit_yes
+    : floatformat_intbit_no;
+
+  return true;
+}
+
+/* Converts from a Python integer to a unsigned integer. */
+
+static bool
+py_to_unsigned_int (PyObject *object, unsigned int *val)
+{
+  if (!PyObject_IsInstance (object, (PyObject*) &PyLong_Type))
+    {
+      PyErr_SetString (PyExc_TypeError, "value must be an integer");
+      return false;
+    }
+
+  long native_val = PyLong_AsLong (object);
+  if (native_val > (long) UINT_MAX)
+    {
+      PyErr_SetString (PyExc_ValueError, "value is too large");
+      return false;
+    }
+  if (native_val < 0)
+    {
+      PyErr_SetString (PyExc_ValueError,
+		       "value must not be smaller than zero");
+      return false;
+    }
+
+  *val = (unsigned int) native_val;
+  return true;
+}
+
+/* Converts from a Python integer to a signed integer. */
+
+static bool
+py_to_int(PyObject *object, int *val)
+{
+  if(!PyObject_IsInstance(object, (PyObject*)&PyLong_Type))
+    {
+      PyErr_SetString(PyExc_TypeError, "value must be an integer");
+      return false;
+    }
+
+  long native_val = PyLong_AsLong(object);
+  if(native_val > (long)INT_MAX)
+    {
+      PyErr_SetString(PyExc_ValueError, "value is too large");
+      return false;
+    }
+
+  *val = (int)native_val;
+  return true;
+}
+
+/* Instantiate functions for all of the float format fields we'd like to be
+ * able to read and change from our Python object. These will be used later to
+ * define `getset` entries for them. */
+
+INSTANCE_FIELD_GETTER (ffpy_get_totalsize, totalsize,
+		       unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_sign_start, sign_start,
+		       unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_exp_start, exp_start,
+		       unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_exp_len, exp_len,
+		       unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_exp_bias, exp_bias, int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_exp_nan, exp_nan,
+		       unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_man_start, man_start,
+		       unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_man_len, man_len,
+		       unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_intbit, intbit,
+		       enum floatformat_intbit, intbit_to_py)
+INSTANCE_FIELD_GETTER (ffpy_get_name, name,
+		       const char *, PyUnicode_FromString)
+
+INSTANCE_FIELD_SETTER (ffpy_set_totalsize, totalsize,
+		       unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_sign_start, sign_start,
+		       unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_exp_start, exp_start,
+		       unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_exp_len, exp_len,
+		       unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_exp_bias, exp_bias, int, py_to_int)
+INSTANCE_FIELD_SETTER (ffpy_set_exp_nan, exp_nan,
+		       unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_man_start, man_start,
+		       unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_man_len, man_len,
+		       unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_intbit, intbit,
+		       enum floatformat_intbit, py_to_intbit)
+
+/* Makes sure float formats created from Python always test as valid. */
+
+static int
+ffpy_always_valid (const struct floatformat *fmt ATTRIBUTE_UNUSED,
+		   const void *from ATTRIBUTE_UNUSED)
+{
+  return 1;
+}
+
+/* Initializes new float format objects. */
+
+static int
+ffpy_init (PyObject *self,
+	   PyObject *args ATTRIBUTE_UNUSED,
+	   PyObject *kwds ATTRIBUTE_UNUSED)
+{
+  auto ff = (float_format_object*) self;
+  ff->format = floatformat ();
+  ff->float_format ()->name = "";
+  ff->float_format ()->is_valid = ffpy_always_valid;
+  return 0;
+}
+
+/* See python/python-internal.h. */
+
+struct floatformat *
+float_format_object_as_float_format (PyObject *self)
+{
+  if (!PyObject_TypeCheck (self, &float_format_object_type))
+    {
+      PyErr_SetString(PyExc_TypeError, "expected gdb.FloatFormat");
+      return nullptr;
+    }
+  return ((float_format_object*) self)->float_format ();
+}
+
+static gdb_PyGetSetDef float_format_object_getset[] =
+{
+  { "totalsize", ffpy_get_totalsize, ffpy_set_totalsize,
+    "The total size of the floating point number, in bits.", nullptr },
+  { "sign_start", ffpy_get_sign_start, ffpy_set_sign_start,
+    "The bit offset of the sign bit.", nullptr },
+  { "exp_start", ffpy_get_exp_start, ffpy_set_exp_start,
+    "The bit offset of the start of the exponent.", nullptr },
+  { "exp_len", ffpy_get_exp_len, ffpy_set_exp_len,
+    "The size of the exponent, in bits.", nullptr },
+  { "exp_bias", ffpy_get_exp_bias, ffpy_set_exp_bias,
+    "Bias added to the written exponent to form the biased exponent.",
+    nullptr },
+  { "exp_nan", ffpy_get_exp_nan, ffpy_set_exp_nan,
+    "Exponent value which indicates NaN.", nullptr },
+  { "man_start", ffpy_get_man_start, ffpy_set_man_start,
+    "The bit offset of the start of the mantissa.", nullptr },
+  { "man_len", ffpy_get_man_len, ffpy_set_man_len,
+    "The size of the mantissa, in bits.", nullptr },
+  { "intbit", ffpy_get_intbit, ffpy_set_intbit,
+    "Is the integer bit explicit or implicit?", nullptr },
+  { "name", ffpy_get_name, nullptr,
+    "Internal name for debugging.", nullptr },
+  { nullptr }
+};
+
+PyTypeObject float_format_object_type =
+{
+  PyVarObject_HEAD_INIT (NULL, 0)
+  "gdb.FloatFormat",		  /*tp_name*/
+  sizeof (float_format_object),   /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  nullptr,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  nullptr,			  /*tp_getattr*/
+  nullptr,			  /*tp_setattr*/
+  nullptr,			  /*tp_compare*/
+  nullptr,			  /*tp_repr*/
+  nullptr,			  /*tp_as_number*/
+  nullptr,			  /*tp_as_sequence*/
+  nullptr,			  /*tp_as_mapping*/
+  nullptr,			  /*tp_hash */
+  nullptr,			  /*tp_call*/
+  nullptr,			  /*tp_str*/
+  nullptr,			  /*tp_getattro*/
+  nullptr,			  /*tp_setattro*/
+  nullptr,			  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB float format object",      /* tp_doc */
+  nullptr,			  /* tp_traverse */
+  nullptr,			  /* tp_clear */
+  nullptr,			  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  nullptr,			  /* tp_iter */
+  nullptr,			  /* tp_iternext */
+  nullptr,			  /* tp_methods */
+  nullptr,			  /* tp_members */
+  float_format_object_getset,     /* tp_getset */
+  nullptr,			  /* tp_base */
+  nullptr,			  /* tp_dict */
+  nullptr,			  /* tp_descr_get */
+  nullptr,			  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  ffpy_init,			  /* tp_init */
+  nullptr,			  /* tp_alloc */
+  PyType_GenericNew,		  /* tp_new */
+};
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index bb5d0d92aba..71d840c3e00 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -705,6 +705,23 @@ objfile_to_objfile_object (struct objfile *objfile)
   return gdbpy_ref<>::new_reference (result);
 }
 
+/* See python/python-internal.h. */
+
+struct objfile *
+objfile_object_to_objfile (PyObject *self)
+{
+  if (!PyObject_TypeCheck (self, &objfile_object_type))
+    {
+      PyErr_SetString(PyExc_TypeError, "expected gdb.Objfile");
+      return nullptr;
+    }
+
+  auto objfile_object = (struct objfile_object*) self;
+  OBJFPY_REQUIRE_VALID (objfile_object);
+
+  return objfile_object->objfile;
+}
+
 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_objfile (void)
 {
diff --git a/gdb/python/py-type-init.c b/gdb/python/py-type-init.c
new file mode 100644
index 00000000000..58f29393413
--- /dev/null
+++ b/gdb/python/py-type-init.c
@@ -0,0 +1,520 @@
+/* Functionality for creating new types accessible from python.
+
+   Copyright (C) 2008-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "python-internal.h"
+#include "gdbtypes.h"
+#include "floatformat.h"
+#include "objfiles.h"
+#include "gdbsupport/gdb_obstack.h"
+
+
+/* An abstraction covering the objects types that can own a type object. */
+
+class type_storage_owner
+{
+public:
+  /* Creates a new type owner from the given python object. If the object is
+   * of a type that is not supported, the newly created instance will be
+   * marked as invalid and nothing should be done with it. */
+
+  type_storage_owner (PyObject *owner)
+  {
+    if (gdbpy_is_architecture (owner))
+      {
+	this->kind = owner_kind::arch;
+	this->owner.arch = arch_object_to_gdbarch (owner);
+	return;
+      }
+
+    this->kind = owner_kind::objfile;
+    this->owner.objfile = objfile_object_to_objfile (owner);
+    if (this->owner.objfile != nullptr)
+	return;
+
+    this->kind = owner_kind::none;
+    PyErr_SetString(PyExc_TypeError, "unsupported owner type");
+  }
+
+  /* Whether the owner is valid. An owner may not be valid if the type that
+   * was used to create it is not known. Operations must only be done on valid
+   * instances of this class. */
+
+  bool valid ()
+  {
+    return this->kind != owner_kind::none;
+  }
+
+  /* Returns a type allocator that allocates on this owner. */
+
+  type_allocator allocator ()
+  {
+    gdb_assert (this->valid ());
+
+    if (this->kind == owner_kind::arch)
+      return type_allocator (this->owner.arch);
+    else if (this->kind == owner_kind::objfile)
+      {
+	/* Creating types on the gdbarch sets their language to minimal, we
+	 * maintain this behavior here. */
+	return type_allocator (this->owner.objfile, language_minimal);
+      }
+
+    /* Should never be reached, but it's better to fail in a safe way than try
+     * to instance the allocator with arbitraty parameters here. */
+    abort ();
+  }
+
+  /* Get a reference to the owner's obstack. */
+
+  obstack *get_obstack ()
+  {
+    gdb_assert (this->valid ());
+
+    if (this->kind == owner_kind::arch)
+	return gdbarch_obstack (this->owner.arch);
+    else if (this->kind == owner_kind::objfile)
+	return &this->owner.objfile->objfile_obstack;
+
+    return nullptr;
+  }
+
+  /* Get a reference to the owner's architecture. */
+
+  struct gdbarch *get_arch ()
+  {
+    gdb_assert (this->valid ());
+
+    if (this->kind == owner_kind::arch)
+	return this->owner.arch;
+    else if (this->kind == owner_kind::objfile)
+	return this->owner.objfile->arch ();
+
+    return nullptr;
+  }
+
+  /* Copy a null-terminated string to the owner's obstack. */
+
+  const char *copy_string (const char *py_str)
+  {
+    gdb_assert (this->valid ());
+
+    unsigned int len = strlen (py_str);
+    return obstack_strndup (this->get_obstack (), py_str, len);
+  }
+
+
+
+private:
+  enum class owner_kind { arch, objfile, none };
+
+  owner_kind kind = owner_kind::none;
+  union {
+    struct gdbarch *arch;
+    struct objfile *objfile;
+  } owner;
+};
+
+/* Creates a new type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "owner", "type_code", "bit_size", "name",
+				    NULL };
+  PyObject *owner_object;
+  enum type_code code;
+  int bit_length;
+  const char *py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oiis", keywords, &owner_object,
+					&code, &bit_length, &py_name))
+    return nullptr;
+
+  type_storage_owner owner (owner_object);
+  if (!owner.valid ())
+    return nullptr;
+
+  const char *name = owner.copy_string (py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator = owner.allocator ();
+      type = allocator.new_type (code, bit_length, name);
+      gdb_assert (type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object (type);
+}
+
+/* Creates a new integer type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_integer_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "owner", "bit_size", "unsigned", "name",
+				    NULL };
+  PyObject *owner_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+					&owner_object, &bit_size, &unsigned_p,
+					&py_name))
+    return nullptr;
+
+  type_storage_owner owner (owner_object);
+  if (!owner.valid ())
+    return nullptr;
+
+  const char *name = owner.copy_string (py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator = owner.allocator ();
+      type = init_integer_type (allocator, bit_size, unsigned_p, name);
+      gdb_assert (type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object(type);
+}
+
+/* Creates a new character type and returns a new gdb.Type associated
+ * with it. */
+
+PyObject *
+gdbpy_init_character_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "owner", "bit_size", "unsigned", "name",
+				    NULL };
+  PyObject *owner_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+					&owner_object, &bit_size, &unsigned_p,
+					&py_name))
+    return nullptr;
+
+  type_storage_owner owner (owner_object);
+  if (!owner.valid ())
+    return nullptr;
+
+  const char *name = owner.copy_string (py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator = owner.allocator ();
+      type = init_character_type (allocator, bit_size, unsigned_p, name);
+      gdb_assert (type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object (type);
+}
+
+/* Creates a new boolean type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_boolean_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "owner", "bit_size", "unsigned", "name",
+				    NULL };
+  PyObject *owner_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+					&owner_object, &bit_size, &unsigned_p,
+					&py_name))
+    return nullptr;
+
+  type_storage_owner owner (owner_object);
+  if (!owner.valid ())
+    return nullptr;
+
+  const char *name = owner.copy_string (py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator = owner.allocator ();
+      type = init_boolean_type (allocator, bit_size, unsigned_p, name);
+      gdb_assert (type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object (type);
+}
+
+/* Creates a new float type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_float_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "owner", "format", "name", NULL };
+  PyObject *owner_object, *float_format_object;
+  const char *py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OOs", keywords, &owner_object,
+					&float_format_object, &py_name))
+    return nullptr;
+
+  type_storage_owner owner (owner_object);
+  if (!owner.valid ())
+    return nullptr;
+
+  struct floatformat *local_ff = float_format_object_as_float_format
+    (float_format_object);
+  if (local_ff == nullptr)
+    return nullptr;
+
+  /* Persist a copy of the format in the objfile's obstack. This guarantees
+   * that the format won't outlive the type being created from it and that
+   * changes made to the object used to create this type will not affect it
+   * after creation. */
+  auto ff = OBSTACK_CALLOC (owner.get_obstack (), 1, struct floatformat);
+  memcpy (ff, local_ff, sizeof (struct floatformat));
+
+  /* We only support creating float types in the architecture's endianness, so
+   * make sure init_float_type sees the float format structure we need it to.
+   */
+  enum bfd_endian endianness = gdbarch_byte_order (owner.get_arch ());
+  gdb_assert (endianness < BFD_ENDIAN_UNKNOWN);
+
+  const struct floatformat *per_endian[2] = { nullptr, nullptr };
+  per_endian[endianness] = ff;
+
+  const char *name = owner.copy_string (py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator = owner.allocator ();
+      type = init_float_type (allocator, -1, name, per_endian, endianness);
+      gdb_assert (type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object (type);
+}
+
+/* Creates a new decimal float type and returns a new gdb.Type
+ * associated with it. */
+
+PyObject *
+gdbpy_init_decfloat_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "owner", "bit_size", "name", NULL };
+  PyObject *owner_object;
+  int bit_length;
+  const char *py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Ois", keywords, &owner_object,
+					&bit_length, &py_name))
+    return nullptr;
+
+  type_storage_owner owner (owner_object);
+  if (!owner.valid ())
+    return nullptr;
+
+  const char *name = owner.copy_string (py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator = owner.allocator ();
+      type = init_decfloat_type (allocator, bit_length, name);
+      gdb_assert (type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object (type);
+}
+
+/* Returns whether a given type can be used to create a complex type. */
+
+PyObject *
+gdbpy_can_create_complex_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "type", NULL };
+  PyObject *type_object;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O", keywords,
+					&type_object))
+    return nullptr;
+
+  struct type *type = type_object_to_type (type_object);
+  if (type == nullptr)
+    return nullptr;
+
+  bool can_create_complex = false;
+  try
+    {
+      can_create_complex = can_create_complex_type (type);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  if (can_create_complex)
+    Py_RETURN_TRUE;
+  else
+    Py_RETURN_FALSE;
+}
+
+/* Creates a new complex type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_complex_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "type", "name", NULL };
+  PyObject *type_object;
+  const char *py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Os", keywords, &type_object,
+					&py_name))
+    return nullptr;
+
+  struct type *type = type_object_to_type (type_object);
+  if (type == nullptr)
+    return nullptr;
+
+  obstack *obstack;
+  if (type->is_objfile_owned ())
+    obstack = &type->objfile_owner ()->objfile_obstack;
+  else
+    obstack = gdbarch_obstack (type->arch_owner ());
+
+  unsigned int len = strlen (py_name);
+  const char *name = obstack_strndup (obstack,
+				      py_name,
+				      len);
+  struct type *complex_type;
+  try
+    {
+      complex_type = init_complex_type (name, type);
+      gdb_assert (complex_type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object (complex_type);
+}
+
+/* Creates a new pointer type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_pointer_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "owner", "target", "bit_size", "name",
+				    NULL };
+  PyObject *owner_object, *type_object;
+  int bit_length;
+  const char *py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OOis", keywords,
+					&owner_object, &type_object,
+					&bit_length, &py_name))
+    return nullptr;
+
+  struct type *type = type_object_to_type (type_object);
+  if (type == nullptr)
+    return nullptr;
+
+  type_storage_owner owner (owner_object);
+  if (!owner.valid ())
+    return nullptr;
+
+  const char *name = owner.copy_string (py_name);
+  struct type *pointer_type = nullptr;
+  try
+    {
+      type_allocator allocator = owner.allocator ();
+      pointer_type = init_pointer_type (allocator, bit_length, name, type);
+      gdb_assert (type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object (pointer_type);
+}
+
+/* Creates a new fixed point type and returns a new gdb.Type associated
+ * with it. */
+
+PyObject *
+gdbpy_init_fixed_point_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = { "owner", "bit_size", "unsigned", "name",
+				    NULL };
+  PyObject *owner_object;
+  int bit_length;
+  int unsigned_p;
+  const char* py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+					&owner_object, &bit_length,
+					&unsigned_p, &py_name))
+    return nullptr;
+
+  type_storage_owner owner (owner_object);
+  if (!owner.valid ())
+    return nullptr;
+
+  const char *name = owner.copy_string (py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator = owner.allocator ();
+      type = init_fixed_point_type (allocator, bit_length, unsigned_p, name);
+      gdb_assert (type != nullptr);
+    }
+  catch (gdb_exception_error& ex)
+    {
+      GDB_PY_HANDLE_EXCEPTION (ex);
+    }
+
+  return type_to_type_object (type);
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 14e15574685..51e1202d5bd 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -291,6 +291,8 @@ extern PyTypeObject frame_object_type
     CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("frame_object");
 extern PyTypeObject thread_object_type
     CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("thread_object");
+extern PyTypeObject float_format_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("float_format");
 
 /* Ensure that breakpoint_object_type is initialized and return true.  If
    breakpoint_object_type can't be initialized then set a suitable Python
@@ -433,6 +435,26 @@ gdb::unique_xmalloc_ptr<char> gdbpy_parse_command_name
 PyObject *gdbpy_register_tui_window (PyObject *self, PyObject *args,
 				     PyObject *kw);
 
+PyObject *gdbpy_init_type (PyObject *self, PyObject *args, PyObject *kw);
+PyObject *gdbpy_init_integer_type (PyObject *self, PyObject *args,
+				   PyObject *kw);
+PyObject *gdbpy_init_character_type (PyObject *self, PyObject *args,
+				     PyObject *kw);
+PyObject *gdbpy_init_boolean_type (PyObject *self, PyObject *args,
+				   PyObject *kw);
+PyObject *gdbpy_init_float_type (PyObject *self, PyObject *args,
+				 PyObject *kw);
+PyObject *gdbpy_init_decfloat_type (PyObject *self, PyObject *args,
+				    PyObject *kw);
+PyObject *gdbpy_can_create_complex_type (PyObject *self, PyObject *args,
+					 PyObject *kw);
+PyObject *gdbpy_init_complex_type (PyObject *self, PyObject *args,
+				   PyObject *kw);
+PyObject *gdbpy_init_pointer_type (PyObject *self, PyObject *args,
+				   PyObject *kw);
+PyObject *gdbpy_init_fixed_point_type (PyObject *self, PyObject *args,
+				       PyObject *kw);
+
 PyObject *symtab_and_line_to_sal_object (struct symtab_and_line sal);
 PyObject *symtab_to_symtab_object (struct symtab *symtab);
 PyObject *symbol_to_symbol_object (struct symbol *sym);
@@ -504,6 +526,18 @@ extern void serialize_mi_results (PyObject *results);
 extern PyObject *gdbpy_notify_mi (PyObject *self, PyObject *args,
 				  PyObject *kw);
 
+/* Retrieves a pointer to the underlying float format structure. Expects an
+ * instance of gdb.Objfile for SELF. If SELF is of an incompatible type,
+ * returns nullptr and raises a Python exception. */
+
+extern struct objfile *objfile_object_to_objfile (PyObject *self);
+
+/* Retrieves a pointer to the underlying float format structure. Expects an
+ * instance of gdb.FloatFormat for SELF. If SELF is of an incompatible type,
+ * returns nullptr and raises a Python exception. */
+
+extern struct floatformat *float_format_object_as_float_format (PyObject *self);
+
 /* Convert Python object OBJ to a program_space pointer.  OBJ must be a
    gdb.Progspace reference.  Return nullptr if the gdb.Progspace is not
    valid (see gdb.Progspace.is_valid), otherwise return the program_space
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 2ca3c50afd4..1ccba1ca519 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -2626,6 +2626,56 @@ Return current recording object." },
     "stop_recording () -> None.\n\
 Stop current recording." },
 
+  /* Type initialization functions. */
+  { "init_type", (PyCFunction) gdbpy_init_type, METH_VARARGS | METH_KEYWORDS,
+    "init_type (objfile, type_code, bit_length, name) -> type\n\
+    Creates a new type with the given bit length and type code, owned\
+    by the given objfile." },
+  { "init_integer_type", (PyCFunction) gdbpy_init_integer_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "init_integer_type (objfile, bit_length, unsigned, name) -> type\n\
+    Creates a new integer type with the given bit length and \
+    signedness, owned by the given objfile." },
+  { "init_character_type", (PyCFunction) gdbpy_init_character_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "init_character_type (objfile, bit_length, unsigned, name) -> type\n\
+    Creates a new character type with the given bit length and \
+    signedness, owned by the given objfile." },
+  { "init_boolean_type", (PyCFunction) gdbpy_init_boolean_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "init_boolean_type (objfile, bit_length, unsigned, name) -> type\n\
+    Creates a new boolean type with the given bit length and \
+    signedness, owned by the given objfile." },
+  { "init_float_type", (PyCFunction) gdbpy_init_float_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "init_float_type (objfile, float_format, name) -> type\n\
+    Creates a new floating point type with the given bit length and \
+    format, owned by the given objfile." },
+  { "init_decfloat_type", (PyCFunction) gdbpy_init_decfloat_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "init_decfloat_type (objfile, bit_length, name) -> type\n\
+    Creates a new decimal float type with the given bit length,\
+    owned by the given objfile." },
+  { "can_create_complex_type", (PyCFunction) gdbpy_can_create_complex_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "can_create_complex_type (type) -> bool\n\
+     Returns whether a given type can form a new complex type." },
+  { "init_complex_type", (PyCFunction) gdbpy_init_complex_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "init_complex_type (base_type, name) -> type\n\
+    Creates a new complex type whose components belong to the\
+    given type, owned by the given objfile." },
+  { "init_pointer_type", (PyCFunction) gdbpy_init_pointer_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "init_pointer_type (objfile, target_type, bit_length, name) -> type\n\
+    Creates a new pointer type with the given bit length, pointing\
+    to the given target type, and owned by the given objfile." },
+  { "init_fixed_point_type", (PyCFunction) gdbpy_init_fixed_point_type,
+    METH_VARARGS | METH_KEYWORDS,
+    "init_fixed_point_type (objfile, bit_length, unsigned, name) -> type\n\
+    Creates a new fixed point type with the given bit length and\
+    signedness, owned by the given objfile." },
+
   { "lookup_type", (PyCFunction) gdbpy_lookup_type,
     METH_VARARGS | METH_KEYWORDS,
     "lookup_type (name [, block]) -> type\n\
diff --git a/gdb/testsuite/gdb.python/py-type-init.c b/gdb/testsuite/gdb.python/py-type-init.c
new file mode 100644
index 00000000000..010e62bd248
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-type-init.c
@@ -0,0 +1,21 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-type-init.exp b/gdb/testsuite/gdb.python/py-type-init.exp
new file mode 100644
index 00000000000..8ef3c2c57af
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-type-init.exp
@@ -0,0 +1,132 @@
+# Copyright (C) 2009-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the mechanism
+# of creating new types from within Python.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# Build inferior to language specification.
+proc build_inferior {exefile lang} {
+  global srcdir subdir srcfile testfile hex
+
+  if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${exefile}" executable "debug $lang"] != "" } {
+      untested "failed to compile in $lang mode"
+      return -1
+  }
+
+  return 0
+}
+
+# Restart GDB.
+proc restart_gdb {exefile} {
+  clean_restart $exefile
+
+  if {![runto_main]} {
+      return
+  }
+}
+
+# Tests the basic values of a type.
+proc test_type_basic {owner t code sizeof name} {
+  gdb_test "python print(${t}.code == ${code})" \
+    "True" "check the code for the python-constructed type (${owner}/${name})"
+  gdb_test "python print(${t}.sizeof == ${sizeof})" \
+    "True" "check the size for the python-constructed type (${owner}/${name})"
+  gdb_test "python print(${t}.name == ${name})" \
+    "True" "check the name for the python-constructed type (${owner}/${name})"
+}
+
+# Runs the tests for a given owner object.
+proc for_owner {owner} {
+  # Simple direct type creation.
+  gdb_test "python t = gdb.init_type(${owner}, gdb.TYPE_CODE_INT, 24, 'long short int')" \
+    "" "construct a new type from inside python (${owner})"
+  test_type_basic $owner "t" "gdb.TYPE_CODE_INT" "3" "'long short int'"
+
+  # Integer type creation.
+  gdb_test "python t = gdb.init_integer_type(${owner}, 24, True, 'test_int_t')" \
+    "" "construct a new integer type from inside python (${owner})"
+  test_type_basic $owner "t" "gdb.TYPE_CODE_INT" "3" "'test_int_t'"
+
+  # Character type creation.
+  gdb_test "python t = gdb.init_character_type(${owner}, 24, True, 'test_char_t')" \
+    "" "construct a new character type from inside python (${owner})"
+  test_type_basic $owner "t" "gdb.TYPE_CODE_CHAR" "3" "'test_char_t'"
+
+  # Boolean type creation.
+  gdb_test "python t = gdb.init_boolean_type(${owner}, 24, True, 'test_bool_t')" \
+    "" "construct a new boolean type from inside python (${owner})"
+  test_type_basic $owner "t" "gdb.TYPE_CODE_BOOL" "3" "'test_bool_t'"
+
+  # Float type creation.
+  gdb_test "python f = gdb.FloatFormat()" "" "create a float format object (${owner})"
+  gdb_test "python f.totalsize = 32" "" "set totalsize for the float format (${owner})"
+  gdb_test "python f.sign_start = 31" "" "set sign_start for the float format (${owner})"
+  gdb_test "python f.exp_start = 23" "" "set exp_start for the float format (${owner})"
+  gdb_test "python f.exp_len = 8" "" "set exp_len for the float format (${owner})"
+  gdb_test "python f.exp_bias = 0" "" "set exp_bias for the float format (${owner})"
+  gdb_test "python f.exp_nan = 0xff" "" "set exp_nan for the float format (${owner})"
+  gdb_test "python f.man_start = 0" "" "set man_start for the float format (${owner})"
+  gdb_test "python f.man_len = 22" "" "set man_len for the float format (${owner})"
+  gdb_test "python f.intbit = False" "" "set intbit for the float format (${owner})"
+  gdb_test "python f.name = 'test_float_fmt'" "" "set name for the float format (${owner})"
+
+  gdb_test "python ft = gdb.init_float_type(${owner}, f, 'test_float_t')" \
+    "" "construct a new float type from inside python (${owner})"
+  test_type_basic $owner "ft" "gdb.TYPE_CODE_FLT" "4" "'test_float_t'"
+
+  # Decfloat type creation.
+  gdb_test "python t = gdb.init_decfloat_type(${owner}, 24, 'test_decfloat_t')" \
+    "" "construct a new decfloat type from inside python (${owner})"
+  test_type_basic $owner "t" "gdb.TYPE_CODE_DECFLOAT" "3" "'test_decfloat_t'"
+
+  # Test complex type.
+  gdb_test "python print(gdb.can_create_complex_type(ft))" "True" \
+    "check whether the float type we created can be the basis for a complex (${owner})"
+
+  gdb_test "python t = gdb.init_complex_type(ft, 'test_complex_t')" \
+    "" "construct a new complex type from inside python (${owner})"
+  test_type_basic $owner "t" "gdb.TYPE_CODE_COMPLEX" "8" "'test_complex_t'"
+
+  # Create a 24-bit pointer to our floating point type.
+  gdb_test "python t = gdb.init_pointer_type(${owner}, ft, 24, 'test_pointer_t')" \
+    "" "construct a new pointer type from inside python (${owner})"
+  test_type_basic $owner "t" "gdb.TYPE_CODE_PTR" "3" "'test_pointer_t'"
+}
+
+# Run the tests.
+if { [build_inferior "${binfile}" "c"] == 0 } {
+  restart_gdb "${binfile}"
+
+  # Skip all tests if Python scripting is not enabled.
+  if { ![allow_python_tests] } { continue }
+
+  # Test objfile-owned type construction
+  for_owner "gdb.objfiles()\[0\]"
+
+  # Objfile-owned fixed point type creation.
+  #
+  # Currently, these cannot be owned by architectures, so we have to
+  # test them separately.
+  gdb_test "python t = gdb.init_fixed_point_type(gdb.objfiles()\[0\], 24, True, 'test_fixed_t')" \
+    "" "construct a new fixed point type from inside python (gdb.objfile()\[0\])"
+  test_type_basic "gdb.objfile()\[0\]" "t" "gdb.TYPE_CODE_FIXED_POINT" "3" "'test_fixed_t'"
+
+  # Test arch-owned type construction
+  for_owner "gdb.inferiors()\[0\].architecture()"
+}
-- 
2.40.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4] Add support for creating new types from the Python API
  2024-01-16  4:54 [PATCH v4] Add support for creating new types from the Python API Matheus Branco Borella
@ 2024-01-16 12:45 ` Eli Zaretskii
  2024-01-16 17:50   ` Matheus Branco Borella
  2024-01-16 18:20   ` [PATCH v4] Add support for creating new types from the Python API Matheus Branco Borella
  2024-02-06 18:20 ` Tom Tromey
  1 sibling, 2 replies; 10+ messages in thread
From: Eli Zaretskii @ 2024-01-16 12:45 UTC (permalink / raw)
  To: Matheus Branco Borella; +Cc: gdb-patches

> From: Matheus Branco Borella <dark.ryu.550@gmail.com>
> Cc: eli@gnu.org,
> 	Matheus Branco Borella <dark.ryu.550@gmail.com>
> Date: Tue, 16 Jan 2024 01:54:40 -0300
> 
>  gdb/Makefile.in                           |   2 +
>  gdb/NEWS                                  |  16 +
>  gdb/doc/python.texi                       | 161 +++++++
>  gdb/python/py-float-format.c              | 307 +++++++++++++
>  gdb/python/py-objfile.c                   |  17 +
>  gdb/python/py-type-init.c                 | 520 ++++++++++++++++++++++
>  gdb/python/python-internal.h              |  34 ++
>  gdb/python/python.c                       |  50 +++
>  gdb/testsuite/gdb.python/py-type-init.c   |  21 +
>  gdb/testsuite/gdb.python/py-type-init.exp | 132 ++++++
>  10 files changed, 1260 insertions(+)
>  create mode 100644 gdb/python/py-float-format.c
>  create mode 100644 gdb/python/py-type-init.c
>  create mode 100644 gdb/testsuite/gdb.python/py-type-init.c
>  create mode 100644 gdb/testsuite/gdb.python/py-type-init.exp

Thanks.

> diff --git a/gdb/NEWS b/gdb/NEWS
> index 11cd6c0663e..e541544a027 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -87,6 +87,22 @@ show remote thread-options-packet
>    ** New function gdb.interrupt(), that interrupts GDB as if the user
>       typed control-c.
>  
> +  ** Functions that allow creation of instances of gdb.Type, and a new
> +     class gdb.FloatFormat that may be used to create floating point
> +     types.  The functions that allow new type creation are:
> +      - gdb.init_type: Create a new type given a type code.
> +      - gdb.init_integer_type: Create a new integer type.
> +      - gdb.init_character_type: Create a new character type.
> +      - gdb.init_boolean_type: Create a new boolean type.
> +      - gdb.init_float_type: Create a new floating point type.
> +      - gdb.init_decfloat_type: Create a new decimal floating point type.
> +      - gdb.can_create_complex_type: Whether a type can be used to create a
> +          new complex type.
> +      - gdb.init_complex_type: Create a new complex type.
> +      - gdb.init_pointer_type: Create a new pointer type.
> +          * This allows creating pointers of arbitrary size.
> +      - gdb.init_fixed_point_type: Create a new fixed point type.
> +
>  * Debugger Adapter Protocol changes

This part is okay.

> +@var{format} is an reference to a @code{gdb.FloatFormat} object, as
                   ^^^^^^^^^^^^
"a reference"

> +@findex gdb.init_pointer_type
> +@defun gdb.init_pointer_type (owner, target, bit_size, name)
> +This function creates a new @code{gdb.Type} instance corresponding to a
> +pointer type that points to @var{target} and is owned by the given
> +@var{owner}, with the given @var{name} and size.

I asked previously what does BIT_SIZE mean for pointer types.  Is it
the size of the pointer or of the data type to which the pointer
points?  If it's the size of the pointer, then does it mean this
function can create pointers of arbitrary sizes regardless of the
sizes of pointers that are supported by the target?

> +When creating a floating point type through @code{gdb.init_float_type},
> +one has to use a @code{gdb.FloatFormat} object. These objects may be
                                                 ^^
Two spaces there, please.

> +@defvar FloatFormat.totalsize
> +The size of the floating point number, in bits. Currently, accepted
                                                 ^^
Likewise.

> +@defvar FloatFormat.intbit
> +This is a boolean values that indicates whether the integer bit is part
> +of the value or if it is determined implicitly. A value of true
                                                 ^^
And here.

> +@defvar FloatFormat.name
> +The name of the float format. Used internally, for debugging purposes.
                               ^^
And here.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* (no subject)
  2024-01-16 12:45 ` Eli Zaretskii
@ 2024-01-16 17:50   ` Matheus Branco Borella
  2024-01-16 18:20   ` [PATCH v4] Add support for creating new types from the Python API Matheus Branco Borella
  1 sibling, 0 replies; 10+ messages in thread
From: Matheus Branco Borella @ 2024-01-16 17:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: eli



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4] Add support for creating new types from the Python API
  2024-01-16 12:45 ` Eli Zaretskii
  2024-01-16 17:50   ` Matheus Branco Borella
@ 2024-01-16 18:20   ` Matheus Branco Borella
  2024-01-16 18:56     ` Eli Zaretskii
  1 sibling, 1 reply; 10+ messages in thread
From: Matheus Branco Borella @ 2024-01-16 18:20 UTC (permalink / raw)
  To: gdb-patches; +Cc: eli

Apologies for my previous empty response, my email client got a little
trigger-happy (And I'm still getting the hang of it).

On Jan 16, 2024, at 9:45=E2=80=AFAM, Eli Zaretskii <eliz@gnu.org> wrote:
> I asked previously what does BIT_SIZE mean for pointer types.  Is it
> the size of the pointer or of the data type to which the pointer
> points?  If it's the size of the pointer, then does it mean this
> function can create pointers of arbitrary sizes regardless of the
> sizes of pointers that are supported by the target?

It's the size of the pointer itself. This feature is mostly intended
for cases where proper types may not be available - such as reverse
engineering - but may still be desirable. While one could get around
the lack of such a function by using `gdb.Type.pointer()`, I felt like
there was no reason to restrict it, and that trusting that the
consumers of the API will pick sizes that are valid for the target was
probably good enough.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4] Add support for creating new types from the Python API
  2024-01-16 18:20   ` [PATCH v4] Add support for creating new types from the Python API Matheus Branco Borella
@ 2024-01-16 18:56     ` Eli Zaretskii
  2024-01-16 21:27       ` Matheus Branco Borella
  0 siblings, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2024-01-16 18:56 UTC (permalink / raw)
  To: Matheus Branco Borella; +Cc: gdb-patches

> From: Matheus Branco Borella <dark.ryu.550@gmail.com>
> Cc: eli@gnu.org
> Date: Tue, 16 Jan 2024 15:20:24 -0300
> X-Spam-Status: No, score=-4.0 required=5.0 tests=BAYES_00, DKIM_SIGNED,
>  DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT,
>  FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP,
>  T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6
> 
> Apologies for my previous empty response, my email client got a little
> trigger-happy (And I'm still getting the hang of it).
> 
> On Jan 16, 2024, at 9:45=E2=80=AFAM, Eli Zaretskii <eliz@gnu.org> wrote:
> > I asked previously what does BIT_SIZE mean for pointer types.  Is it
> > the size of the pointer or of the data type to which the pointer
> > points?  If it's the size of the pointer, then does it mean this
> > function can create pointers of arbitrary sizes regardless of the
> > sizes of pointers that are supported by the target?
> 
> It's the size of the pointer itself.

How can that work?  AFAIU, most architectures only allow pointers of
certain sizes, some allow pointers of just one size.  E.g., what
happens if I create a 16-bit pointer on a 64-bit target?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4] Add support for creating new types from the Python API
  2024-01-16 18:56     ` Eli Zaretskii
@ 2024-01-16 21:27       ` Matheus Branco Borella
  0 siblings, 0 replies; 10+ messages in thread
From: Matheus Branco Borella @ 2024-01-16 21:27 UTC (permalink / raw)
  To: gdb-patches; +Cc: eli

> How can that work?  AFAIU, most architectures only allow pointers of
> certain sizes, some allow pointers of just one size.  E.g., what
> happens if I create a 16-bit pointer on a 64-bit target?

My intention is primarily to make it possible to construct such types,
assuming the given configuration is valid. In this case it would be the
consumer of the API who would be responsible for guaranteeing what they
are doing is valid.

Regardless, as far as I could gather, GDB doesn't really seem to care
if values whose types have TYPE_CODE_PTR have sizes that are valid in
the target architecture. `unsigned_pointer_to_address`, as well as all
the `*_pointer_to_address` functions I could find by grepping for uses
of `set_gdbarch_pointer_to_address` in the code are perfectly fine just
reading the data in the pointers as if they were type->length()-sized
integers. And mostly the same goes for their `*_address_to_pointer`
counterparts, except for large values being clipped (including extra
function pointer information). But I believe that behavior should be
fairly unsurprising if you're creating a pointer with the wrong size.

So, to answer your question, AFAICT it would just treat it as an
address stored in an (u)int16_t.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4] Add support for creating new types from the Python API
  2024-01-16  4:54 [PATCH v4] Add support for creating new types from the Python API Matheus Branco Borella
  2024-01-16 12:45 ` Eli Zaretskii
@ 2024-02-06 18:20 ` Tom Tromey
  2024-02-21 18:11   ` Matheus Branco Borella
  1 sibling, 1 reply; 10+ messages in thread
From: Tom Tromey @ 2024-02-06 18:20 UTC (permalink / raw)
  To: Matheus Branco Borella; +Cc: gdb-patches, eli

>>>>> "Matheus" == Matheus Branco Borella <dark.ryu.550@gmail.com> writes:

Matheus> The main drawback of using the `init_*_type` family over implementing type
Matheus> initialization by hand is that any type that's created gets immediately
Matheus> allocated on its owner's obstack, regardless of what its real lifetime
Matheus> requirements are. The main implication of this is that types that become
Matheus> unreachable will remain live for the lifetime of the owner.

Yeah.  gdb leaks a lot of types this way, actually.  We've collectively
put off implementing "type GC", though I do think there's a bug for it.

Matheus> +  ** Functions that allow creation of instances of gdb.Type, and a new
Matheus> +     class gdb.FloatFormat that may be used to create floating point
Matheus> +     types.  The functions that allow new type creation are:
Matheus> +      - gdb.init_type: Create a new type given a type code.
Matheus> +      - gdb.init_integer_type: Create a new integer type.
Matheus> +      - gdb.init_character_type: Create a new character type.
Matheus> +      - gdb.init_boolean_type: Create a new boolean type.
Matheus> +      - gdb.init_float_type: Create a new floating point type.
Matheus> +      - gdb.init_decfloat_type: Create a new decimal floating point type.
Matheus> +      - gdb.can_create_complex_type: Whether a type can be used to create a
Matheus> +          new complex type.
Matheus> +      - gdb.init_complex_type: Create a new complex type.
Matheus> +      - gdb.init_pointer_type: Create a new pointer type.
Matheus> +          * This allows creating pointers of arbitrary size.
Matheus> +      - gdb.init_fixed_point_type: Create a new fixed point type.

I don't really love the "init_" prefixes here.  Like, I get that these
are the names internally, but I don't think they really make sense
externally.

WDYT about "make_" instead?

Matheus> +@findex gdb.init_type
Matheus> +@defun gdb.init_type (owner, type_code, bit_size, name)
Matheus> +This function creates a new @code{gdb.Type} instance corresponding to a
Matheus> +type owned by the given @var{owner}, with the given @var{type_code},
Matheus> +@var{name} and size.
Matheus> +
Matheus> +@var{owner} must be a reference to either a @code{gdb.Objfile} or a
Matheus> +@code{gdb.Architecture} object.  These correspond to objfile and
Matheus> +architecture-owned types, respectively.
Matheus> +
Matheus> +@var{type_code} is one of the @code{TYPE_CODE_} constants defined in
Matheus> +@ref{Types In Python}.
Matheus> +
Matheus> +@var{bit_size} is the size of instances of the newly created type, in
Matheus> +bits. Currently, accepted values are limited to multiples of 8.
Matheus> +@end defun

Making any sort of type without filling in the details is probably a
recipe for crashes.

Is there a specific situation you needed this for?

Matheus> +@findex gdb.init_pointer_type
Matheus> +@defun gdb.init_pointer_type (owner, target, bit_size, name)
Matheus> +This function creates a new @code{gdb.Type} instance corresponding to a
Matheus> +pointer type that points to @var{target} and is owned by the given
Matheus> +@var{owner}, with the given @var{name} and size.
Matheus> +
Matheus> +@var{target} is a @code{gdb.Type} object, corresponding to the type
Matheus> +that will be pointed to by the newly created pointer type.
Matheus> +@end defun

I'm curious whether this one is really needed, because
gdb.Type.pointer() exists.

Like is there a case where you'd want a pointer type that doesn't match
the architecture somehow?  Seems weird and/or not useful.

Matheus> +/* Converts from a Python integer to a unsigned integer. */
Matheus> +
Matheus> +static bool
Matheus> +py_to_unsigned_int (PyObject *object, unsigned int *val)
Matheus> +{
Matheus> +  if (!PyObject_IsInstance (object, (PyObject*) &PyLong_Type))
Matheus> +    {
Matheus> +      PyErr_SetString (PyExc_TypeError, "value must be an integer");
Matheus> +      return false;
Matheus> +    }
Matheus> +
Matheus> +  long native_val = PyLong_AsLong (object);
Matheus> +  if (native_val > (long) UINT_MAX)
Matheus> +    {
Matheus> +      PyErr_SetString (PyExc_ValueError, "value is too large");
Matheus> +      return false;
Matheus> +    }
Matheus> +  if (native_val < 0)
Matheus> +    {
Matheus> +      PyErr_SetString (PyExc_ValueError,
Matheus> +		       "value must not be smaller than zero");
Matheus> +      return false;
Matheus> +    }
Matheus> +
Matheus> +  *val = (unsigned int) native_val;
Matheus> +  return true;

See gdb_py_int_as_long.
I think the type-check isn't really needed (probably) and some of the
other error-handling can be simplified.
There's also gdb_py_long_as_ulongest.

Matheus> +/* Functionality for creating new types accessible from python.
Matheus> +
Matheus> +   Copyright (C) 2008-2023 Free Software Foundation, Inc.

Forgot to mention this elsewhere but I think these dates are wrong.

Matheus> +/* An abstraction covering the objects types that can own a type object. */
Matheus> +
Matheus> +class type_storage_owner
Matheus> +{
Matheus> +public:
Matheus> +  /* Creates a new type owner from the given python object. If the object is
Matheus> +   * of a type that is not supported, the newly created instance will be
Matheus> +   * marked as invalid and nothing should be done with it. */
Matheus> +
Matheus> +  type_storage_owner (PyObject *owner)
Matheus> +  {
Matheus> +    if (gdbpy_is_architecture (owner))
Matheus> +      {
Matheus> +	this->kind = owner_kind::arch;
Matheus> +	this->owner.arch = arch_object_to_gdbarch (owner);
Matheus> +	return;
Matheus> +      }
Matheus> +
Matheus> +    this->kind = owner_kind::objfile;
Matheus> +    this->owner.objfile = objfile_object_to_objfile (owner);
Matheus> +    if (this->owner.objfile != nullptr)
Matheus> +	return;

It seems like this could all just create a type_allocator directly and
be simpler, like the 'kind' isn't needed.

Matheus> +
Matheus> +    this->kind = owner_kind::none;
Matheus> +    PyErr_SetString(PyExc_TypeError, "unsupported owner type");

Spaces before parens in a lot of spots...

Matheus> +    /* Should never be reached, but it's better to fail in a safe way than try
Matheus> +     * to instance the allocator with arbitraty parameters here. */
Matheus> +    abort ();

gdb uses gdb_assert_not_reached instead.

Matheus> +  /* Get a reference to the owner's obstack. */
Matheus> +
Matheus> +  obstack *get_obstack ()
Matheus> +  {

I think the uses of this could probably use TYPE_ALLOC instead.

Matheus> +  struct gdbarch *get_arch ()
Matheus> +  {

This could use the type allocator's arch.

Matheus> +  enum class owner_kind { arch, objfile, none };

Is the none case really possible?
It might be better to just throw an exception from the constructor or
during argument validation or something like that.

thanks,
Tom

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Re: [PATCH v4] Add support for creating new types from the Python API
  2024-02-06 18:20 ` Tom Tromey
@ 2024-02-21 18:11   ` Matheus Branco Borella
  2024-05-01 16:23     ` [PING] " Matheus Branco Borella
  2024-05-02 17:03     ` Tom Tromey
  0 siblings, 2 replies; 10+ messages in thread
From: Matheus Branco Borella @ 2024-02-21 18:11 UTC (permalink / raw)
  To: gdb-patches; +Cc: dark.ryu.550, tom

Thanks for the review, I've got a few questions and things to add before
I submit the v5, if that's okay.

Tom Tromey <tom@tromey.com> writes:

> WDYT about "make_" instead?

Yeah, that works. I don't feel particularly strongly about the names
that I picked, I'd mostly just picked them to mirror the internal ones
because I didn't have a better idea. I'll switch to `make_*_type`.

> Making any sort of type without filling in the details is probably a
> recipe for crashes.
> 
> Is there a specific situation you needed this for?

The main thing I had in mind was creating a void type, but yeah, it
makes sense to avoid exposing the generic type creation function without
also providing a proper way to fill in the details, so I'll take that
one out.

> I'm curious whether this one is really needed, because
> gdb.Type.pointer() exists.

Like I've told Eli, the main intent behind having it originally was
that, assuming one knows how to create a properly-sized pointer for
the architecture, one could, without having to rely on any of the type
lookup functions.

That being said, now that I think about it, I don't think there's any
case, when avoiding type lookup for pointer types might be necessary,
that can't be solved just as well by using `gdb.Type.pointer()`, while
also avoiding the footguns associated with `make_pointer_type`. So I'll
take it out, too.

> It seems like this could all just create a type_allocator directly and
> be simpler, like the 'kind' isn't needed.

> Is the none case really possible?
> It might be better to just throw an exception from the constructor or
> during argument validation or something like that.

Most of these fall under the same response, so I'll just reply to them
all at once.

When I was writing this patch, I had the following in mind:
 1st - This patch was first written before GDB switched to C++17, so I
       had no access to std::optional<>.
 2nd - I felt like throwing an exception over doing the `->valid()`
       check explicitly would be less clear about my intent for people
	   reading the code.

The design of `type_storage_owner` follows from those, and I don't feel
like changing it to use std::option<> or exceptions would be much of an
improvement in readability.

Would it really be that much of an improvement?

> I think the uses of this could probably use TYPE_ALLOC instead.

Isn't that only valid for `struct type`? I don't think I follow. Some of
the allocations (and I'm pretty sure at least one has to) happen before
the call to `init_*_type`.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PING] Re: [PATCH v4] Add support for creating new types from the Python API
  2024-02-21 18:11   ` Matheus Branco Borella
@ 2024-05-01 16:23     ` Matheus Branco Borella
  2024-05-02 17:03     ` Tom Tromey
  1 sibling, 0 replies; 10+ messages in thread
From: Matheus Branco Borella @ 2024-05-01 16:23 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

Any chance I could get an answer for these before I submit the v5?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4] Add support for creating new types from the Python API
  2024-02-21 18:11   ` Matheus Branco Borella
  2024-05-01 16:23     ` [PING] " Matheus Branco Borella
@ 2024-05-02 17:03     ` Tom Tromey
  1 sibling, 0 replies; 10+ messages in thread
From: Tom Tromey @ 2024-05-02 17:03 UTC (permalink / raw)
  To: Matheus Branco Borella; +Cc: gdb-patches, tom

>>>>> "Matheus" == Matheus Branco Borella <dark.ryu.550@gmail.com> writes:

Matheus> Thanks for the review, I've got a few questions and things to add before
Matheus> I submit the v5, if that's okay.

Thanks.  I'm sorry about the delay on this, pinging was the right thing
to do.

>> Is the none case really possible?
>> It might be better to just throw an exception from the constructor or
>> during argument validation or something like that.

Matheus> Most of these fall under the same response, so I'll just reply to them
Matheus> all at once.

Matheus> When I was writing this patch, I had the following in mind:
Matheus>  1st - This patch was first written before GDB switched to C++17, so I
Matheus>        had no access to std::optional<>.
Matheus>  2nd - I felt like throwing an exception over doing the `->valid()`
Matheus>        check explicitly would be less clear about my intent for people
Matheus> 	   reading the code.

Matheus> The design of `type_storage_owner` follows from those, and I don't feel
Matheus> like changing it to use std::option<> or exceptions would be much of an
Matheus> improvement in readability.

Matheus> Would it really be that much of an improvement?

I took another look at the patch and I think I understand.  I agree,
this seems fine.

>> I think the uses of this could probably use TYPE_ALLOC instead.

Matheus> Isn't that only valid for `struct type`? I don't think I follow. Some of
Matheus> the allocations (and I'm pretty sure at least one has to) happen before
Matheus> the call to `init_*_type`.

Yeah, I see.  It still feels like some of this could be using
type_allocator or be pushed there, but it's not a big deal, I wouldn't
worry about it.

thanks,
Tom

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-05-02 17:03 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-16  4:54 [PATCH v4] Add support for creating new types from the Python API Matheus Branco Borella
2024-01-16 12:45 ` Eli Zaretskii
2024-01-16 17:50   ` Matheus Branco Borella
2024-01-16 18:20   ` [PATCH v4] Add support for creating new types from the Python API Matheus Branco Borella
2024-01-16 18:56     ` Eli Zaretskii
2024-01-16 21:27       ` Matheus Branco Borella
2024-02-06 18:20 ` Tom Tromey
2024-02-21 18:11   ` Matheus Branco Borella
2024-05-01 16:23     ` [PING] " Matheus Branco Borella
2024-05-02 17:03     ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).