* [PATCH 0/7] [gdb] Add superblocks range loops
@ 2026-04-23 6:35 Tom de Vries
2026-04-23 6:35 ` [PATCH 1/7] [gdb] Use block::function_block Tom de Vries
` (6 more replies)
0 siblings, 7 replies; 26+ messages in thread
From: Tom de Vries @ 2026-04-23 6:35 UTC (permalink / raw)
To: gdb-patches
This series attempts to rewrite code like:
...
while (block != NULL)
{
...
block = block->superblock ();
}
...
using range loops.
I'm having problems with the naming, and would like some feedback on that,
hence this is an RFC.
Given these problems, I didn't rewrite a lot of code, just enough to
demonstrate the range loops.
Tom de Vries (7):
[gdb] Use block::function_block
[gdbsupport] Add parameterless iterator_range constructor
[gdbsupport] Factor out base_next_iterator
[gdb] Add block::superblocks
[gdb] Use block::super_blocks
[gdb] Add block::function_blocks
[gdb] Use block::function_blocks
gdb/ada-lang.c | 52 +++++++---------
gdb/block.h | 78 ++++++++++++++++++++++++
gdb/blockframe.c | 5 +-
gdb/symmisc.c | 9 ++-
gdb/unittests/iterator-range-selftests.c | 2 +-
gdbsupport/iterator-range.h | 5 ++
gdbsupport/next-iterator.h | 59 ++++++++++++++----
7 files changed, 159 insertions(+), 51 deletions(-)
base-commit: 34fc62a27e1d8827aa2e64610f4e4b03e5825fc3
--
2.51.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 1/7] [gdb] Use block::function_block
2026-04-23 6:35 [PATCH 0/7] [gdb] Add superblocks range loops Tom de Vries
@ 2026-04-23 6:35 ` Tom de Vries
2026-04-23 14:33 ` Tom Tromey
2026-04-23 6:35 ` [PATCH 2/7] [gdbsupport] Add parameterless iterator_range constructor Tom de Vries
` (5 subsequent siblings)
6 siblings, 1 reply; 26+ messages in thread
From: Tom de Vries @ 2026-04-23 6:35 UTC (permalink / raw)
To: gdb-patches
Use block::function_block to simplify get_frame_function.
---
gdb/blockframe.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/gdb/blockframe.c b/gdb/blockframe.c
index ce28d99c7e4..8e99da11d22 100644
--- a/gdb/blockframe.c
+++ b/gdb/blockframe.c
@@ -120,10 +120,9 @@ get_frame_function (const frame_info_ptr &frame)
if (bl == NULL)
return NULL;
- while (bl->function () == NULL && bl->superblock () != NULL)
- bl = bl->superblock ();
+ bl = bl->function_block ();
- return bl->function ();
+ return bl == nullptr ? nullptr : bl->function ();
}
\f
--
2.51.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 2/7] [gdbsupport] Add parameterless iterator_range constructor
2026-04-23 6:35 [PATCH 0/7] [gdb] Add superblocks range loops Tom de Vries
2026-04-23 6:35 ` [PATCH 1/7] [gdb] Use block::function_block Tom de Vries
@ 2026-04-23 6:35 ` Tom de Vries
2026-04-23 14:34 ` Tom Tromey
2026-04-23 6:35 ` [PATCH 3/7] [gdbsupport] Factor out base_next_iterator Tom de Vries
` (4 subsequent siblings)
6 siblings, 1 reply; 26+ messages in thread
From: Tom de Vries @ 2026-04-23 6:35 UTC (permalink / raw)
To: gdb-patches
I noticed that there's no convenient way to create an empty range in
iterator_range. Add this.
---
gdb/unittests/iterator-range-selftests.c | 2 +-
gdbsupport/iterator-range.h | 5 +++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/gdb/unittests/iterator-range-selftests.c b/gdb/unittests/iterator-range-selftests.c
index a05531d95e0..7c0f7f57484 100644
--- a/gdb/unittests/iterator-range-selftests.c
+++ b/gdb/unittests/iterator-range-selftests.c
@@ -67,7 +67,7 @@ test_iterator_range ()
{
/* Empty range. */
- auto r3 = int_array_iterator_range (end, end);
+ auto r3 = int_array_iterator_range ();
test_iterator_range_1 (r3, nullptr, 0, end, end);
}
diff --git a/gdbsupport/iterator-range.h b/gdbsupport/iterator-range.h
index 42185546f5a..98be1e67152 100644
--- a/gdbsupport/iterator-range.h
+++ b/gdbsupport/iterator-range.h
@@ -27,6 +27,11 @@ struct iterator_range
{
using iterator = IteratorType;
+ /* Create an empty range. */
+ explicit iterator_range ()
+ : iterator_range (IteratorType {}, IteratorType {})
+ {}
+
/* Create an iterator_range using BEGIN as the begin iterator.
Assume that the end iterator can be default-constructed. */
--
2.51.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
2026-04-23 6:35 [PATCH 0/7] [gdb] Add superblocks range loops Tom de Vries
2026-04-23 6:35 ` [PATCH 1/7] [gdb] Use block::function_block Tom de Vries
2026-04-23 6:35 ` [PATCH 2/7] [gdbsupport] Add parameterless iterator_range constructor Tom de Vries
@ 2026-04-23 6:35 ` Tom de Vries
[not found] ` <87340kpbwx.fsf@tromey.com>
2026-04-30 16:24 ` Simon Marchi
2026-04-23 6:35 ` [PATCH 4/7] [gdb] Add block::superblocks Tom de Vries
` (3 subsequent siblings)
6 siblings, 2 replies; 26+ messages in thread
From: Tom de Vries @ 2026-04-23 6:35 UTC (permalink / raw)
To: gdb-patches
Template struct next_iterator<T> allows iterating over the "next" field of T.
I decided to generalize this to be able to use any field.
A first thought was to use macros, but nowadays we try to do things more in
native c++, so I didn't explore that further.
Instead, this patch factors out base_next_iterator, which contains all parts
not specific to "next", in other words, everything except the operator++.
Finally, I explored using a pointer-to-member template argument T::next, but
that meant that using an incomplete T was no longer allowed, so it's not
a drop-in replacement. I also didn't explore this option further, and made
a note of it in the base_next_iterator comment.
---
gdbsupport/next-iterator.h | 59 ++++++++++++++++++++++++++++++--------
1 file changed, 47 insertions(+), 12 deletions(-)
diff --git a/gdbsupport/next-iterator.h b/gdbsupport/next-iterator.h
index 0c90428d349..1dee941a252 100644
--- a/gdbsupport/next-iterator.h
+++ b/gdbsupport/next-iterator.h
@@ -21,27 +21,43 @@
#include "gdbsupport/iterator-range.h"
-/* An iterator that uses the 'next' field of a type to iterate. This
- can be used with various GDB types that are stored as linked
- lists. */
+/* An iterator base class for iterating over a field of a type. In order to
+ form a functioning iterator, classes inheriting this should define an
+ operator++, which determines the actual field that is iterated over.
+
+ Instead of factoring out a base class, we could use something like this:
+
+ template<typename T, auto F = &T::next>
+ struct next_iterator
+ {
+ ...
+ self_type &operator++ ()
+ {
+ m_item = m_item->*F;
+ return *this;
+ }
+ ...
+ }
+
+ but that has the drawback that it doesn't work with incomplete T. */
template<typename T>
-struct next_iterator
+struct base_next_iterator
{
- typedef next_iterator self_type;
+ typedef base_next_iterator self_type;
typedef T *value_type;
typedef T *&reference;
typedef T **pointer;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
- explicit next_iterator (T *item)
+ explicit base_next_iterator (T *item)
: m_item (item)
{
}
/* Create a one-past-the-end iterator. */
- next_iterator ()
+ base_next_iterator ()
: m_item (nullptr)
{
}
@@ -61,15 +77,34 @@ struct next_iterator
return m_item != other.m_item;
}
- self_type &operator++ ()
+protected:
+
+ T *m_item;
+};
+
+/* An iterator that uses the 'next' field of a type to iterate. This
+ can be used with various GDB types that are stored as linked
+ lists. */
+
+template<typename T>
+struct next_iterator : base_next_iterator<T> {
+ typedef next_iterator self_type;
+ typedef T *value_type;
+ typedef T *&reference;
+ typedef T **pointer;
+
+ explicit next_iterator (T *item)
+ : base_next_iterator<T> (item)
{
- m_item = m_item->next;
- return *this;
}
-private:
+ next_iterator () = default;
- T *m_item;
+ self_type &operator++ ()
+ {
+ this->m_item = this->m_item->next;
+ return *this;
+ }
};
/* A convenience wrapper to make a range type around a next_iterator. */
--
2.51.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 4/7] [gdb] Add block::superblocks
2026-04-23 6:35 [PATCH 0/7] [gdb] Add superblocks range loops Tom de Vries
` (2 preceding siblings ...)
2026-04-23 6:35 ` [PATCH 3/7] [gdbsupport] Factor out base_next_iterator Tom de Vries
@ 2026-04-23 6:35 ` Tom de Vries
2026-04-27 11:11 ` Jan Vrany
2026-04-30 16:24 ` Simon Marchi
2026-04-23 6:35 ` [PATCH 5/7] [gdb] Use block::super_blocks Tom de Vries
` (2 subsequent siblings)
6 siblings, 2 replies; 26+ messages in thread
From: Tom de Vries @ 2026-04-23 6:35 UTC (permalink / raw)
To: gdb-patches
Add a function block::superblocks that can be used to transform:
...
while (block != NULL)
{
...
block = block->superblock ();
}
...
into:
...
for (auto b : block::super_blocks (block))
{
...
}
...
I'm not sure about the name. It might suggest that block is not included in
the iteration, but in fact it is.
I considered block::block_and_supers () instead, but it seems a bit awkward.
---
gdb/block.h | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/gdb/block.h b/gdb/block.h
index 091120ae2b8..4a1db79d7ed 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -108,6 +108,27 @@ struct blockranges
struct block : public allocate_on_obstack<block>
{
+ /* Variant of next_iterator using the superblock field instead of next. */
+ struct superblock_iterator : base_next_iterator<const block>
+ {
+ typedef superblock_iterator self_type;
+
+ explicit superblock_iterator (value_type item)
+ : base_next_iterator (item)
+ {
+ }
+
+ superblock_iterator () = default;
+
+ self_type &operator++ ()
+ {
+ this->m_item = this->m_item->superblock ();
+ return *this;
+ }
+ };
+
+ using superblock_range = iterator_range<superblock_iterator>;
+
/* Return this block's start address. */
CORE_ADDR start () const
{ return m_start; }
@@ -306,6 +327,21 @@ struct block : public allocate_on_obstack<block>
struct dynamic_prop *static_link () const;
+ superblock_range super_blocks () const
+ {
+ superblock_range::iterator begin (this);
+
+ return superblock_range (std::move (begin));
+ }
+
+ static superblock_range super_blocks (const block *b)
+ {
+ if (b == nullptr)
+ return superblock_range ();
+
+ return b->super_blocks ();
+ }
+
/* Return true if block A is lexically nested within this block, or
if A and this block have the same pc range. Return false
otherwise. If ALLOW_NESTED is true, then block A is considered
--
2.51.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 5/7] [gdb] Use block::super_blocks
2026-04-23 6:35 [PATCH 0/7] [gdb] Add superblocks range loops Tom de Vries
` (3 preceding siblings ...)
2026-04-23 6:35 ` [PATCH 4/7] [gdb] Add block::superblocks Tom de Vries
@ 2026-04-23 6:35 ` Tom de Vries
2026-04-24 16:27 ` Tom Tromey
2026-04-23 6:35 ` [PATCH 6/7] [gdb] Add block::function_blocks Tom de Vries
2026-04-23 6:35 ` [PATCH 7/7] [gdb] Use block::function_blocks Tom de Vries
6 siblings, 1 reply; 26+ messages in thread
From: Tom de Vries @ 2026-04-23 6:35 UTC (permalink / raw)
To: gdb-patches
Use block::super_blocks in a few locations to simplify code.
---
gdb/ada-lang.c | 9 +++------
gdb/symmisc.c | 9 ++++-----
2 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 71a338ce17e..da6c3b04dd9 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -13825,9 +13825,7 @@ class ada_language : public language_defn
/* Search upwards from currently selected frame (so that we can
complete on local vars. */
- for (const block *b = get_selected_block (0);
- b != nullptr;
- b = b->superblock ())
+ for (auto b : block::super_blocks (get_selected_block (0)))
{
if (b->is_static_block ())
surrounding_static_block = b; /* For elmin of dups */
@@ -13852,9 +13850,8 @@ class ada_language : public language_defn
auto callback = [&] (compunit_symtab *s)
{
QUIT;
- for (const block *b = s->blockvector ()->static_block ();
- b != nullptr;
- b = b->superblock ())
+ for (auto b
+ : block::super_blocks (s->blockvector ()->static_block ()))
{
/* Don't do this block twice. */
if (b == surrounding_static_block)
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index 89374bd8a2f..5a34986ebc2 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -961,11 +961,10 @@ block_depth (const struct block *block)
{
int i = 0;
- while ((block = block->superblock ()) != NULL)
- {
- i++;
- }
- return i;
+ for (auto b [[maybe_unused]]: block::super_blocks (block))
+ i++;
+
+ return i - 1;
}
\f
--
2.51.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 6/7] [gdb] Add block::function_blocks
2026-04-23 6:35 [PATCH 0/7] [gdb] Add superblocks range loops Tom de Vries
` (4 preceding siblings ...)
2026-04-23 6:35 ` [PATCH 5/7] [gdb] Use block::super_blocks Tom de Vries
@ 2026-04-23 6:35 ` Tom de Vries
2026-04-23 6:35 ` [PATCH 7/7] [gdb] Use block::function_blocks Tom de Vries
6 siblings, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-04-23 6:35 UTC (permalink / raw)
To: gdb-patches
Add a function block::function_blocks that can be used to transform:
...
while (block != NULL)
{
...
if (block->function () != nullptr)
break;
block = block->superblock ();
}
...
into:
...
for (auto b : block::function_blocks (block))
{
...
}
...
I'm not sure about the name. It might suggest that it iterates over all
function blocks, but that's not the case, it only does so for the inner
function.
I considered function block::inner_function_blocks instead, but it seems a bit
awkward.
---
gdb/block.h | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/gdb/block.h b/gdb/block.h
index 4a1db79d7ed..53cc5435576 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -127,7 +127,34 @@ struct block : public allocate_on_obstack<block>
}
};
+ /* Variant of next_iterator using the superblock field instead of next. */
+ struct function_block_iterator : base_next_iterator<const block>
+ {
+ typedef function_block_iterator self_type;
+
+ explicit function_block_iterator (value_type item)
+ : base_next_iterator (item->is_global_block () || item->is_static_block ()
+ ? nullptr : item)
+ {
+ }
+
+ function_block_iterator () = default;
+
+ self_type &operator++ ()
+ {
+ if (this->m_item->function () != nullptr)
+ {
+ this->m_item = nullptr;
+ return *this;
+ }
+
+ this->m_item = this->m_item->superblock ();
+ return *this;
+ }
+ };
+
using superblock_range = iterator_range<superblock_iterator>;
+ using function_block_range = iterator_range<function_block_iterator>;
/* Return this block's start address. */
CORE_ADDR start () const
@@ -334,6 +361,13 @@ struct block : public allocate_on_obstack<block>
return superblock_range (std::move (begin));
}
+ function_block_range function_blocks () const
+ {
+ function_block_range::iterator begin (this);
+
+ return function_block_range (std::move (begin));
+ }
+
static superblock_range super_blocks (const block *b)
{
if (b == nullptr)
@@ -342,6 +376,14 @@ struct block : public allocate_on_obstack<block>
return b->super_blocks ();
}
+ static function_block_range function_blocks (const block *b)
+ {
+ if (b == nullptr)
+ return function_block_range ();
+
+ return b->function_blocks ();
+ }
+
/* Return true if block A is lexically nested within this block, or
if A and this block have the same pc range. Return false
otherwise. If ALLOW_NESTED is true, then block A is considered
--
2.51.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 7/7] [gdb] Use block::function_blocks
2026-04-23 6:35 [PATCH 0/7] [gdb] Add superblocks range loops Tom de Vries
` (5 preceding siblings ...)
2026-04-23 6:35 ` [PATCH 6/7] [gdb] Add block::function_blocks Tom de Vries
@ 2026-04-23 6:35 ` Tom de Vries
6 siblings, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-04-23 6:35 UTC (permalink / raw)
To: gdb-patches
Add a use of block::function_blocks
---
gdb/ada-lang.c | 43 +++++++++++++++++++------------------------
1 file changed, 19 insertions(+), 24 deletions(-)
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index da6c3b04dd9..fa465d2439b 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -13120,32 +13120,27 @@ ada_add_exceptions_from_frame (compiled_regex *preg,
const frame_info_ptr &frame,
std::vector<ada_exc_info> *exceptions)
{
- const struct block *block = get_frame_block (frame, 0);
+ const struct block *frame_block = get_frame_block (frame, 0);
- while (block != 0)
- {
- for (struct symbol *sym : block_iterator_range (block))
- {
- switch (sym->loc_class ())
- {
- case LOC_TYPEDEF:
- case LOC_BLOCK:
- case LOC_CONST:
- break;
- default:
- if (ada_is_exception_sym (sym))
- {
- struct ada_exc_info info = {sym->print_name (),
- sym->value_address ()};
+ for (auto block : block::function_blocks (frame_block))
+ for (struct symbol *sym : block_iterator_range (block))
+ {
+ switch (sym->loc_class ())
+ {
+ case LOC_TYPEDEF:
+ case LOC_BLOCK:
+ case LOC_CONST:
+ break;
+ default:
+ if (ada_is_exception_sym (sym))
+ {
+ struct ada_exc_info info = {sym->print_name (),
+ sym->value_address ()};
- exceptions->push_back (info);
- }
- }
- }
- if (block->function () != NULL)
- break;
- block = block->superblock ();
- }
+ exceptions->push_back (info);
+ }
+ }
+ }
}
/* Add all exceptions defined globally whose name name match
--
2.51.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 1/7] [gdb] Use block::function_block
2026-04-23 6:35 ` [PATCH 1/7] [gdb] Use block::function_block Tom de Vries
@ 2026-04-23 14:33 ` Tom Tromey
2026-04-24 12:19 ` Tom de Vries
0 siblings, 1 reply; 26+ messages in thread
From: Tom Tromey @ 2026-04-23 14:33 UTC (permalink / raw)
To: Tom de Vries; +Cc: gdb-patches
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
Tom> Use block::function_block to simplify get_frame_function.
It would be even simpler to have it use block::containing_function,
which AFAICT is identical to the current code.
Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 2/7] [gdbsupport] Add parameterless iterator_range constructor
2026-04-23 6:35 ` [PATCH 2/7] [gdbsupport] Add parameterless iterator_range constructor Tom de Vries
@ 2026-04-23 14:34 ` Tom Tromey
2026-04-24 12:20 ` Tom de Vries
0 siblings, 1 reply; 26+ messages in thread
From: Tom Tromey @ 2026-04-23 14:34 UTC (permalink / raw)
To: Tom de Vries; +Cc: gdb-patches
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
Tom> I noticed that there's no convenient way to create an empty range in
Tom> iterator_range. Add this.
Tom> + /* Create an empty range. */
Tom> + explicit iterator_range ()
Tom> + : iterator_range (IteratorType {}, IteratorType {})
Tom> + {}
I don't think the 'explicit' is needed here.
Also there's no real need to use constructor delegation here but OTOH it
doesn't really matter I suppose.
Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 1/7] [gdb] Use block::function_block
2026-04-23 14:33 ` Tom Tromey
@ 2026-04-24 12:19 ` Tom de Vries
0 siblings, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-04-24 12:19 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 4/23/26 4:33 PM, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
>
> Tom> Use block::function_block to simplify get_frame_function.
>
> It would be even simpler to have it use block::containing_function,
> which AFAICT is identical to the current code.
>
Hi,
thanks for the review.
I've:
- update the patch to use block::containing_function,
- updated $subject to "[gdb] Simplify get_frame_function", and
- pushed.
Thanks,
- Tom
> Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 2/7] [gdbsupport] Add parameterless iterator_range constructor
2026-04-23 14:34 ` Tom Tromey
@ 2026-04-24 12:20 ` Tom de Vries
0 siblings, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-04-24 12:20 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 4/23/26 4:34 PM, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
>
> Tom> I noticed that there's no convenient way to create an empty range in
> Tom> iterator_range. Add this.
>
> Tom> + /* Create an empty range. */
> Tom> + explicit iterator_range ()
> Tom> + : iterator_range (IteratorType {}, IteratorType {})
> Tom> + {}
>
> I don't think the 'explicit' is needed here.
>
> Also there's no real need to use constructor delegation here but OTOH it
> doesn't really matter I suppose.
Thanks for the review.
I ended up committing:
...
+ /* Create an empty range. */
+ iterator_range () = default;
...
Thanks,
- Tom
>
> Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 5/7] [gdb] Use block::super_blocks
2026-04-23 6:35 ` [PATCH 5/7] [gdb] Use block::super_blocks Tom de Vries
@ 2026-04-24 16:27 ` Tom Tromey
2026-04-24 21:24 ` Tom de Vries
0 siblings, 1 reply; 26+ messages in thread
From: Tom Tromey @ 2026-04-24 16:27 UTC (permalink / raw)
To: Tom de Vries; +Cc: gdb-patches
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
Tom> Use block::super_blocks in a few locations to simplify code.
Tom> - for (const block *b = get_selected_block (0);
Tom> - b != nullptr;
Tom> - b = b->superblock ())
Tom> + for (auto b : block::super_blocks (get_selected_block (0)))
Why not "get_selected_block(0)->super_blocks ()" here?
Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 5/7] [gdb] Use block::super_blocks
2026-04-24 16:27 ` Tom Tromey
@ 2026-04-24 21:24 ` Tom de Vries
0 siblings, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-04-24 21:24 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 4/24/26 6:27 PM, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
>
> Tom> Use block::super_blocks in a few locations to simplify code.
>
> Tom> - for (const block *b = get_selected_block (0);
> Tom> - b != nullptr;
> Tom> - b = b->superblock ())
> Tom> + for (auto b : block::super_blocks (get_selected_block (0)))
>
> Why not "get_selected_block(0)->super_blocks ()" here?
Because get_selected_block (0) might be nullptr.
I actually checked with an assert:
...
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 71a338ce17e..76639685977 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -13825,6 +13825,7 @@ class ada_language : public language_defn
/* Search upwards from currently selected frame (so that we can
complete on local vars. */
+ gdb_assert (get_selected_block (0) != nullptr);
for (const block *b = get_selected_block (0);
b != nullptr;
b = b->superblock ())
...
and it triggers in gdb.dwarf2/ada-cold-name.exp.
Thanks,
- Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 4/7] [gdb] Add block::superblocks
2026-04-23 6:35 ` [PATCH 4/7] [gdb] Add block::superblocks Tom de Vries
@ 2026-04-27 11:11 ` Jan Vrany
2026-05-01 13:06 ` Tom de Vries
2026-04-30 16:24 ` Simon Marchi
1 sibling, 1 reply; 26+ messages in thread
From: Jan Vrany @ 2026-04-27 11:11 UTC (permalink / raw)
To: Tom de Vries, gdb-patches
> Add a function block::superblocks that can be used to transform:
Hi,
a nit: shouldn't the commit summary and message say super_blocks (with underscore)?
On Thu, 2026-04-23 at 08:35 +0200, Tom de Vries wrote:
> Add a function block::superblocks that can be used to transform:
> ...
> while (block != NULL)
> {
> ...
> block = block->superblock ();
> }
> ...
> into:
> ...
> for (auto b : block::super_blocks (block))
> {
> ...
> }
> ...
>
> I'm not sure about the name. It might suggest that block is not included in
> the iteration, but in fact it is.
> I considered block::block_and_supers () instead, but it seems a bit awkward.
I'd personally prefer block::block_and_supers over block::super_blocks. It's bit
awkward, but better awkward than misleading.
> ---
> gdb/block.h | 36 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
>
> diff --git a/gdb/block.h b/gdb/block.h
> index 091120ae2b8..4a1db79d7ed 100644
> --- a/gdb/block.h
> +++ b/gdb/block.h
> @@ -108,6 +108,27 @@ struct blockranges
>
> struct block : public allocate_on_obstack<block>
> {
> + /* Variant of next_iterator using the superblock field instead of next. */
> + struct superblock_iterator : base_next_iterator<const block>
> + {
> + typedef superblock_iterator self_type;
> +
> + explicit superblock_iterator (value_type item)
> + : base_next_iterator (item)
> + {
> + }
> +
> + superblock_iterator () = default;
> +
> + self_type &operator++ ()
> + {
> + this->m_item = this->m_item->superblock ();
> + return *this;
> + }
> + };
> +
> + using superblock_range = iterator_range<superblock_iterator>;
> +
> /* Return this block's start address. */
> CORE_ADDR start () const
> { return m_start; }
> @@ -306,6 +327,21 @@ struct block : public allocate_on_obstack<block>
>
> struct dynamic_prop *static_link () const;
>
>
I think comment would be useful here, especially as "super_blocks"
returns this block AND all its superblocks.
> + superblock_range super_blocks () const
> + {
> + superblock_range::iterator begin (this);
> +
> + return superblock_range (std::move (begin));
> + }
> +
> + static superblock_range super_blocks (const block *b)
> + {
> + if (b == nullptr)
> + return superblock_range ();
> +
> + return b->super_blocks ();
> + }
> +
Thanks!
Jan
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
[not found] ` <87340kpbwx.fsf@tromey.com>
@ 2026-04-30 4:49 ` Tom de Vries
2026-05-01 12:54 ` Tom de Vries
2026-05-01 13:00 ` Tom de Vries
1 sibling, 1 reply; 26+ messages in thread
From: Tom de Vries @ 2026-04-30 4:49 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 4/24/26 6:21 PM, Tom Tromey wrote:
> Tom> + Instead of factoring out a base class, we could use something like this:
> Tom> +
> Tom> + template<typename T, auto F = &T::next>
> Tom> + struct next_iterator
> Tom> + {
> Tom> + ...
> Tom> + self_type &operator++ ()
> Tom> + {
> Tom> + m_item = m_item->*F;
> Tom> + return *this;
> Tom> + }
> Tom> + ...
> Tom> + }
> Tom> +
> Tom> + but that has the drawback that it doesn't work with incomplete T. */
>
> I am curious about this because I wonder how the current code could work
> with an incomplete T -- since the code references T::next directly.
Yeah, that's a good question.
I wrote the following stand-alone demonstrator:
...
$ cat test.c
#include <cstddef>
#include <utility>
#include <iterator>
#include <cstdio>
#include "gdbsupport/next-iterator.h"
struct list;
void
print_pointer (list *l)
{
auto begin = next_iterator<struct list> (l);
for (auto elem : next_range<struct list> (begin))
printf ("%p\n", elem);
}
struct list
{
int i;
struct list *next;
};
void
print_int (list *l)
{
auto begin = next_iterator<struct list> (l);
for (auto elem : next_range<struct list> (begin))
printf ("%d\n", elem->i);
}
struct list a = { 1, nullptr };
struct list b = { 2, &a };
struct list c = { 3, &b };
int
main ()
{
print_pointer (&c);
print_int (&c);
return 0;
}
$ g++ test.c -I .
$ ./a.out
0x404040
0x404030
0x404020
3
2
1
$
...
I asked an LLM about this, and it explained that this is deferred or
delayed template instantiation at work.
Basically the operator++ is used in print_pointer, but not yet
instantiated, so we don't have to know the details of the incomplete
type at that point.
Thanks,
- Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
2026-04-23 6:35 ` [PATCH 3/7] [gdbsupport] Factor out base_next_iterator Tom de Vries
[not found] ` <87340kpbwx.fsf@tromey.com>
@ 2026-04-30 16:24 ` Simon Marchi
2026-04-30 19:09 ` Simon Marchi
2026-05-01 13:15 ` Tom de Vries
1 sibling, 2 replies; 26+ messages in thread
From: Simon Marchi @ 2026-04-30 16:24 UTC (permalink / raw)
To: Tom de Vries, gdb-patches
On 4/23/26 2:35 AM, Tom de Vries wrote:
> diff --git a/gdbsupport/next-iterator.h b/gdbsupport/next-iterator.h
> index 0c90428d349..1dee941a252 100644
> --- a/gdbsupport/next-iterator.h
> +++ b/gdbsupport/next-iterator.h
> @@ -21,27 +21,43 @@
>
> #include "gdbsupport/iterator-range.h"
>
> -/* An iterator that uses the 'next' field of a type to iterate. This
> - can be used with various GDB types that are stored as linked
> - lists. */
> +/* An iterator base class for iterating over a field of a type. In order to
> + form a functioning iterator, classes inheriting this should define an
> + operator++, which determines the actual field that is iterated over.
> +
> + Instead of factoring out a base class, we could use something like this:
> +
> + template<typename T, auto F = &T::next>
> + struct next_iterator
> + {
> + ...
> + self_type &operator++ ()
> + {
> + m_item = m_item->*F;
> + return *this;
> + }
> + ...
> + }
> +
> + but that has the drawback that it doesn't work with incomplete T. */
I'm of the opinion that this information is not really relevant here, it
belongs to the commit message.
I was initially skeptic that the version with the pointer-to-member
didn't work, so I tried it for myself and indeed, I don't think it would
work with the block and superblock_iterator relationship (at least, if
we want to have a method of block returning a
superblock_iterator/range).
While next_iterator is "iterate using the raw field `next`",
base_next_iterator is more generic just for "iterating over a field of a
type". It looks like you defined a pretty generic class where the
derived class only needs to fill in operator++ (and perhaps a bit more
boilerplate). But operator++ doesn't have to just follow a field, it
can have any logic in there, as function_block_iterator proves, which is
nice. Following the raw `next` field (or another raw field of any other
name) is just one specific case.
Given that all the concrete iterate needs to provide is "given a
reference to the current element, give me the next element", I think we
could reduce the boilerplate needed at each site by having the concrete
iterator provide a functor that does the increment (much like you
defined an std::map by providing a "hash" functor, not by deriving from
std::map).
Rather than copy paste what I mean here, I pushed what I think it could
look like to the users/simark/next-iterator branch.
https://sourceware.org/git/?p=binutils-gdb.git;a=shortlog;h=refs/heads/users/simark/next-iterator
https://sourceware.org/cgit/binutils-gdb/log/?h=users/simark/next-iterator
But just to illustrate here is how the function block iterator and
range are implemented:
/* Iterator and range type to iterate over this block and its superblocks
within a function. */
struct function_block_incrementer
{
const block *operator() (const block &b) const noexcept
{
/* If the current item is the function-defining block, we're done. */
if (b.function () != nullptr)
return nullptr;
return b.superblock ();
}
};
using function_block_iterator
= base_next_iterator<const block, function_block_incrementer>;
using function_block_range = iterator_range<function_block_iterator>;
I kept the name `base_next_iterator`, we could perhaps find a better
name, since it's not realted to `next_iterator` more than any other
concrete instance. Perhaps `base_iterator`, or `simple_iterator`?
> @@ -61,15 +77,34 @@ struct next_iterator
> return m_item != other.m_item;
> }
>
> - self_type &operator++ ()
> +protected:
> +
> + T *m_item;
> +};
> +
> +/* An iterator that uses the 'next' field of a type to iterate. This
> + can be used with various GDB types that are stored as linked
> + lists. */
> +
> +template<typename T>
> +struct next_iterator : base_next_iterator<T> {
> + typedef next_iterator self_type;
> + typedef T *value_type;
> + typedef T *&reference;
> + typedef T **pointer;
I think we should use `using` instead of typedef everywhere now.
> +
> + explicit next_iterator (T *item)
> + : base_next_iterator<T> (item)
> {
> - m_item = m_item->next;
> - return *this;
> }
>
> -private:
> + next_iterator () = default;
Just noting that instead of those two constructors, you can use:
using base_next_iterator<T>::base_next_iterator;
Simon
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 4/7] [gdb] Add block::superblocks
2026-04-23 6:35 ` [PATCH 4/7] [gdb] Add block::superblocks Tom de Vries
2026-04-27 11:11 ` Jan Vrany
@ 2026-04-30 16:24 ` Simon Marchi
2026-05-01 13:19 ` Tom de Vries
1 sibling, 1 reply; 26+ messages in thread
From: Simon Marchi @ 2026-04-30 16:24 UTC (permalink / raw)
To: Tom de Vries, gdb-patches
On 4/23/26 2:35 AM, Tom de Vries wrote:
> +
> + self_type &operator++ ()
> + {
> + this->m_item = this->m_item->superblock ();
IMO we don't need to use `this->` on members prefixed with `m_`, because
it's already clear that they are fields of this class. I do use
`this->` on fields that are not prefixed with `m_` though, otherwise
it's not clear where it comes from.
Simon
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
2026-04-30 16:24 ` Simon Marchi
@ 2026-04-30 19:09 ` Simon Marchi
2026-05-01 12:57 ` Tom Tromey
2026-05-01 13:20 ` Tom de Vries
2026-05-01 13:15 ` Tom de Vries
1 sibling, 2 replies; 26+ messages in thread
From: Simon Marchi @ 2026-04-30 19:09 UTC (permalink / raw)
To: Tom de Vries, gdb-patches
On 4/30/26 12:24 PM, Simon Marchi wrote:
> Rather than copy paste what I mean here, I pushed what I think it could
> look like to the users/simark/next-iterator branch.
>
> https://sourceware.org/git/?p=binutils-gdb.git;a=shortlog;h=refs/heads/users/simark/next-iterator
> https://sourceware.org/cgit/binutils-gdb/log/?h=users/simark/next-iterator
>
> But just to illustrate here is how the function block iterator and
> range are implemented:
>
> /* Iterator and range type to iterate over this block and its superblocks
> within a function. */
>
> struct function_block_incrementer
> {
> const block *operator() (const block &b) const noexcept
> {
> /* If the current item is the function-defining block, we're done. */
> if (b.function () != nullptr)
> return nullptr;
>
> return b.superblock ();
> }
> };
>
> using function_block_iterator
> = base_next_iterator<const block, function_block_incrementer>;
> using function_block_range = iterator_range<function_block_iterator>;
Just a note: in your version, if you pass a global or static block to
function_block_iterator, it sets the item to nullptr right away, so you
iterate on nothing. I couldn't do that in my version, so I chose to
assert that the block isn't global or static, which puts the
responsibillity on the caller not to call this on a global or static
block. The only caller is in ada-lang.c, it passes the result of
get_frame_block(). I guess that a block from a frame is always in a
function (is not the global or static block), but if that assumption
isn't true, then a check would be needed in the caller.
Simon
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
2026-04-30 4:49 ` Tom de Vries
@ 2026-05-01 12:54 ` Tom de Vries
0 siblings, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-05-01 12:54 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 4/30/26 6:49 AM, Tom de Vries wrote:
> On 4/24/26 6:21 PM, Tom Tromey wrote:
>> Tom> + Instead of factoring out a base class, we could use something
>> like this:
>> Tom> +
>> Tom> + template<typename T, auto F = &T::next>
>> Tom> + struct next_iterator
>> Tom> + {
>> Tom> + ...
>> Tom> + self_type &operator++ ()
>> Tom> + {
>> Tom> + m_item = m_item->*F;
>> Tom> + return *this;
>> Tom> + }
>> Tom> + ...
>> Tom> + }
>> Tom> +
>> Tom> + but that has the drawback that it doesn't work with incomplete
>> T. */
>> I am curious about this because I wonder how the current code could work
>> with an incomplete T -- since the code references T::next directly.
>
> Yeah, that's a good question.
>
> I wrote the following stand-alone demonstrator:
> ...
> $ cat test.c
> #include <cstddef>
> #include <utility>
> #include <iterator>
> #include <cstdio>
>
> #include "gdbsupport/next-iterator.h"
>
> struct list;
>
> void
> print_pointer (list *l)
> {
> auto begin = next_iterator<struct list> (l);
> for (auto elem : next_range<struct list> (begin))
> printf ("%p\n", elem);
> }
>
> struct list
> {
> int i;
> struct list *next;
> };
>
> void
> print_int (list *l)
> {
> auto begin = next_iterator<struct list> (l);
> for (auto elem : next_range<struct list> (begin))
> printf ("%d\n", elem->i);
> }
>
> struct list a = { 1, nullptr };
> struct list b = { 2, &a };
> struct list c = { 3, &b };
>
> int
> main ()
> {
> print_pointer (&c);
> print_int (&c);
>
> return 0;
> }
> $ g++ test.c -I .
> $ ./a.out
> 0x404040
> 0x404030
> 0x404020
> 3
> 2
> 1
> $
> ...
>
> I asked an LLM about this, and it explained that this is deferred or
> delayed template instantiation at work.
>
> Basically the operator++ is used in print_pointer, but not yet
> instantiated, so we don't have to know the details of the incomplete
> type at that point.
I've written a next_iterator unit test, and added (part of) this
demonstrator (
https://sourceware.org/pipermail/gdb-patches/2026-May/227065.html ).
Thanks,
- Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
2026-04-30 19:09 ` Simon Marchi
@ 2026-05-01 12:57 ` Tom Tromey
2026-05-01 13:20 ` Tom de Vries
1 sibling, 0 replies; 26+ messages in thread
From: Tom Tromey @ 2026-05-01 12:57 UTC (permalink / raw)
To: Simon Marchi; +Cc: Tom de Vries, gdb-patches
>>>>> "Simon" == Simon Marchi <simark@simark.ca> writes:
Simon> The only caller is in ada-lang.c, it passes the result of
Simon> get_frame_block(). I guess that a block from a frame is always in a
Simon> function (is not the global or static block), but if that assumption
Simon> isn't true, then a check would be needed in the caller.
I think it would be a bug if a frame's block were the static block, but
OTOH we had to preserve that hack in the blockvector lookup code, so who
knows.
Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
[not found] ` <87340kpbwx.fsf@tromey.com>
2026-04-30 4:49 ` Tom de Vries
@ 2026-05-01 13:00 ` Tom de Vries
1 sibling, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-05-01 13:00 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 4/24/26 6:21 PM, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
> Tom> Instead, this patch factors out base_next_iterator, which contains all parts
> Tom> not specific to "next", in other words, everything except the operator++.
>
> This looks pretty reasonable to me, though I wonder if it would be
> improved using the CRTP idiom.
>
> It's not really needed but it could possibly be cleaner.
>
> https://en.cppreference.com/cpp/language/crtp
>
Thanks, I've done that in a v1 (
https://sourceware.org/pipermail/gdb-patches/2026-May/227066.html ).
> Tom> + Instead of factoring out a base class, we could use something like this:
> Tom> +
> Tom> + template<typename T, auto F = &T::next>
> Tom> + struct next_iterator
> Tom> + {
> Tom> + ...
> Tom> + self_type &operator++ ()
> Tom> + {
> Tom> + m_item = m_item->*F;
> Tom> + return *this;
> Tom> + }
> Tom> + ...
> Tom> + }
> Tom> +
> Tom> + but that has the drawback that it doesn't work with incomplete T. */
>
> I am curious about this because I wonder how the current code could work
> with an incomplete T -- since the code references T::next directly.
>
> Tom> - explicit next_iterator (T *item)
> Tom> + explicit base_next_iterator (T *item)
> Tom> : m_item (item)
> Tom> {
> Tom> }
>
> Tom> /* Create a one-past-the-end iterator. */
> Tom> - next_iterator ()
> Tom> + base_next_iterator ()
> Tom> : m_item (nullptr)
> Tom> {
> Tom> }
>
> These should probably be protected now because it doesn't make sense to
> directly instantiate a base_next_iterator.
>
I did that initially, but Simon suggested importing the constructor from
base_next_iterator, and that doesn't work if it's protected.
> Tom> +template<typename T>
> Tom> +struct next_iterator : base_next_iterator<T> {
>
> Brace placement.
>
Fixed.
> Tom> + typedef next_iterator self_type;
> Tom> + typedef T *value_type;
> Tom> + typedef T *&reference;
> Tom> + typedef T **pointer;
>
> I would have thought most of these would be inherited.
> (I guess with CRTP this could all be in the base class)
That's indeed what happened, these are gone now.
Thanks,
- Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 4/7] [gdb] Add block::superblocks
2026-04-27 11:11 ` Jan Vrany
@ 2026-05-01 13:06 ` Tom de Vries
0 siblings, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-05-01 13:06 UTC (permalink / raw)
To: Jan Vrany, gdb-patches
On 4/27/26 1:11 PM, Jan Vrany wrote:
>> Add a function block::superblocks that can be used to transform:
>
> Hi,
>
> a nit: shouldn't the commit summary and message say super_blocks (with underscore)?
>
>
Hi Jan,
yes, though now I've renamed it to blocks::block_and_superblocks, so
that's fixed in the v1 (
https://sourceware.org/pipermail/gdb-patches/2026-May/227068.html ).
> On Thu, 2026-04-23 at 08:35 +0200, Tom de Vries wrote:
>> Add a function block::superblocks that can be used to transform:
>> ...
>> while (block != NULL)
>> {
>> ...
>> block = block->superblock ();
>> }
>> ...
>> into:
>> ...
>> for (auto b : block::super_blocks (block))
>> {
>> ...
>> }
>> ...
>>
>> I'm not sure about the name. It might suggest that block is not included in
>> the iteration, but in fact it is.
>
>
>> I considered block::block_and_supers () instead, but it seems a bit awkward.
>
> I'd personally prefer block::block_and_supers over block::super_blocks. It's bit
> awkward, but better awkward than misleading.
>
Thanks for weighing in. As mentioned, I've gone with
blocks::block_and_superblocks, so I hope that's clear enough.
>> ---
>> gdb/block.h | 36 ++++++++++++++++++++++++++++++++++++
>> 1 file changed, 36 insertions(+)
>>
>> diff --git a/gdb/block.h b/gdb/block.h
>> index 091120ae2b8..4a1db79d7ed 100644
>> --- a/gdb/block.h
>> +++ b/gdb/block.h
>> @@ -108,6 +108,27 @@ struct blockranges
>>
>> struct block : public allocate_on_obstack<block>
>> {
>> + /* Variant of next_iterator using the superblock field instead of next. */
>> + struct superblock_iterator : base_next_iterator<const block>
>> + {
>> + typedef superblock_iterator self_type;
>> +
>> + explicit superblock_iterator (value_type item)
>> + : base_next_iterator (item)
>> + {
>> + }
>> +
>> + superblock_iterator () = default;
>> +
>> + self_type &operator++ ()
>> + {
>> + this->m_item = this->m_item->superblock ();
>> + return *this;
>> + }
>> + };
>> +
>> + using superblock_range = iterator_range<superblock_iterator>;
>> +
>> /* Return this block's start address. */
>> CORE_ADDR start () const
>> { return m_start; }
>> @@ -306,6 +327,21 @@ struct block : public allocate_on_obstack<block>
>>
>> struct dynamic_prop *static_link () const;
>>
>>
>
> I think comment would be useful here, especially as "super_blocks"
> returns this block AND all its superblocks.
>
I've added comments in the v1, thanks for pointing this out.
Thanks,
- Tom
>> + superblock_range super_blocks () const
>> + {
>> + superblock_range::iterator begin (this);
>> +
>> + return superblock_range (std::move (begin));
>> + }
>> +
>> + static superblock_range super_blocks (const block *b)
>> + {
>> + if (b == nullptr)
>> + return superblock_range ();
>> +
>> + return b->super_blocks ();
>> + }
>> +
>
>
> Thanks!
>
> Jan
>
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
2026-04-30 16:24 ` Simon Marchi
2026-04-30 19:09 ` Simon Marchi
@ 2026-05-01 13:15 ` Tom de Vries
1 sibling, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-05-01 13:15 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
On 4/30/26 6:24 PM, Simon Marchi wrote:
> On 4/23/26 2:35 AM, Tom de Vries wrote:
>> diff --git a/gdbsupport/next-iterator.h b/gdbsupport/next-iterator.h
>> index 0c90428d349..1dee941a252 100644
>> --- a/gdbsupport/next-iterator.h
>> +++ b/gdbsupport/next-iterator.h
>> @@ -21,27 +21,43 @@
>>
>> #include "gdbsupport/iterator-range.h"
>>
>> -/* An iterator that uses the 'next' field of a type to iterate. This
>> - can be used with various GDB types that are stored as linked
>> - lists. */
>> +/* An iterator base class for iterating over a field of a type. In order to
>> + form a functioning iterator, classes inheriting this should define an
>> + operator++, which determines the actual field that is iterated over.
>> +
>> + Instead of factoring out a base class, we could use something like this:
>> +
>> + template<typename T, auto F = &T::next>
>> + struct next_iterator
>> + {
>> + ...
>> + self_type &operator++ ()
>> + {
>> + m_item = m_item->*F;
>> + return *this;
>> + }
>> + ...
>> + }
>> +
>> + but that has the drawback that it doesn't work with incomplete T. */
>
Hi Simon,
thanks for the review.
> I'm of the opinion that this information is not really relevant here, it
> belongs to the commit message.
>
Ack, I've moved that to the commit message in a v1 (
https://sourceware.org/pipermail/gdb-patches/2026-May/227066.html ).
> I was initially skeptic that the version with the pointer-to-member
> didn't work, so I tried it for myself and indeed, I don't think it would
> work with the block and superblock_iterator relationship (at least, if
> we want to have a method of block returning a
> superblock_iterator/range).
>
That was not what I was getting at. The problem is that the gdb build
breaks with the pointer-to-member approach because we exploit this
incomplete type behavior in the sources.
> While next_iterator is "iterate using the raw field `next`",
> base_next_iterator is more generic just for "iterating over a field of a
> type". It looks like you defined a pretty generic class where the
> derived class only needs to fill in operator++ (and perhaps a bit more
> boilerplate). But operator++ doesn't have to just follow a field, it
> can have any logic in there, as function_block_iterator proves, which is
> nice. Following the raw `next` field (or another raw field of any other
> name) is just one specific case.
>
> Given that all the concrete iterate needs to provide is "given a
> reference to the current element, give me the next element", I think we
> could reduce the boilerplate needed at each site by having the concrete
> iterator provide a functor that does the increment (much like you
> defined an std::map by providing a "hash" functor, not by deriving from
> std::map).
>
Tom Tromey suggested using CRTP, and doing so has I think brought it
closer to what you suggest here.
> Rather than copy paste what I mean here, I pushed what I think it could
> look like to the users/simark/next-iterator branch.
>
> https://sourceware.org/git/?p=binutils-gdb.git;a=shortlog;h=refs/heads/users/simark/next-iterator
> https://sourceware.org/cgit/binutils-gdb/log/?h=users/simark/next-iterator
>
> But just to illustrate here is how the function block iterator and
> range are implemented:
>
> /* Iterator and range type to iterate over this block and its superblocks
> within a function. */
>
> struct function_block_incrementer
> {
> const block *operator() (const block &b) const noexcept
> {
> /* If the current item is the function-defining block, we're done. */
> if (b.function () != nullptr)
> return nullptr;
>
> return b.superblock ();
> }
> };
>
> using function_block_iterator
> = base_next_iterator<const block, function_block_incrementer>;
> using function_block_range = iterator_range<function_block_iterator>;
>
> I kept the name `base_next_iterator`, we could perhaps find a better
> name, since it's not realted to `next_iterator` more than any other
> concrete instance. Perhaps `base_iterator`, or `simple_iterator`?
>
Yeah, agreed. I kept it as is in v1 thought.
>> @@ -61,15 +77,34 @@ struct next_iterator
>> return m_item != other.m_item;
>> }
>>
>> - self_type &operator++ ()
>> +protected:
>> +
>> + T *m_item;
>> +};
>> +
>> +/* An iterator that uses the 'next' field of a type to iterate. This
>> + can be used with various GDB types that are stored as linked
>> + lists. */
>> +
>> +template<typename T>
>> +struct next_iterator : base_next_iterator<T> {
>> + typedef next_iterator self_type;
>> + typedef T *value_type;
>> + typedef T *&reference;
>> + typedef T **pointer;
>
> I think we should use `using` instead of typedef everywhere now.
>
I've added a patch to the series doing this transformation in
next-iterator.h and one other file.
>> +
>> + explicit next_iterator (T *item)
>> + : base_next_iterator<T> (item)
>> {
>> - m_item = m_item->next;
>> - return *this;
>> }
>>
>> -private:
>> + next_iterator () = default;
>
> Just noting that instead of those two constructors, you can use:
>
> using base_next_iterator<T>::base_next_iterator;
Yeah, that's neat, thanks, I've used that in the v1.
Thanks,
- Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 4/7] [gdb] Add block::superblocks
2026-04-30 16:24 ` Simon Marchi
@ 2026-05-01 13:19 ` Tom de Vries
0 siblings, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-05-01 13:19 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
On 4/30/26 6:24 PM, Simon Marchi wrote:
> On 4/23/26 2:35 AM, Tom de Vries wrote:
>> +
>> + self_type &operator++ ()
>> + {
>> + this->m_item = this->m_item->superblock ();
>
> IMO we don't need to use `this->` on members prefixed with `m_`, because
> it's already clear that they are fields of this class. I do use
> `this->` on fields that are not prefixed with `m_` though, otherwise
> it's not clear where it comes from.
I've fixed this.
In next_iterator, there was a similar pattern, but I couldn't just drop
the this prefix. I managed to fix this by adding "using ...::m_item":
...
template<typename T>
struct next_iterator : base_next_iterator<T, next_iterator<T>>
{
using base_next_iterator<T, next_iterator<T>>::base_next_iterator;
using base_next_iterator<T, next_iterator<T>>::m_item;
T *next ()
{
return m_item->next;
}
};
...
Thanks,
- Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/7] [gdbsupport] Factor out base_next_iterator
2026-04-30 19:09 ` Simon Marchi
2026-05-01 12:57 ` Tom Tromey
@ 2026-05-01 13:20 ` Tom de Vries
1 sibling, 0 replies; 26+ messages in thread
From: Tom de Vries @ 2026-05-01 13:20 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
On 4/30/26 9:09 PM, Simon Marchi wrote:
> On 4/30/26 12:24 PM, Simon Marchi wrote:
>> Rather than copy paste what I mean here, I pushed what I think it could
>> look like to the users/simark/next-iterator branch.
>>
>> https://sourceware.org/git/?p=binutils-gdb.git;a=shortlog;h=refs/heads/users/simark/next-iterator
>> https://sourceware.org/cgit/binutils-gdb/log/?h=users/simark/next-iterator
>>
>> But just to illustrate here is how the function block iterator and
>> range are implemented:
>>
>> /* Iterator and range type to iterate over this block and its superblocks
>> within a function. */
>>
>> struct function_block_incrementer
>> {
>> const block *operator() (const block &b) const noexcept
>> {
>> /* If the current item is the function-defining block, we're done. */
>> if (b.function () != nullptr)
>> return nullptr;
>>
>> return b.superblock ();
>> }
>> };
>>
>> using function_block_iterator
>> = base_next_iterator<const block, function_block_incrementer>;
>> using function_block_range = iterator_range<function_block_iterator>;
>
> Just a note: in your version, if you pass a global or static block to
> function_block_iterator, it sets the item to nullptr right away, so you
> iterate on nothing. I couldn't do that in my version, so I chose to
> assert that the block isn't global or static, which puts the
> responsibillity on the caller not to call this on a global or static
> block. The only caller is in ada-lang.c, it passes the result of
> get_frame_block(). I guess that a block from a frame is always in a
> function (is not the global or static block), but if that assumption
> isn't true, then a check would be needed in the caller.
I think letting the iterator sort this out is the better option.
Thanks,
- Tom
^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2026-05-01 13:21 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-23 6:35 [PATCH 0/7] [gdb] Add superblocks range loops Tom de Vries
2026-04-23 6:35 ` [PATCH 1/7] [gdb] Use block::function_block Tom de Vries
2026-04-23 14:33 ` Tom Tromey
2026-04-24 12:19 ` Tom de Vries
2026-04-23 6:35 ` [PATCH 2/7] [gdbsupport] Add parameterless iterator_range constructor Tom de Vries
2026-04-23 14:34 ` Tom Tromey
2026-04-24 12:20 ` Tom de Vries
2026-04-23 6:35 ` [PATCH 3/7] [gdbsupport] Factor out base_next_iterator Tom de Vries
[not found] ` <87340kpbwx.fsf@tromey.com>
2026-04-30 4:49 ` Tom de Vries
2026-05-01 12:54 ` Tom de Vries
2026-05-01 13:00 ` Tom de Vries
2026-04-30 16:24 ` Simon Marchi
2026-04-30 19:09 ` Simon Marchi
2026-05-01 12:57 ` Tom Tromey
2026-05-01 13:20 ` Tom de Vries
2026-05-01 13:15 ` Tom de Vries
2026-04-23 6:35 ` [PATCH 4/7] [gdb] Add block::superblocks Tom de Vries
2026-04-27 11:11 ` Jan Vrany
2026-05-01 13:06 ` Tom de Vries
2026-04-30 16:24 ` Simon Marchi
2026-05-01 13:19 ` Tom de Vries
2026-04-23 6:35 ` [PATCH 5/7] [gdb] Use block::super_blocks Tom de Vries
2026-04-24 16:27 ` Tom Tromey
2026-04-24 21:24 ` Tom de Vries
2026-04-23 6:35 ` [PATCH 6/7] [gdb] Add block::function_blocks Tom de Vries
2026-04-23 6:35 ` [PATCH 7/7] [gdb] Use block::function_blocks Tom de Vries
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox