In the testcase below, we pretty print the nested type A<int>::B as A<int>::B<int> because we don't check that B is itself a class template before printing the innermost set of template arguments from B's TEMPLATE_INFO (which in this case belongs to A). This patch fixes this by checking PRIMARY_TEMPLATE_P beforehand.
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK to commit to trunk and perhaps to the 10 branch? gcc/ChangeLog: PR c++/95303 * cxx-pretty-print.c (pp_cxx_unqualified_id): Check PRIMARY_TEMPLATE_P before printing the innermost template arguments. gcc/testsuite/ChangeLog: PR c++/95303 * g++.dg/concepts/diagnostic14.C: New test. --- gcc/cp/cxx-pretty-print.c | 13 +++---- gcc/testsuite/g++.dg/concepts/diagnostic14.C | 36 ++++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic14.C diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index 188462a79e7..263f225a492 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -173,12 +173,13 @@ pp_cxx_unqualified_id (cxx_pretty_printer *pp, tree t) case UNBOUND_CLASS_TEMPLATE: pp_cxx_unqualified_id (pp, TYPE_NAME (t)); if (tree ti = TYPE_TEMPLATE_INFO_MAYBE_ALIAS (t)) - { - pp_cxx_begin_template_argument_list (pp); - tree args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti)); - pp_cxx_template_argument_list (pp, args); - pp_cxx_end_template_argument_list (pp); - } + if (PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti))) + { + pp_cxx_begin_template_argument_list (pp); + tree args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti)); + pp_cxx_template_argument_list (pp, args); + pp_cxx_end_template_argument_list (pp); + } break; case BIT_NOT_EXPR: diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic14.C b/gcc/testsuite/g++.dg/concepts/diagnostic14.C new file mode 100644 index 00000000000..ec2b68c4a3c --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/diagnostic14.C @@ -0,0 +1,36 @@ +// PR c++/95303 +// { dg-do compile { target c++20 } } + +template<class> +struct A { + struct B {}; +}; + +template<class T> + requires __is_same(T, char) +struct A<T> { + struct B {}; +}; + +template<> + struct A<bool> { + struct B {}; + }; + +template<class T> +concept C = requires (T&& t) { // { dg-message "\\\[with T = A<int>::B\\\]" } + t.a; +}; +static_assert(C<A<int>::B>); // { dg-error "failed" } + +template<class T> +concept D = requires (T&& t) { // { dg-message "\\\[with T = A<char>::B\\\]" } + t.a; +}; +static_assert(D<A<char>::B>); // { dg-error "failed" } + +template<class T> +concept E = requires (T&& t) { // { dg-message "\\\[with T = A<bool>::B\\\]" } + t.a; +}; +static_assert(E<A<bool>::B>); // { dg-error "failed" } -- 2.27.0.203.gf402ea6816