c++undefined-behaviorconstexpr

Undefined behavior allowed in constexpr -- compiler bug?


My understanding is that:

It seems to follow that something like the following should not compile, and indeed on my compiler it doesn't.

template<int n> struct S { };

template<int a, int b>
S<a * b> f()
{
  return S<a * b>();
}

int main(int, char **)
{
  f<50000, 49999>();
  return 0;
}

However, now I try the following instead:

#include <numeric>

template<int n> struct S { };

template<int a, int b>
S<std::lcm(a, b)> g()
{
  return S<std::lcm(a,b)>();
}

int main(int, char **)
{
  g<50000, 49999>();
  return 0;
}

Each of g++, clang, and MSVC will happily compile this, despite the fact that

The behavior is undefined if |m|, |n|, or the least common multiple of |m| and |n| is not representable as a value of type std::common_type_t<M, N>.

(Source: https://en.cppreference.com/w/cpp/numeric/lcm)

Is this a bug in all three compilers? Or is cppreference wrong about lcm's behavior being undefined if it can't represent the result?


Solution

  • A late reply, but it was a bug in gcc and is now fixed: See https://gcc.gnu.org/PR105844

    Quoting some details:

        When I fixed PR libstdc++/92978 I introduced a regression whereby
        std::lcm(INT_MIN, 1) and std::lcm(50000, 49999) would no longer produce
        errors during constant evaluation. Those calls are undefined, because
        they violate the preconditions that |m| and the result can be
        represented in the return type (which is int in both those cases). The
        regression occurred because __absu<unsigned>(INT_MIN) is well-formed,
        due to the explicit casts to unsigned in that new helper function, and
        the out-of-range multiplication is well-formed, because unsigned
        arithmetic wraps instead of overflowing.