Skip to content

[clang] SFINAE hard error (while the gcc is successful) in some function default argument scenario #160107

@coyorkdow

Description

@coyorkdow

By giving the following codes

#include <string>
#include <type_traits>

template <class Tp, class = decltype(Tp{})>
auto test_default_constructible(int) -> std::true_type;

template <class...> auto test_default_constructible(float) -> std::false_type;

template <class Tp>
constexpr bool is_default_constructible_v =
    decltype(test_default_constructible<Tp>(0))::value;

struct NoDefaultConstructible {
  NoDefaultConstructible(int) {}
};

template <class Tp> struct ABC {
  ABC(std::initializer_list<int>, Tp = Tp{}) {}
};

int main() {
  constexpr auto v = is_default_constructible_v<ABC<NoDefaultConstructible>>;
  static_assert(!v);
}

It's a simple type trait implementaion that using the old fashion SFINAE trick. In gcc it compiles (the value v is false). However, it will cause a hard error when using Clang.

Here is a compiler explorer reproducer: https://godbolt.org/z/84378b3Tx

We can see the same problem if we change to use the modern concept and constraint stuff: https://godbolt.org/z/ve8GE9K8x

#include <type_traits>
#include <string>

namespace test {
template <typename Tp>
constexpr bool
    is_implicitly_default_constructible_v = requires {
        Tp{};
    };
} // namespace test

struct NonDefaultConstructible {
  NonDefaultConstructible(int) {}
};

template <class Tp> class ABC {
public:
  ABC() requires test::is_implicitly_default_constructible_v<Tp> {}
  ABC(std::initializer_list<int>, const Tp& v = Tp()) : obj(v) {}
  Tp obj;
};

int main() {
  constexpr auto v = test::is_implicitly_default_constructible_v<ABC<NonDefaultConstructible>>;
  static_assert(!v);
}

In both two examples, Clang complains an error that there is no matching constructor for Tp in ABC(std::initializer_list<int>, const Tp& v = Tp()). While I think the correct behaviour is fallback to the another overload of test_default_constructible (in the SFINAE example), or the requires expression returns false (in the concept and constraint example).

Why do I think this is a Clang bug? It not just because of the different behaviour than gcc. It is indeed possible to make a hard error during the SFINAE, if we have the wrong syntax, or the error occurs outside the immediate contex. (I am not an expert, forgive me if I'm wrong). However, this case doesn't apply to any of them.

  • If this default argument belongs to the immediate context, then it shouldn't be a hard error. SFINAE will work, and is_implicitly_default_constructible_v is evaulated as false.
  • If this outside the immediate context, then it should choose the first test_default_constructible overload. The type trait will mistakenly return true. It should only lead an error in the following codes (i.e., if we use this type trait to choose the proper code branch, then the wrong type trait result will lead a wrong code branch). The type trait itself shoule compile (although its result is wrong).

Futhermore, We have a lot of codes that similar to my examples in the libstdc++. For example, in std::basic_string, we have (in https://gcc.gnu.org/onlinedocs/gcc-15.1.0/libstdc++/api/a00650_source.html line:798)

basic_string(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())

And we also have the similar type traits in the libstdc++, https://gcc.gnu.org/onlinedocs/gcc-15.1.0/libstdc++/api/a00248_source.html line:1362

  template<typename _Tp>
    constexpr bool __is_implicitly_default_constructible_v
      = requires (void(&__f)(_Tp)) { __f({}); };
 
  template<typename _Tp>
    struct __is_implicitly_default_constructible
    : __bool_constant<__is_implicitly_default_constructible_v<_Tp>>
    { };

When we have some std::basic_string or other containers with a custom allocator that doesn't have a default constructor. Then it's no doubt some promblems will occur. And this issue would be a minimal reproducer (Actually we encountered the promblem already, that's why I create this issue).

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:frontendLanguage frontend issues, e.g. anything involving "Sema"diverges-from:gccDoes the clang frontend diverge from gcc on this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions