c++c++-conceptsrequires-clause

Overloading and requires clauses: which overload is chosen?


I'm not sure whether I'm misunderstanding requires or whether clang has a bug. Here's the code:

#include <cassert>
#include <concepts>

template<std::unsigned_integral T>
constexpr unsigned int
foo(T x)
{
    return x;
}

template<std::unsigned_integral T> requires (sizeof(T) == 1)
constexpr unsigned int
foo(T x)
{
    return x + 1;
}

int
main()
{
    assert(foo(static_cast<unsigned char>(0)) == 1);

    unsigned int (*function)(unsigned char x) = foo;
    assert(function(0) == 1);
}

clang -v says

Apple clang version 16.0.0 (clang-1600.0.26.6)
Target: arm64-apple-darwin24.2.0

and compiling and running this as clang -std=c++20 file.cc -o file; file produces

Assertion failed: (function(0) == 1), function main, file file.cc, line 24.
Abort trap: 6

This confuses me: why would the compiler choose different instantiations? In both cases I'd expect a call to unsigned int foo<unsigned char> (unsigned char x) but maybe I'm misunderstanding what should happen.


Solution

  • This is a bug in clang that was fixed.
    I actually managed to reproduce it only on clang 17 (not 16) - as you can try in the live demo below.

    The assertion is not triggered on clang 18 or later (nor in earlier clang versions).

    It is also not triggered on MSVC and gcc.

    I.e. the compiler should indeed choose your second foo in both cases.

    Live demo.