On 12/20/24 2:37 PM, Simon Martin wrote:
We currently fail due to "infinite recursion" on the following invalid
code with -std=c++20 and above
=== cut here ===
template <class T> struct S { struct U { const S s; } u; };
S t{2};
=== cut here ===
The problem is that reshape_init_class for S calls reshape_init_r for
its field S::u, that calls reshape_init_class for S::U, that calls
reshape_init_r for field S::U::s that calls reshape_init_class for its
type S, etc.
This patch fixes the issue by erroring out in reshape_init_class if we
detect that we're about to call reshape_init_r for a type that's part of
our "TYPE_CONTEXT chain".
An alternative was to change the check in grokdeclarator that rejects
fields with an incomplete type to check for the field type's
TYPE_BEING_DECLARED (which sounds like the right thing to do), however
erroring for the definition of U::s breaks a bunch of existing test
cases, and it would also make us diverge from clang and MSVC (but behave
like EDG) - see https://godbolt.org/z/hT8d1GWa3. It feels to me like we
don't want that (I'm happy to revisit if people think it's OK).
We do error for U::s at instantiation time, because we need to
instantiate U in order to instantiate S.
Erroring at template parse time is a bit tricky; we can't error
immediately on the declaration of U::s, what makes it a problem is the
later declaration of S::u. Without that we could instantiate U later,
after S is already complete.
Successfully tested on x86_64-pc-linux-gnu.
PR c++/118078
gcc/cp/ChangeLog:
* decl.cc (reshape_init_class): Don't trigger infinite recursion
for invalid "recursive types".
gcc/testsuite/ChangeLog:
* g++.dg/cpp1z/class-deduction118.C: New test.
* g++.dg/cpp2a/class-deduction-aggr16.C: New test.
---
gcc/cp/decl.cc | 21 ++++++++++++++++---
.../g++.dg/cpp1z/class-deduction118.C | 8 +++++++
.../g++.dg/cpp2a/class-deduction-aggr16.C | 7 +++++++
3 files changed, 33 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction118.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr16.C
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 42e83f880f9..ea55ea4c0e5 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -7315,9 +7315,24 @@ reshape_init_class (tree type, reshape_iter *d, bool
first_initializer_p,
d->cur++;
}
else
- field_init = reshape_init_r (TREE_TYPE (field), d,
- /*first_initializer_p=*/NULL_TREE,
- complain);
+ {
+ /* Make sure that we won't be calling ourselves recursively, which
+ could happen with "recursive template types" (PR c++/118078). */
+ tree ctx = TYPE_CONTEXT (type);
+ while (ctx && CLASS_TYPE_P (ctx))
+ {
+ if (ctx == TYPE_MAIN_VARIANT (TREE_TYPE (field))) {
+ if (complain & tf_error)
+ error ("field %qD has incomplete type %qT", field,
+ TREE_TYPE (field));
+ return error_mark_node;
+ }
+ ctx = TYPE_CONTEXT (ctx);
+ }
I'm concerned that this will break the situation I mentioned, of
initializing an S::U in the case where S doesn't actually have a member
of type U. That the member has the enclosing class type isn't the
problem; the problem is that combined with the enclosing class having a
member of the nested class type. It's the recursion, not the nesting.
+ field_init = reshape_init_r (TREE_TYPE (field), d,
+ /*first_initializer_p=*/NULL_TREE,
+ complain);
+ }
if (field_init == error_mark_node)
return error_mark_node;
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction118.C
b/gcc/testsuite/g++.dg/cpp1z/class-deduction118.C
new file mode 100644
index 00000000000..b14d62df793
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction118.C
@@ -0,0 +1,8 @@
+// PR c++/118078
+// { dg-do "compile" { target c++11 } }
+
+template <class T>
+struct S { struct U { const S s; } u; };
+S t{2}; // { dg-error "invalid use of template-name 'S' without an argument list"
"" { target c++14_down } }
+ // { dg-error "class template argument deduction failed" "" { target
c++17 } .-1 }
+ // { dg-error "no matching function for call to 'S\\\(int\\\)'" "" {
target c++17 } .-2 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr16.C
b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr16.C
new file mode 100644
index 00000000000..feab11927c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr16.C
@@ -0,0 +1,7 @@
+// PR c++/118078
+// { dg-do "compile" { target c++20 } }
+
+template <class T>
+struct S { const struct U { struct V { volatile S s; } v; } u; };
+S t{2}; // { dg-error "class template argument deduction failed" }
+ // { dg-error "no matching function for call to 'S\\\(int\\\)'" "" {
target *-*-* } .-1 }