[3/3] gdb/testsuite: add a test to check for Python traits static_assert
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gdb_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 |
success
|
Test passed
|
| linaro-tcwg-bot/tcwg_gdb_check--master-arm |
fail
|
Patch failed to apply
|
Commit Message
The previous two commits added a new type trait which can be used
within a static_assert to check the properties of a struct used by GDB
to implement Python objects.
The previous commit fixed a bug in GDB which this trait check exposed.
This commit adds a new test gdb.gdb/python-traits-check.exp which
checks that every struct in the Python/ directory that inherits from
PyObject, has a suitable static_assert in place.
Adding this test should mean that if someone adds a new Python object
type to GDB, and they forget to add the static_assert, then this test
should give a failure, which should remind them to add the required
static_assert. The static_assert will then check that their new
struct is compliant.
---
gdb/testsuite/gdb.gdb/python-traits-check.exp | 156 ++++++++++++++++++
1 file changed, 156 insertions(+)
create mode 100644 gdb/testsuite/gdb.gdb/python-traits-check.exp
Comments
>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
Andrew> The previous two commits added a new type trait which can be used
Andrew> within a static_assert to check the properties of a struct used by GDB
Andrew> to implement Python objects.
Andrew> The previous commit fixed a bug in GDB which this trait check exposed.
Andrew> This commit adds a new test gdb.gdb/python-traits-check.exp which
Andrew> checks that every struct in the Python/ directory that inherits from
Andrew> PyObject, has a suitable static_assert in place.
Andrew> Adding this test should mean that if someone adds a new Python object
Andrew> type to GDB, and they forget to add the static_assert, then this test
Andrew> should give a failure, which should remind them to add the required
Andrew> static_assert. The static_assert will then check that their new
Andrew> struct is compliant.
With the "safety" work we can have the compiler check this; or if the
tp_init plan works out, I guess just drop the requirement entirely.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
Tom Tromey <tom@tromey.com> writes:
>>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
>
> Andrew> The previous two commits added a new type trait which can be used
> Andrew> within a static_assert to check the properties of a struct used by GDB
> Andrew> to implement Python objects.
>
> Andrew> The previous commit fixed a bug in GDB which this trait check exposed.
>
> Andrew> This commit adds a new test gdb.gdb/python-traits-check.exp which
> Andrew> checks that every struct in the Python/ directory that inherits from
> Andrew> PyObject, has a suitable static_assert in place.
>
> Andrew> Adding this test should mean that if someone adds a new Python object
> Andrew> type to GDB, and they forget to add the static_assert, then this test
> Andrew> should give a failure, which should remind them to add the required
> Andrew> static_assert. The static_assert will then check that their new
> Andrew> struct is compliant.
>
> With the "safety" work we can have the compiler check this; or if the
> tp_init plan works out, I guess just drop the requirement entirely.
ACK. As with patch #1, once there's a better way, and the static_asserts
are no longer of value, I'm happy for this patch to be reverted. But
for now, while it adds some value, I've gone ahead and checked it in.
Thanks for reviewing this series,
Andrew
>
> Approved-By: Tom Tromey <tom@tromey.com>
>
> Tom
new file mode 100644
@@ -0,0 +1,156 @@
+# Copyright 2026 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/>.
+
+# Check that every struct inheriting from PyObject in the gdb/python/
+# directory has a corresponding static_assert for
+# gdb::is_python_allocatable_v immediately after the struct definition.
+# The expected format is:
+#
+# struct some_new_type : public PyObject
+# {
+# ... fields go here ...
+# };
+#
+# static_assert (gdb::is_python_allocatable_v<some_new_type>);
+#
+# It is OK to add comments between the struct and the static_assert if
+# needed, but nothing else, the static_assert must be the next non-empty,
+# non-comment line.
+#
+# If the new type has no fields then this can be written like:
+#
+# struct some_empty_type : public PyObject
+# {};
+#
+# static_assert (gdb::is_python_allocatable_v<some_empty_type>);
+#
+# We do have a few of these in GDB currently. We require that the
+# static_assert still be present because (a) it has zero run-time cost,
+# and (b) it catches issues if fields are added in the future.
+
+set python_dir "$srcdir/$subdir/../../python"
+
+# Gather all .c and .h files in the python directory.
+set files [lsort [concat \
+ [glob -nocomplain -directory $python_dir *.c] \
+ [glob -nocomplain -directory $python_dir *.h]]]
+
+gdb_assert { [llength $files] > 0 } "found python source files"
+
+# Check a single file for PyObject-derived structs and matching
+# static_asserts.
+#
+# Opens FILENAME, reads it line by line looking for struct definitions of
+# the form "struct NAME : public PyObject". For each one found, scans
+# forward past the struct body, then checks that a matching static_assert
+# line follows, allowing only blank lines and GDB-style comments to
+# intervene.
+
+proc check_file { filename } {
+ set fd [open $filename r]
+ set lines [split [read $fd] "\n"]
+ close $fd
+
+ set num_lines [llength $lines]
+ set short_name [file tail $filename]
+
+ for { set i 0 } { $i < $num_lines } { incr i } {
+ set line [lindex $lines $i]
+
+ # Look for struct definitions inheriting from PyObject. These
+ # start in column 0.
+ if { ![regexp {^struct (\w+)\s*:\s*public PyObject} \
+ $line whole struct_name] } {
+ continue
+ }
+
+ set testname "$short_name: $struct_name: static assert check"
+
+ # Found a struct. Now scan forward for the closing brace and
+ # semicolon. Within the struct body, lines are either blank or
+ # start with whitespace. The closing line starts in column 0. For
+ # empty structs the open and close brace may appear together on a
+ # single line.
+ set found_close false
+ for { incr i } { $i < $num_lines } { incr i } {
+ set line [lindex $lines $i]
+ if { [regexp "^(?:\\{\\s*)?\\};" $line] } {
+ set found_close true
+ break
+ }
+ }
+
+ if { !$found_close } {
+ fail "$testname (no closing brace found)"
+ continue
+ }
+
+ # Now scan forward from the line after the struct close, skipping
+ # empty lines and GDB-style /* ... */ comments. The next non-blank,
+ # non-comment line should be the static_assert.
+ set in_comment false
+ set found_assert false
+ set found_other false
+ for { incr i } { $i < $num_lines } { incr i } {
+ set line [lindex $lines $i]
+
+ if { $in_comment } {
+ # Inside a multi-line comment, look for the closing "*/".
+ if { [regexp {\*/} $line] } {
+ set in_comment false
+ }
+ continue
+ }
+
+ # Skip blank lines.
+ if { [regexp {^\s*$} $line] } {
+ continue
+ }
+
+ # Check for the start of a comment.
+ if { [regexp {^\s*/\*} $line] } {
+ # If the comment also ends on this line then we don't need
+ # to enter IN_COMMENT mode, we can just ignore this line.
+ if { ![regexp {\*/} $line] } {
+ set in_comment true
+ }
+ continue
+ }
+
+ # This is a non-blank, non-comment line. Check if it is the
+ # expected static_assert.
+ set expected \
+ "static_assert (gdb::is_python_allocatable_v<$struct_name>);"
+ if { $line eq $expected } {
+ set found_assert true
+ } else {
+ set found_other true
+ }
+ break
+ }
+
+ if { $found_assert } {
+ pass $testname
+ } elseif { $found_other } {
+ fail "$testname (missing static_assert)"
+ } else {
+ fail "$testname (reached end of file)"
+ }
+ }
+}
+
+foreach file $files {
+ check_file $file
+}