Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk and perhaps 13?
-- >8 -- Here we're incorrectly rejecting the first type-requirement at parse time with concepts-requires35.C:14:56: error: ‘typename A<T>::B’ is not a template [-fpermissive] We also incorrectly reject the second type-requirement at satisfaction time with concepts-requires35.C:17:34: error: ‘typename A<int>::B’ names ‘template<class U> struct A<int>::B’, which is not a type and similarly for the third type-requirement. This seems to happen only within a type-requirement; if we instead use e.g. an alias template then it works as expected. The difference ultimately seems to be that during parsing of a using-declaration, we pass check_dependency_p=true to cp_parser_nested_name_specifier_opt whereas during parsing of a type-requirement we pass check_dependency_p=false. Passing =false causes cp_parser_template_id for the dependently-scoped template-id B<bool> to return a TYPE_DECL of TYPENAME_TYPE (sometimes with TYPENAME_IS_CLASS_P unexpectedly set for the last two type-requirements) whereas passing =true causes it to return a TEMPLATE_ID_EXPR. The simplest fix therefore seems to be to pass check_dependency_p=true from cp_parser_type_requirement, matching the behavior of cp_parser_elaborated_type_specifier. PR c++/110927 gcc/cp/ChangeLog: * parser.cc (cp_parser_type_requirement): Pass check_dependency_p=true instead of =false. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-requires35.C: New test. --- gcc/cp/parser.cc | 4 +-- .../g++.dg/cpp2a/concepts-requires35.C | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires35.C diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 589ac879c6d..2d27376d988 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -30977,7 +30977,7 @@ cp_parser_type_requirement (cp_parser *parser) cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false); cp_parser_nested_name_specifier_opt (parser, /*typename_keyword_p=*/true, - /*check_dependency_p=*/false, + /*check_dependency_p=*/true, /*type_p=*/true, /*is_declaration=*/false); @@ -30987,7 +30987,7 @@ cp_parser_type_requirement (cp_parser *parser) cp_lexer_consume_token (parser->lexer); type = cp_parser_template_id (parser, /*template_keyword_p=*/true, - /*check_dependency=*/false, + /*check_dependency_p=*/true, /*tag_type=*/none_type, /*is_declaration=*/false); type = make_typename_type (parser->scope, type, typename_type, diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires35.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires35.C new file mode 100644 index 00000000000..1dad931c01a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires35.C @@ -0,0 +1,28 @@ +// PR c++/110927 +// { dg-do compile { target c++20 } } + +template<class T> +struct A { + template<class U> struct B { using type = B; }; + + template<class U> using type = U; +}; + +template<> struct A<void> { }; + +template<class T> +concept C1 = requires { typename A<T>::template B<bool>::type; }; + +template<class T> +concept C2 = requires { typename A<T>::template B<bool>; }; + +template<class T> +concept C3 = requires { typename A<T>::template type<bool>; }; + +static_assert(C1<int>); +static_assert(C2<int>); +static_assert(C3<int>); + +static_assert(!C1<void>); +static_assert(!C2<void>); +static_assert(!C3<void>); -- 2.42.0.rc0.25.ga82fb66fed