llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.36k stars 12.15k forks source link

Distinct template specializations result in redefinition error #117654

Open davidmatson opened 4 days ago

davidmatson commented 4 days ago
// © Microsoft Corporation. All rights reserved.

#include <type_traits>
#include <utility>

namespace async::details
{
    template <typename T, typename = std::void_t<void, void>>
    struct co_await_operator_or_self final
    {
        using type = T;
    };

    template <typename T>
    struct co_await_operator_or_self<T, std::void_t<decltype(std::declval<T>().operator co_await()), void>> final
    {
        using type = decltype(std::declval<T>().operator co_await());
    };

    template <typename T>
    struct co_await_operator_or_self<T, std::void_t<void, decltype(operator co_await(std::declval<T>()))>> final
    {
        using type = decltype(operator co_await(std::declval<T>()));
    };

    template <typename T>
    using co_await_t = typename co_await_operator_or_self<T>::type;

    template <typename T>
    struct awaitable_resume final
    {
        using type = decltype(std::declval<co_await_t<T>>().await_resume());
    };
}

namespace async
{
    template <typename T>
    using awaitable_resume_t = typename details::awaitable_resume<T>::type;
}

https://godbolt.org/z/KM9xEd6q6

The above code complies on both MSVC and GCC.

The code above is from the cpp-async project (see microsoft/cppasync#20).

Is the above code valid, or are MSVC and GCC mistaken to accept it?

llvmbot commented 4 days ago

@llvm/issue-subscribers-clang-frontend

Author: David Matson (davidmatson)

```c++ // © Microsoft Corporation. All rights reserved. #include <type_traits> #include <utility> namespace async::details { template <typename T, typename = std::void_t<void, void>> struct co_await_operator_or_self final { using type = T; }; template <typename T> struct co_await_operator_or_self<T, std::void_t<decltype(std::declval<T>().operator co_await()), void>> final { using type = decltype(std::declval<T>().operator co_await()); }; template <typename T> struct co_await_operator_or_self<T, std::void_t<void, decltype(operator co_await(std::declval<T>()))>> final { using type = decltype(operator co_await(std::declval<T>())); }; template <typename T> using co_await_t = typename co_await_operator_or_self<T>::type; template <typename T> struct awaitable_resume final { using type = decltype(std::declval<co_await_t<T>>().await_resume()); }; } namespace async { template <typename T> using awaitable_resume_t = typename details::awaitable_resume<T>::type; } ``` https://godbolt.org/z/KM9xEd6q6 The above code complies on both MSVC and GCC. The code above is from the cpp-async project (see [microsoft/cppasync#20](https://github.com/microsoft/cpp-async/issues/20)). Is the above code valid, or are MSVC and GCC mistaken to accept it?
zygoloid commented 3 days ago

CWG1390 seems relevant here. Is std::void_t<anything> a dependent type? Also CWG1797 and its related issues, in which CWG is considering having two different kinds of alias templates depending on whether they're "simple" enough, with "simple" ones eagerly expanded and the rest remaining opaque and dependent until the arguments are known.

But prior to the resolution of those issues, I think Clang is wrong here: void_t<dependent> is a dependent type, even though it's "equivalent to" the non-dependent type void, and so the two declarations are not equivalent.