[10/11] Add Python support for dynamic types

Message ID 20200408175452.30637-11-tromey@adacore.com
State New
Headers show
Series
  • Variant part support, plus more
Related show

Commit Message

Tom Tromey April 8, 2020, 5:54 p.m.
This changes the gdb Python API to add support for dynamic types.  In
particular, this adds an attribute to gdb.Type, and updates some
attributes to reflect dynamic sizes and field offsets.

There's still no way to get the dynamic type from one of its concrete
instances.  This could perhaps be added if needed.

gdb/ChangeLog
2020-04-08  Tom Tromey  <tromey@adacore.com>

	PR python/23662:
	* python/py-type.c (convert_field): Handle
	FIELD_LOC_KIND_DWARF_BLOCK.
	(typy_get_sizeof): Handle TYPE_HAS_DYNAMIC_LENGTH.
	(typy_get_dynamic): Nw function.
	(type_object_getset): Add "dynamic".
	* NEWS: Add entry.

gdb/doc/ChangeLog
2020-04-08  Tom Tromey  <tromey@adacore.com>

	PR python/23662:
	* python.texi (Types In Python): Document new features.

gdb/testsuite/ChangeLog
2020-04-08  Tom Tromey  <tromey@adacore.com>

	PR python/23662:
	* gdb.ada/variant.exp: Add Python checks.
	* gdb.rust/simple.exp: Add dynamic type checks.
---
 gdb/ChangeLog                     | 10 +++++++++
 gdb/NEWS                          |  5 +++++
 gdb/doc/ChangeLog                 |  5 +++++
 gdb/doc/python.texi               | 14 +++++++++++--
 gdb/python/py-type.c              | 35 +++++++++++++++++++++++++++++--
 gdb/testsuite/ChangeLog           |  6 ++++++
 gdb/testsuite/gdb.ada/variant.exp | 10 +++++++++
 gdb/testsuite/gdb.rust/simple.exp | 10 +++++++++
 8 files changed, 91 insertions(+), 4 deletions(-)

-- 
2.21.1

Comments

Eli Zaretskii April 8, 2020, 6:59 p.m. | #1
> From: Tom Tromey <tromey@adacore.com>

> Date: Wed,  8 Apr 2020 11:54:51 -0600

> Cc: Tom Tromey <tromey@adacore.com>

> 

> This changes the gdb Python API to add support for dynamic types.  In

> particular, this adds an attribute to gdb.Type, and updates some

> attributes to reflect dynamic sizes and field offsets.

> 

> There's still no way to get the dynamic type from one of its concrete

> instances.  This could perhaps be added if needed.

> 

> gdb/ChangeLog

> 2020-04-08  Tom Tromey  <tromey@adacore.com>

> 

> 	PR python/23662:

> 	* python/py-type.c (convert_field): Handle

> 	FIELD_LOC_KIND_DWARF_BLOCK.

> 	(typy_get_sizeof): Handle TYPE_HAS_DYNAMIC_LENGTH.

> 	(typy_get_dynamic): Nw function.

> 	(type_object_getset): Add "dynamic".

> 	* NEWS: Add entry.

> 

> gdb/doc/ChangeLog

> 2020-04-08  Tom Tromey  <tromey@adacore.com>

> 

> 	PR python/23662:

> 	* python.texi (Types In Python): Document new features.

> 

> gdb/testsuite/ChangeLog

> 2020-04-08  Tom Tromey  <tromey@adacore.com>

> 

> 	PR python/23662:

> 	* gdb.ada/variant.exp: Add Python checks.

> 	* gdb.rust/simple.exp: Add dynamic type checks.


Thanks, the documentation parts are OK.
Simon Marchi via Gdb-patches April 9, 2020, 12:20 a.m. | #2
On Wed, Apr 8, 2020 at 12:55 PM Tom Tromey <tromey@adacore.com> wrote:
>

> This changes the gdb Python API to add support for dynamic types.  In

> particular, this adds an attribute to gdb.Type, and updates some

> attributes to reflect dynamic sizes and field offsets.

>

> There's still no way to get the dynamic type from one of its concrete

> instances.  This could perhaps be added if needed.

>

> gdb/ChangeLog

> 2020-04-08  Tom Tromey  <tromey@adacore.com>

>

>         PR python/23662:

>         * python/py-type.c (convert_field): Handle

>         FIELD_LOC_KIND_DWARF_BLOCK.

>         (typy_get_sizeof): Handle TYPE_HAS_DYNAMIC_LENGTH.

>         (typy_get_dynamic): Nw function.

>         (type_object_getset): Add "dynamic".

>         * NEWS: Add entry.

>

> gdb/doc/ChangeLog

> 2020-04-08  Tom Tromey  <tromey@adacore.com>

>

>         PR python/23662:

>         * python.texi (Types In Python): Document new features.

>

> gdb/testsuite/ChangeLog

> 2020-04-08  Tom Tromey  <tromey@adacore.com>

>

>         PR python/23662:

>         * gdb.ada/variant.exp: Add Python checks.

>         * gdb.rust/simple.exp: Add dynamic type checks.

> ---

>  gdb/ChangeLog                     | 10 +++++++++

>  gdb/NEWS                          |  5 +++++

>  gdb/doc/ChangeLog                 |  5 +++++

>  gdb/doc/python.texi               | 14 +++++++++++--

>  gdb/python/py-type.c              | 35 +++++++++++++++++++++++++++++--

>  gdb/testsuite/ChangeLog           |  6 ++++++

>  gdb/testsuite/gdb.ada/variant.exp | 10 +++++++++

>  gdb/testsuite/gdb.rust/simple.exp | 10 +++++++++

>  8 files changed, 91 insertions(+), 4 deletions(-)

>

> diff --git a/gdb/NEWS b/gdb/NEWS

> index 6657f6fadce..01e73c9e5ea 100644

> --- a/gdb/NEWS

> +++ b/gdb/NEWS

> @@ -68,6 +68,11 @@ GNU/Linux/RISC-V (gdbserver) riscv*-*-linux*

>    ** gdb.register_window_type can be used to implement new TUI windows

>       in Python.

>

> +  ** Dynamic types can now be queried.  gdb.Type has a new attribute,

> +     "dynamic", and gdb.Type.sizeof can be None for a dynamic type.  A

> +     field of a dynamic type may have None for its "bitpos" attribute

> +     as well.

> +

>  *** Changes in GDB 9

>

>  * 'thread-exited' event is now available in the annotations interface.

> diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi

> index 76cdf7f5419..bcc27f6a224 100644

> --- a/gdb/doc/python.texi

> +++ b/gdb/doc/python.texi

> @@ -1068,6 +1068,12 @@ The type code for this type.  The type code will be one of the

>  @code{TYPE_CODE_} constants defined below.

>  @end defvar

>

> +@defvar Type.dynamic

> +A boolean indicating whether this type is dynamic.  In some

> +situations, such as Rust @code{enum} types or Ada variant records, the

> +concrete type of a value may vary depending on its contents.

> +@end defvar

> +

>  @defvar Type.name

>  The name of this type.  If this type has no name, then @code{None}

>  is returned.

> @@ -1076,7 +1082,9 @@ is returned.

>  @defvar Type.sizeof

>  The size of this type, in target @code{char} units.  Usually, a

>  target's @code{char} type will be an 8-bit byte.  However, on some

> -unusual platforms, this type may have a different size.

> +unusual platforms, this type may have a different size.  A dynamic

> +type may not have a fixed size; in this case, this attribute's value

> +will be @code{None}.

>  @end defvar

>

>  @defvar Type.tag

> @@ -1106,7 +1114,9 @@ Each field is a @code{gdb.Field} object, with some pre-defined attributes:

>  @item bitpos

>  This attribute is not available for @code{enum} or @code{static}

>  (as in C@t{++}) fields.  The value is the position, counting

> -in bits, from the start of the containing type.

> +in bits, from the start of the containing type.  Note that, in a

> +dynamic type, the position of a field may not be constant.  In this

> +case, the value will be @code{None}.


Just to make sure I understand, if I have a type like this (using
Haskell-like syntax, I hope this makes sense):

Type t =
  Int x |
  Float y |
  (String a, Float b)

This would be reflected in Python as the type having fields x, y, a
and b, all of which have a None position?

Is there a way to access these fields, and is there a way to see which
specific type a certain variable value is?

Christian

>

>  @item enumval

>  This attribute is only available for @code{enum} fields, and its value

> diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c

> index b19cad098a4..acdd80fccc3 100644

> --- a/gdb/python/py-type.c

> +++ b/gdb/python/py-type.c

> @@ -189,8 +189,11 @@ convert_field (struct type *type, int field)

>         }

>        else

>         {

> -         arg.reset (gdb_py_long_from_longest (TYPE_FIELD_BITPOS (type,

> -                                                                 field)));

> +         if (TYPE_FIELD_LOC_KIND (type, field) == FIELD_LOC_KIND_DWARF_BLOCK)

> +           arg = gdbpy_ref<>::new_reference (Py_None);

> +         else

> +           arg.reset (gdb_py_long_from_longest (TYPE_FIELD_BITPOS (type,

> +                                                                   field)));

>           attrstring = "bitpos";

>         }

>

> @@ -710,9 +713,12 @@ typy_get_sizeof (PyObject *self, void *closure)

>  {

>    struct type *type = ((type_object *) self)->type;

>

> +  bool size_varies = false;

>    try

>      {

>        check_typedef (type);

> +

> +      size_varies = TYPE_HAS_DYNAMIC_LENGTH (type);

>      }

>    catch (const gdb_exception &except)

>      {

> @@ -720,6 +726,8 @@ typy_get_sizeof (PyObject *self, void *closure)

>

>    /* Ignore exceptions.  */

>

> +  if (size_varies)

> +    Py_RETURN_NONE;

>    return gdb_py_long_from_longest (TYPE_LENGTH (type));

>  }

>

> @@ -744,6 +752,27 @@ typy_get_alignof (PyObject *self, void *closure)

>    return gdb_py_object_from_ulongest (align).release ();

>  }

>

> +/* Return whether or not the type is dynamic.  */

> +static PyObject *

> +typy_get_dynamic (PyObject *self, void *closure)

> +{

> +  struct type *type = ((type_object *) self)->type;

> +

> +  bool result = false;

> +  try

> +    {

> +      result = is_dynamic_type (type);

> +    }

> +  catch (const gdb_exception &except)

> +    {

> +      /* Ignore exceptions.  */

> +    }

> +

> +  if (result)

> +    Py_RETURN_TRUE;

> +  Py_RETURN_FALSE;

> +}

> +

>  static struct type *

>  typy_lookup_typename (const char *type_name, const struct block *block)

>  {

> @@ -1436,6 +1465,8 @@ static gdb_PyGetSetDef type_object_getset[] =

>      "The alignment of this type, in bytes.", NULL },

>    { "code", typy_get_code, NULL,

>      "The code for this type.", NULL },

> +  { "dynamic", typy_get_dynamic, NULL,

> +    "Whether this type is dynamic.", NULL },

>    { "name", typy_get_name, NULL,

>      "The name for this type, or None.", NULL },

>    { "sizeof", typy_get_sizeof, NULL,

> diff --git a/gdb/testsuite/gdb.ada/variant.exp b/gdb/testsuite/gdb.ada/variant.exp

> index 490956a2666..da51f7ba2e8 100644

> --- a/gdb/testsuite/gdb.ada/variant.exp

> +++ b/gdb/testsuite/gdb.ada/variant.exp

> @@ -14,6 +14,7 @@

>  # along with this program.  If not, see <http://www.gnu.org/licenses/>.

>

>  load_lib "ada.exp"

> +load_lib "gdb-python.exp"

>

>  standard_ada_testfile pkg

>

> @@ -43,4 +44,13 @@ foreach_with_prefix scenario {none all minimal} {

>         " = \\(one => 3, two => 0, str => \"zzz\", onevalue => 33, str2 => \"\"\\)"

>      gdb_test "print nav3" \

>         " = \\(one => 3, two => 7, str => \"zzz\", onevalue => 33, str2 => \"qqqqqqq\", twovalue => 88\\)"

> +

> +    # This is only supported for the DWARF encoding.

> +    if {$scenario == "minimal" && ![skip_python_tests]} {

> +       gdb_test_no_output \

> +           "python t = gdb.lookup_type('nested_and_variable')" \

> +           "fetch type for python"

> +       gdb_test "python print(t.dynamic)" "True"

> +       gdb_test "python print(t\['onevalue'\].bitpos)" "None"

> +    }

>  }

> diff --git a/gdb/testsuite/gdb.rust/simple.exp b/gdb/testsuite/gdb.rust/simple.exp

> index 92b3666386b..6daaf8415c5 100644

> --- a/gdb/testsuite/gdb.rust/simple.exp

> +++ b/gdb/testsuite/gdb.rust/simple.exp

> @@ -364,3 +364,13 @@ if {[skip_python_tests]} {

>  }

>

>  gdb_test "python print(gdb.lookup_type('simple::HiBob'))" "simple::HiBob"

> +

> +gdb_test_no_output "python e = gdb.parse_and_eval('e')" \

> +    "get value of e for python"

> +gdb_test "python print(len(e.type.fields()))" "2"

> +gdb_test "python print(e.type.fields()\[0\].artificial)" "True"

> +gdb_test "python print(e.type.fields()\[1\].name)" "Two"

> +

> +gdb_test "python print(e.type.dynamic)" "False"

> +gdb_test "python print(gdb.lookup_type('simple::MoreComplicated').dynamic)" \

> +    "True"

> --

> 2.21.1

>
Tom Tromey April 9, 2020, 6:44 p.m. | #3
>>>>> "Christian" == Christian Biesinger via Gdb-patches <gdb-patches@sourceware.org> writes:


>> This attribute is not available for @code{enum} or @code{static}

>> (as in C@t{++}) fields.  The value is the position, counting

>> -in bits, from the start of the containing type.

>> +in bits, from the start of the containing type.  Note that, in a

>> +dynamic type, the position of a field may not be constant.  In this

>> +case, the value will be @code{None}.


Christian> Just to make sure I understand, if I have a type like this (using
Christian> Haskell-like syntax, I hope this makes sense):

Christian> Type t =
Christian>   Int x |
Christian>   Float y |
Christian>   (String a, Float b)

Christian> This would be reflected in Python as the type having fields x, y, a
Christian> and b, all of which have a None position?

IIUC this would have field x, y, and a field with an anonymous structure
type, that in turn has fields a and b.  But otherwise yes.

Christian> Is there a way to access these fields, and is there a way to see which
Christian> specific type a certain variable value is?

Yes, the type of a value will be a concrete instance of such a dynamic
type.

The Rust test is similar:

>> +gdb_test "python print(len(e.type.fields()))" "2"

>> +gdb_test "python print(e.type.fields()\[0\].artificial)" "True"

>> +gdb_test "python print(e.type.fields()\[1\].name)" "Two"

>> +

>> +gdb_test "python print(e.type.dynamic)" "False"

>> +gdb_test "python print(gdb.lookup_type('simple::MoreComplicated').dynamic)" \

>> +    "True"


This is inspecting a dynamic type "MoreComplicated" and a value of that
type, "e".

MoreComplicated is an enum, which is the Rust equivalent of your Haskell
example.

    enum MoreComplicated {
        One,
        Two(i32),
        Three(HiBob),
        Four{this: bool, is: u8, a: char, struct_: u64, variant: u32},
    }

The value comes from:

    let e = MoreComplicated::Two(73);

So, if we have the dynamic type, "dynamic" is True and all the fields
are available (not shown in the test).  If we have the value "e", only
the applicable fields for the variant are available -- the
compiler-provided discriminant, and the "Two" payload.

Tom
Simon Marchi via Gdb-patches April 10, 2020, 5:44 p.m. | #4
On Thu, Apr 9, 2020 at 1:44 PM Tom Tromey <tom@tromey.com> wrote:
>

> >>>>> "Christian" == Christian Biesinger via Gdb-patches <gdb-patches@sourceware.org> writes:

>

> >> This attribute is not available for @code{enum} or @code{static}

> >> (as in C@t{++}) fields.  The value is the position, counting

> >> -in bits, from the start of the containing type.

> >> +in bits, from the start of the containing type.  Note that, in a

> >> +dynamic type, the position of a field may not be constant.  In this

> >> +case, the value will be @code{None}.

>

> Christian> Just to make sure I understand, if I have a type like this (using

> Christian> Haskell-like syntax, I hope this makes sense):

>

> Christian> Type t =

> Christian>   Int x |

> Christian>   Float y |

> Christian>   (String a, Float b)

>

> Christian> This would be reflected in Python as the type having fields x, y, a

> Christian> and b, all of which have a None position?

>

> IIUC this would have field x, y, and a field with an anonymous structure

> type, that in turn has fields a and b.  But otherwise yes.

>

> Christian> Is there a way to access these fields, and is there a way to see which

> Christian> specific type a certain variable value is?

>

> Yes, the type of a value will be a concrete instance of such a dynamic

> type.

>

> The Rust test is similar:

>

> >> +gdb_test "python print(len(e.type.fields()))" "2"

> >> +gdb_test "python print(e.type.fields()\[0\].artificial)" "True"

> >> +gdb_test "python print(e.type.fields()\[1\].name)" "Two"

> >> +

> >> +gdb_test "python print(e.type.dynamic)" "False"

> >> +gdb_test "python print(gdb.lookup_type('simple::MoreComplicated').dynamic)" \

> >> +    "True"

>

> This is inspecting a dynamic type "MoreComplicated" and a value of that

> type, "e".

>

> MoreComplicated is an enum, which is the Rust equivalent of your Haskell

> example.

>

>     enum MoreComplicated {

>         One,

>         Two(i32),

>         Three(HiBob),

>         Four{this: bool, is: u8, a: char, struct_: u64, variant: u32},

>     }

>

> The value comes from:

>

>     let e = MoreComplicated::Two(73);

>

> So, if we have the dynamic type, "dynamic" is True and all the fields

> are available (not shown in the test).  If we have the value "e", only

> the applicable fields for the variant are available -- the

> compiler-provided discriminant, and the "Two" payload.


Thanks for the detailed explanation! I would suggest two things:
- For your note in the documentation that "the position of a field may
not be constant", maybe add that the field may not exist in a specific
value, and
- mention somewhere that value.type will be different from
gdb.lookup_type(value.type.name), and lets you access the concrete
fields

The "dynamic type" name is unfortunate, since it is unrelated to
Value.dynamic_type AFAICT. I thought discriminated/tagged union was a
more common name for this :(

Thanks,
Christian
Tom Tromey April 24, 2020, 7:42 p.m. | #5
>>>>> "Christian" == Christian Biesinger via Gdb-patches <gdb-patches@sourceware.org> writes:


Christian> Thanks for the detailed explanation! I would suggest two things:
Christian> - For your note in the documentation that "the position of a field may
Christian> not be constant", maybe add that the field may not exist in a specific
Christian> value, and
Christian> - mention somewhere that value.type will be different from
Christian> gdb.lookup_type(value.type.name), and lets you access the concrete
Christian> fields

I'll write a new doc patch for this.

Christian> The "dynamic type" name is unfortunate, since it is unrelated to
Christian> Value.dynamic_type AFAICT. I thought discriminated/tagged union was a
Christian> more common name for this :(

Yeah, though this code doesn't really only apply to variant parts /
discriminated unions.  It applies to any type that might have some kind
of dynamically-resolved attribute.  For example, this can be used by
structures that contain dynamically-sized arrays.

Tom
Tom Tromey April 24, 2020, 8:35 p.m. | #6
>>>>> "Christian" == Christian Biesinger via Gdb-patches <gdb-patches@sourceware.org> writes:


Christian> The "dynamic type" name is unfortunate, since it is unrelated to
Christian> Value.dynamic_type AFAICT. I thought discriminated/tagged union was a
Christian> more common name for this :(

When I sent my earlier note, this hadn't fully sunk in I guess.  Maybe
we ought to rename the "dynamic" type business.  It isn't too late, even
though that's checked in.

What do you think would be better?

Tom
Simon Marchi via Gdb-patches April 25, 2020, 8:43 a.m. | #7
On Fri, Apr 24, 2020 at 8:38 PM Tom Tromey <tom@tromey.com> wrote:
>

> >>>>> "Christian" == Christian Biesinger via Gdb-patches <gdb-patches@sourceware.org> writes:

>

> Christian> The "dynamic type" name is unfortunate, since it is unrelated to

> Christian> Value.dynamic_type AFAICT. I thought discriminated/tagged union was a

> Christian> more common name for this :(

>

> When I sent my earlier note, this hadn't fully sunk in I guess.  Maybe

> we ought to rename the "dynamic" type business.  It isn't too late, even

> though that's checked in.

>

> What do you think would be better?

>

> Tom


I know that Harper has used the terminology "Extensible", and the term
"Index", is commonly the generalization of Tag.
first paragraph from the paper by Licata & Harper, An Extensible
Theory of Indexed Types
http://www.cs.cmu.edu/~rwh/papers/extidx/paper.pdf

"Indexed families of types are a way of associating run-time data with
compile-time abstractions that can be used to reason about them. We
propose an extensible theory of indexed types, in which programmers
can define the index data appropriate to their programs and use them
to track properties of run-time code."

Extensible seems alright to me for the bool, in that the concrete data
obtained from the type is extensible beyond that what is statically
known about the type?

shrug

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 6657f6fadce..01e73c9e5ea 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -68,6 +68,11 @@  GNU/Linux/RISC-V (gdbserver)	riscv*-*-linux*
   ** gdb.register_window_type can be used to implement new TUI windows
      in Python.
 
+  ** Dynamic types can now be queried.  gdb.Type has a new attribute,
+     "dynamic", and gdb.Type.sizeof can be None for a dynamic type.  A
+     field of a dynamic type may have None for its "bitpos" attribute
+     as well.
+
 *** Changes in GDB 9
 
 * 'thread-exited' event is now available in the annotations interface.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 76cdf7f5419..bcc27f6a224 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1068,6 +1068,12 @@  The type code for this type.  The type code will be one of the
 @code{TYPE_CODE_} constants defined below.
 @end defvar
 
+@defvar Type.dynamic
+A boolean indicating whether this type is dynamic.  In some
+situations, such as Rust @code{enum} types or Ada variant records, the
+concrete type of a value may vary depending on its contents.
+@end defvar
+
 @defvar Type.name
 The name of this type.  If this type has no name, then @code{None}
 is returned.
@@ -1076,7 +1082,9 @@  is returned.
 @defvar Type.sizeof
 The size of this type, in target @code{char} units.  Usually, a
 target's @code{char} type will be an 8-bit byte.  However, on some
-unusual platforms, this type may have a different size.
+unusual platforms, this type may have a different size.  A dynamic
+type may not have a fixed size; in this case, this attribute's value
+will be @code{None}.
 @end defvar
 
 @defvar Type.tag
@@ -1106,7 +1114,9 @@  Each field is a @code{gdb.Field} object, with some pre-defined attributes:
 @item bitpos
 This attribute is not available for @code{enum} or @code{static}
 (as in C@t{++}) fields.  The value is the position, counting
-in bits, from the start of the containing type.
+in bits, from the start of the containing type.  Note that, in a
+dynamic type, the position of a field may not be constant.  In this
+case, the value will be @code{None}.
 
 @item enumval
 This attribute is only available for @code{enum} fields, and its value
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index b19cad098a4..acdd80fccc3 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -189,8 +189,11 @@  convert_field (struct type *type, int field)
 	}
       else
 	{
-	  arg.reset (gdb_py_long_from_longest (TYPE_FIELD_BITPOS (type,
-								  field)));
+	  if (TYPE_FIELD_LOC_KIND (type, field) == FIELD_LOC_KIND_DWARF_BLOCK)
+	    arg = gdbpy_ref<>::new_reference (Py_None);
+	  else
+	    arg.reset (gdb_py_long_from_longest (TYPE_FIELD_BITPOS (type,
+								    field)));
 	  attrstring = "bitpos";
 	}
 
@@ -710,9 +713,12 @@  typy_get_sizeof (PyObject *self, void *closure)
 {
   struct type *type = ((type_object *) self)->type;
 
+  bool size_varies = false;
   try
     {
       check_typedef (type);
+
+      size_varies = TYPE_HAS_DYNAMIC_LENGTH (type);
     }
   catch (const gdb_exception &except)
     {
@@ -720,6 +726,8 @@  typy_get_sizeof (PyObject *self, void *closure)
 
   /* Ignore exceptions.  */
 
+  if (size_varies)
+    Py_RETURN_NONE;
   return gdb_py_long_from_longest (TYPE_LENGTH (type));
 }
 
@@ -744,6 +752,27 @@  typy_get_alignof (PyObject *self, void *closure)
   return gdb_py_object_from_ulongest (align).release ();
 }
 
+/* Return whether or not the type is dynamic.  */
+static PyObject *
+typy_get_dynamic (PyObject *self, void *closure)
+{
+  struct type *type = ((type_object *) self)->type;
+
+  bool result = false;
+  try
+    {
+      result = is_dynamic_type (type);
+    }
+  catch (const gdb_exception &except)
+    {
+      /* Ignore exceptions.  */
+    }
+
+  if (result)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
 static struct type *
 typy_lookup_typename (const char *type_name, const struct block *block)
 {
@@ -1436,6 +1465,8 @@  static gdb_PyGetSetDef type_object_getset[] =
     "The alignment of this type, in bytes.", NULL },
   { "code", typy_get_code, NULL,
     "The code for this type.", NULL },
+  { "dynamic", typy_get_dynamic, NULL,
+    "Whether this type is dynamic.", NULL },
   { "name", typy_get_name, NULL,
     "The name for this type, or None.", NULL },
   { "sizeof", typy_get_sizeof, NULL,
diff --git a/gdb/testsuite/gdb.ada/variant.exp b/gdb/testsuite/gdb.ada/variant.exp
index 490956a2666..da51f7ba2e8 100644
--- a/gdb/testsuite/gdb.ada/variant.exp
+++ b/gdb/testsuite/gdb.ada/variant.exp
@@ -14,6 +14,7 @@ 
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 load_lib "ada.exp"
+load_lib "gdb-python.exp"
 
 standard_ada_testfile pkg
 
@@ -43,4 +44,13 @@  foreach_with_prefix scenario {none all minimal} {
 	" = \\(one => 3, two => 0, str => \"zzz\", onevalue => 33, str2 => \"\"\\)"
     gdb_test "print nav3" \
 	" = \\(one => 3, two => 7, str => \"zzz\", onevalue => 33, str2 => \"qqqqqqq\", twovalue => 88\\)"
+
+    # This is only supported for the DWARF encoding.
+    if {$scenario == "minimal" && ![skip_python_tests]} {
+	gdb_test_no_output \
+	    "python t = gdb.lookup_type('nested_and_variable')" \
+	    "fetch type for python"
+	gdb_test "python print(t.dynamic)" "True"
+	gdb_test "python print(t\['onevalue'\].bitpos)" "None"
+    }
 }
diff --git a/gdb/testsuite/gdb.rust/simple.exp b/gdb/testsuite/gdb.rust/simple.exp
index 92b3666386b..6daaf8415c5 100644
--- a/gdb/testsuite/gdb.rust/simple.exp
+++ b/gdb/testsuite/gdb.rust/simple.exp
@@ -364,3 +364,13 @@  if {[skip_python_tests]} {
 }
 
 gdb_test "python print(gdb.lookup_type('simple::HiBob'))" "simple::HiBob"
+
+gdb_test_no_output "python e = gdb.parse_and_eval('e')" \
+    "get value of e for python"
+gdb_test "python print(len(e.type.fields()))" "2"
+gdb_test "python print(e.type.fields()\[0\].artificial)" "True"
+gdb_test "python print(e.type.fields()\[1\].name)" "Two"
+
+gdb_test "python print(e.type.dynamic)" "False"
+gdb_test "python print(gdb.lookup_type('simple::MoreComplicated').dynamic)" \
+    "True"