c++/reflection: ICE with ns splice scope in ns alias [PR127344]
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_check--master-arm |
success
|
Test passed
|
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
Commit Message
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
This fixes another problem when using namespace splice scopes,
this time in a namespace alias like this:
template<auto ns> void f () {
namespace A = [:ns:];
int i = A::N::x;
}
where we crash in make_typename_type or make_unbound_class_template
because they expect a type but get a NAMESPACE_DECL built for the
A alias with ORIGINAL_NAMESPACE being a SPLICE_EXPR. The decl is
coming from cp_parser_namespace_alias_definition -> do_namespace_alias.
We want a SPLICE_SCOPE which is a type, so build one with
make_splice_scope.
I considered changing cp_parser_namespace_alias_definition to build
the SPLICE_SCOPE there but that brought so many other issues that it
wasn't worth it.
PR c++/127344
gcc/cp/ChangeLog:
* parser.cc (cp_parser_lookup_name): Call make_splice_scope for
dependent_namespace_p. Use the result for make_typename_type
and make_unbound_class_template.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/ns9.C: New test.
---
gcc/cp/parser.cc | 17 ++++++----
gcc/testsuite/g++.dg/reflect/ns9.C | 52 ++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/reflect/ns9.C
base-commit: f4d32f612a3c43e9d0a861b7dae7bb67139c9ab0
Comments
On 9/30/26 6:12 PM, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
OK.
> -- >8 --
> This fixes another problem when using namespace splice scopes,
> this time in a namespace alias like this:
>
> template<auto ns> void f () {
> namespace A = [:ns:];
> int i = A::N::x;
> }
>
> where we crash in make_typename_type or make_unbound_class_template
> because they expect a type but get a NAMESPACE_DECL built for the
> A alias with ORIGINAL_NAMESPACE being a SPLICE_EXPR. The decl is
> coming from cp_parser_namespace_alias_definition -> do_namespace_alias.
>
> We want a SPLICE_SCOPE which is a type, so build one with
> make_splice_scope.
>
> I considered changing cp_parser_namespace_alias_definition to build
> the SPLICE_SCOPE there but that brought so many other issues that it
> wasn't worth it.
>
> PR c++/127344
>
> gcc/cp/ChangeLog:
>
> * parser.cc (cp_parser_lookup_name): Call make_splice_scope for
> dependent_namespace_p. Use the result for make_typename_type
> and make_unbound_class_template.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/reflect/ns9.C: New test.
> ---
> gcc/cp/parser.cc | 17 ++++++----
> gcc/testsuite/g++.dg/reflect/ns9.C | 52 ++++++++++++++++++++++++++++++
> 2 files changed, 63 insertions(+), 6 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/reflect/ns9.C
>
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 373d3358c25..3c84f6e335f 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -35608,15 +35608,21 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
> && dependentish_scope_p (parser->scope))
> || dependent_namespace_p (parser->scope)))
> {
> + tree scope = parser->scope;
> + /* The make_* functions below expect something that's a type. SCOPE
> + is a NAMESPACE_DECL when we are dealing with a dependent namespace
> + alias like "namespace A = [:ns:];". */
> + if (dependent_namespace_p (scope))
> + scope = make_splice_scope (ORIGINAL_NAMESPACE (scope),
> + /*type_p=*/false);
> +
> if (tag_type)
> {
> - tree type;
> -
> /* The resolution to Core Issue 180 says that `struct
> A::B' should be considered a type-name, even if `A'
> is dependent. */
> - type = make_typename_type (parser->scope, name, tag_type,
> - /*complain=*/tf_error);
> + tree type = make_typename_type (scope, name, tag_type,
> + /*complain=*/tf_error);
> if (type != error_mark_node)
> decl = TYPE_NAME (type);
> }
> @@ -35624,8 +35630,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
> && (cp_parser_next_token_ends_template_argument_p (parser)
> || cp_lexer_next_token_is (parser->lexer,
> CPP_CLOSE_PAREN)))
> - decl = make_unbound_class_template (parser->scope,
> - name, NULL_TREE,
> + decl = make_unbound_class_template (scope, name, NULL_TREE,
> /*complain=*/tf_error);
> else
> decl = build_qualified_name (/*type=*/NULL_TREE,
> diff --git a/gcc/testsuite/g++.dg/reflect/ns9.C b/gcc/testsuite/g++.dg/reflect/ns9.C
> new file mode 100644
> index 00000000000..3f2141f6271
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/reflect/ns9.C
> @@ -0,0 +1,52 @@
> +// PR c++/127344
> +// { dg-do compile { target c++26 } }
> +// { dg-additional-options "-freflection" }
> +
> +template<class, class> struct same_type;
> +template<class T> struct same_type<T, T> {};
> +
> +namespace X { struct N { static constexpr int x = 42; }; }
> +
> +template<auto ns>
> +int
> +f ()
> +{
> + namespace A = [:ns:];
> + int i = A::N::x;
> + int j = [:ns:]::N::x;
> + return i + j;
> +}
> +
> +namespace N { template<class> struct X {}; }
> +template<template<class> class> struct S {};
> +
> +template<auto ns>
> +void
> +h ()
> +{
> + namespace A = [:ns:];
> + S<A::template X> s;
> +}
> +
> +namespace Y { int y = 1; void fn (); namespace N { int x = 2; } }
> +
> +template<auto ns>
> +int
> +k ()
> +{
> + namespace A = [:ns:];
> + namespace B = A;
> + auto p = &A::fn;
> + same_type<decltype(p), void (*) ()>();
> + auto q = &[:ns:]::fn;
> + same_type<decltype(q), void (*) ()>();
> + return A::y + B::N::x;
> +}
> +
> +void
> +g ()
> +{
> + f<^^X>();
> + h<^^N>();
> + k<^^Y>();
> +}
>
> base-commit: f4d32f612a3c43e9d0a861b7dae7bb67139c9ab0
@@ -35608,15 +35608,21 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
&& dependentish_scope_p (parser->scope))
|| dependent_namespace_p (parser->scope)))
{
+ tree scope = parser->scope;
+ /* The make_* functions below expect something that's a type. SCOPE
+ is a NAMESPACE_DECL when we are dealing with a dependent namespace
+ alias like "namespace A = [:ns:];". */
+ if (dependent_namespace_p (scope))
+ scope = make_splice_scope (ORIGINAL_NAMESPACE (scope),
+ /*type_p=*/false);
+
if (tag_type)
{
- tree type;
-
/* The resolution to Core Issue 180 says that `struct
A::B' should be considered a type-name, even if `A'
is dependent. */
- type = make_typename_type (parser->scope, name, tag_type,
- /*complain=*/tf_error);
+ tree type = make_typename_type (scope, name, tag_type,
+ /*complain=*/tf_error);
if (type != error_mark_node)
decl = TYPE_NAME (type);
}
@@ -35624,8 +35630,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
&& (cp_parser_next_token_ends_template_argument_p (parser)
|| cp_lexer_next_token_is (parser->lexer,
CPP_CLOSE_PAREN)))
- decl = make_unbound_class_template (parser->scope,
- name, NULL_TREE,
+ decl = make_unbound_class_template (scope, name, NULL_TREE,
/*complain=*/tf_error);
else
decl = build_qualified_name (/*type=*/NULL_TREE,
new file mode 100644
@@ -0,0 +1,52 @@
+// PR c++/127344
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+template<class, class> struct same_type;
+template<class T> struct same_type<T, T> {};
+
+namespace X { struct N { static constexpr int x = 42; }; }
+
+template<auto ns>
+int
+f ()
+{
+ namespace A = [:ns:];
+ int i = A::N::x;
+ int j = [:ns:]::N::x;
+ return i + j;
+}
+
+namespace N { template<class> struct X {}; }
+template<template<class> class> struct S {};
+
+template<auto ns>
+void
+h ()
+{
+ namespace A = [:ns:];
+ S<A::template X> s;
+}
+
+namespace Y { int y = 1; void fn (); namespace N { int x = 2; } }
+
+template<auto ns>
+int
+k ()
+{
+ namespace A = [:ns:];
+ namespace B = A;
+ auto p = &A::fn;
+ same_type<decltype(p), void (*) ()>();
+ auto q = &[:ns:]::fn;
+ same_type<decltype(q), void (*) ()>();
+ return A::y + B::N::x;
+}
+
+void
+g ()
+{
+ f<^^X>();
+ h<^^N>();
+ k<^^Y>();
+}