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). 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); + } + 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 } -- 2.44.0