Bug 110927 - GCC fails to parse dependent type in concept through partial specialization
Summary: GCC fails to parse dependent type in concept through partial specialization
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 14.0
: P3 normal
Target Milestone: 13.3
Assignee: Patrick Palka
URL:
Keywords: rejects-valid
: 109181 (view as bug list)
Depends on:
Blocks:
 
Reported: 2023-08-07 02:44 UTC by danakj
Modified: 2023-08-16 17:17 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-08-07 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description danakj 2023-08-07 02:44:04 UTC
Repro: https://godbolt.org/z/rWbbGzWdb

```
#include <concepts>
#include <functional>

template <class T>
struct Foo;

template <>
struct Foo<int> {
    template <class U>
    using Type = U;
};

struct S {
    template <class U>
    using Type = U;
};

template <class T>
concept C1 = requires { typename Foo<T>::template Type<bool>; };

template <class T>
concept C2 = requires { typename T::template Type<bool>; };

int main() {
    static_assert(C1<int>);  // Fails with `the required type 'typename Foo<T>::Type' is invalid`
    static_assert(C2<S>);  // Passes.
}
```

This is accepted by Clang and MSVC.
Comment 1 Andrew Pinski 2023-08-07 02:53:53 UTC
Confirmed, I suspect PR 109181 is the same (or rather reduced example).
Comment 2 waffl3x 2023-08-08 02:49:53 UTC
(In reply to Andrew Pinski from comment #1)
> Confirmed, I suspect PR 109181 is the same (or rather reduced example).

Yes, I believe the case danakj wrote here is very close to my original use case when I first submitted the bug. Since I never got around to recreating it and posting it I'm thankful this report was made, I had mostly forgotten about it. I found 2 workarounds so I ended up moving on.

I suspect that the bug isn't related to partial specializations given the reduced case in PR 109181 doesn't have one, however, my original case also involved partial specializations so perhaps I am wrong.

Here are the workarounds I mentioned just in case they are useful to danakj.

The first workaround is more modern, not resorting to older techniques.
```
template<typename T>
concept HasTypeMemberTemplate1 = requires{
    typename std::type_identity<decltype(std::declval<typename my_template<T>::template type<>>())>;
};
```

The second workaround uses older techniques.
```
template<typename T, typename = void>
struct specialization_has_type_member_template
  : std::false_type {};

template<typename T>
struct specialization_has_type_member_template<T, std::void_t<typename my_template<T>::template type<>>>
  : std::true_type {};

template<typename T>
concept HasTypeMemberTemplate2 = has_valid_adapter_specialization<T>::value;
```

You can see both workarounds in action here, hopefully you can get some use out of them.
https://godbolt.org/z/Po1M4qc5P
I can't take credit for the modern workaround, someone named Raldryniorth in a small community came up with it when I had the problem months back, so thanks to him for that.
Comment 3 Patrick Palka 2023-08-09 18:10:27 UTC
*** Bug 109181 has been marked as a duplicate of this bug. ***
Comment 4 GCC Commits 2023-08-11 17:26:38 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:63bd36be990f3b08fcee5b69718ef97c055fbb31

commit r14-3161-g63bd36be990f3b08fcee5b69718ef97c055fbb31
Author: Patrick Palka <ppalka@redhat.com>
Date:   Fri Aug 11 13:26:02 2023 -0400

    c++: dependently scoped template-id in type-req [PR110927]
    
    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-decl,
    we pass check_dependency_p=true to cp_parser_nested_name_specifier_opt
    whereas for 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 create a TYPE_DECL of TYPENAME_TYPE (with
    TYPENAME_IS_CLASS_P unexpectedly set in the last two cases) whereas
    passing =true causes it to return a TEMPLATE_ID_EXPR.  We then call
    make_typename_type on this TYPE_DECL which does the wrong thing.
    
    Since there seems to be no justification for using check_dependency_p=false
    here, the simplest fix seems to be to pass check_dependency_p=true instead,
    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.
Comment 5 GCC Commits 2023-08-16 16:10:31 UTC
The releases/gcc-13 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:b3cfa47d385c004bfbf1772c41e255e8eb60377e

commit r13-7729-gb3cfa47d385c004bfbf1772c41e255e8eb60377e
Author: Patrick Palka <ppalka@redhat.com>
Date:   Fri Aug 11 13:26:02 2023 -0400

    c++: dependently scoped template-id in type-req [PR110927]
    
    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-decl,
    we pass check_dependency_p=true to cp_parser_nested_name_specifier_opt
    whereas for 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 create a TYPE_DECL of TYPENAME_TYPE (with
    TYPENAME_IS_CLASS_P unexpectedly set in the last two cases) whereas
    passing =true causes it to return a TEMPLATE_ID_EXPR.  We then call
    make_typename_type on this TYPE_DECL which does the wrong thing.
    
    Since there seems to be no justification for using check_dependency_p=false
    here, the simplest fix seems to be to pass check_dependency_p=true instead,
    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.
    
    (cherry picked from commit 63bd36be990f3b08fcee5b69718ef97c055fbb31)
Comment 6 Patrick Palka 2023-08-16 17:17:19 UTC
Should be fixed for GCC 13.3, thanks for the report.