https://gcc.gnu.org/g:cb0b42290642c021cab5f66075a38b15d5f2772d
commit r17-5519-gcb0b42290642c021cab5f66075a38b15d5f2772d Author: Marek Polacek <[email protected]> Date: Thu Sep 10 17:17:06 2026 -0400 c++/reflection: spliced namespace in using directive [PR126653] Now that r17-4860, r17-4232, and r17-4789 are all in, I can finally fix the PR that prompted all of the above. The problem here is that we are not detecting any of: [namespace.udir]/1: The splice-specifier (if any) shall designate a namespace that is not the global namespace. The nested-name-specifier, namespace-name, and splice-specifier shall not be dependent. Do so, then. And likewise for [namespace.alias]/2. Moreover, using namespace [: ^^:: :]::N; should work but currently doesn't. The problem is that when global_namespace is created with void_type_node, void_type_node is still a NULL_TREE; it only becomes a thing after c_common_nodes_and_builtins. And since global_namespace's type is null, it is wrongly considered dependent, and so the using above does not work as expected. Since this is true for not just :: but also for std::, I've adjusted type_dependent_expression_p instead. PR c++/126653 gcc/cp/ChangeLog: * cp-tree.h (check_namespace_splice): Declare. * parser.cc (cp_parser_namespace_alias_definition): Call check_namespace_splice. (cp_parser_using_directive): Likewise. Give an error if the nested-name-specifier, namespace-name, or splice-specifier is dependent. * pt.cc (type_dependent_expression_p): Return false for NAMESPACE_DECL. * reflect.cc (check_namespace_splice): New. gcc/testsuite/ChangeLog: * g++.dg/reflect/ns10.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/cp-tree.h | 1 + gcc/cp/parser.cc | 44 +++++++++++++++++++++++--- gcc/cp/pt.cc | 6 ++++ gcc/cp/reflect.cc | 25 ++++++++++++++- gcc/testsuite/g++.dg/reflect/ns10.C | 61 +++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 5 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 4201eb2d2aba..01df92d7e095 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -9560,6 +9560,7 @@ extern bool dependent_splice_p (const_tree) ATTRIBUTE_PURE; extern tree reflection_mangle_prefix (tree, char [3]); extern bool reflection_function_template_p (const_tree) ATTRIBUTE_PURE; extern void dump_data_member_spec (pretty_printer *, tree); +extern bool check_namespace_splice (cp_expr); /* Inline bodies. */ diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 3b3adc0f09a7..b30d3e7f6755 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -24731,7 +24731,15 @@ cp_parser_namespace_alias_definition (cp_parser* parser) } cp_parser_require (parser, CPP_EQ, RT_EQ); if (cp_parser_nth_token_starts_splice_without_nns_p (parser, 1)) - namespace_specifier = cp_parser_splice_specifier (parser); + { + cp_expr splice = cp_parser_splice_specifier (parser); + /* [namespace.alias]/2: The splice-specifier (if any) shall designate + a namespace that is not the global namespace. */ + if (check_namespace_splice (splice)) + namespace_specifier = splice; + else + namespace_specifier = error_mark_node; + } else /* Look for the qualified-namespace-specifier. */ namespace_specifier = cp_parser_qualified_namespace_specifier (parser); @@ -25219,8 +25227,18 @@ cp_parser_using_directive (cp_parser* parser) cp_parser_require_keyword (parser, RID_USING, RT_USING); /* And the `namespace' keyword. */ cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE); + const location_t loc = cp_lexer_peek_token (parser->lexer)->location; + bool dep_error = false; if (cp_parser_nth_token_starts_splice_without_nns_p (parser, 1)) - namespace_decl = cp_parser_splice_specifier (parser); + { + cp_expr splice = cp_parser_splice_specifier (parser); + /* [namespace.udir]/1: The splice-specifier (if any) shall designate + a namespace that is not the global namespace. */ + if (check_namespace_splice (splice)) + namespace_decl = splice; + else + namespace_decl = error_mark_node; + } else { /* Look for the optional `::' operator. */ @@ -25231,8 +25249,26 @@ cp_parser_using_directive (cp_parser* parser) /*check_dependency_p=*/true, /*type_p=*/false, /*is_declaration=*/true); - /* Get the namespace being used. */ - namespace_decl = cp_parser_namespace_name (parser); + if (parser->scope + && parser->scope != error_mark_node + && (dependent_namespace_p (parser->scope) + || (TYPE_P (parser->scope) && dependent_type_p (parser->scope)))) + { + /* Don't emit the "not a namespace-name" error. */ + cp_parser_identifier (parser); + dep_error = true; + } + else + /* Get the namespace being used. */ + namespace_decl = cp_parser_namespace_name (parser); + } + /* [namespace.udir]/1: The nested-name-specifier, namespace-name, and + splice-specifier shall not be dependent. */ + if (dep_error || dependent_namespace_p (namespace_decl)) + { + error_at (loc, "dependent namespaces cannot appear in a using " + "directive"); + namespace_decl = error_mark_node; } cp_warn_deprecated_use_scopes (namespace_decl); /* And any specified GNU attributes. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 3738b1c477dc..26bc7c21db31 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -30414,6 +30414,12 @@ type_dependent_expression_p (tree expression) || TREE_CODE (expression) == USING_DECL) return true; + /* Most namespaces have void type and so wouldn't be considered + dependent, but some (::, std) have no type. They are still not + dependent, though. */ + if (TREE_CODE (expression) == NAMESPACE_DECL) + return false; + /* A lambda-expression in template context is dependent. dependent_type_p is true for a lambda in the scope of a class or function template, but that doesn't cover all template contexts, like a default template argument. */ diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index 47ec9647de8b..be3f29b9507f 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -9259,7 +9259,7 @@ valid_splice_for_member_access_p (const_tree t, bool decls_only_p/*=true*/) splice is used in foo.[: bar :] or foo->[: bar :] context. TEMPLATE_P is true if the splice-expression was preceded by 'template'. TARGS_P is true if there were template arguments. COMPLAIN_P is true if any errors should - be emitted. Returns true is no problems are found, false otherwise. */ + be emitted. Returns true if no problems are found, false otherwise. */ bool check_splice_expr (location_t loc, location_t start_loc, tree t, @@ -9486,6 +9486,29 @@ dependent_splice_p (const_tree t) && TREE_CODE (TREE_OPERAND (t, 0)) == SPLICE_EXPR)); } +/* Check if SPLICE is a valid namespace splice. Returns true if no problems + are found, false otherwise. */ + +bool +check_namespace_splice (cp_expr splice) +{ + if (TREE_CODE (splice) != NAMESPACE_DECL && !dependent_namespace_p (splice)) + { + auto_diagnostic_group d; + error_at (splice.get_location (), "expected a reflection of " + "a namespace"); + inform_tree_category (splice); + return false; + } + else if (splice == global_namespace) + { + error_at (splice.get_location (), "splice of the global namespace " + "can only appear in a nested-name-specifier"); + return false; + } + return true; +} + /* Annotation index for mangling. */ static GTY(()) int annotation_idx; diff --git a/gcc/testsuite/g++.dg/reflect/ns10.C b/gcc/testsuite/g++.dg/reflect/ns10.C new file mode 100644 index 000000000000..c68d4e97a0c8 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/ns10.C @@ -0,0 +1,61 @@ +// PR c++/126653 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +namespace std { } +struct S { }; +namespace N { int foo; }; + +namespace GS = [: ^^:: :]; // { dg-error "splice of the global namespace" } +using namespace [: ^^GS :]; // { dg-error "declared|expected" } + +using namespace [: ^^int :]; // { dg-error "expected a reflection of a namespace" } +namespace X = [: ^^void :]; // { dg-error "expected a reflection of a namespace" } + +using namespace S::T; // { dg-error ".T. is not a namespace-name" } + +template<auto ns> +void +bad () +{ + using namespace [:ns:]; // { dg-error "dependent namespaces cannot appear in a using directive" } + using namespace ::[:ns:]; // { dg-error "expected" } + using namespace N::[:ns:]; // { dg-error "expected" } + using namespace [:ns:]::N; // { dg-error "dependent namespaces cannot appear in a using directive" } + using namespace [: ^^:: :]; // { dg-error "splice of the global namespace can only appear in a nested-name-specifier" } + using namespace ::; // { dg-error "expected identifier before" } + + namespace A = [:ns:]; + using namespace A::N; // { dg-error "dependent namespaces cannot appear in a using directive" } + using namespace A::N::M; // { dg-error "dependent namespaces cannot appear in a using directive" } +} + +template<typename T> +void +also_bad () +{ + using namespace T::N; // { dg-error "dependent namespaces cannot appear in a using directive" } +} + +int +ok () +{ + using namespace [: ^^:: :]::N; + using namespace [: ^^N :]; + using namespace [: ^^::N :]; + using namespace ::N; + using namespace [: ^^std :]; + namespace B = [: ^^N :]; + using namespace B; + return foo; +} + +template<typename> +void +also_ok () +{ + namespace B = [: ^^N :]; + using namespace B; + using namespace [: ^^N :]; +} +template void also_ok<int> ();
