Is this failing test that adds zero to a null pointer undefined behaviour, a compiler bug, or something else?

  • A+
Category:Languages

I wrote a lightweight 'string_view' wrapper for a C++14 project, and with MSVC 2017 it is triggering a static_assert at compile-time, yet the same code at run-time is passes the regular assert. My question is, is this a compiler bug, manifest undefined behaviour, or something else entirely?

Here's the distilled code:

#include <cassert> // assert #include <cstddef> // size_t  class String_View {     char const* m_data;     std::size_t m_size; public:     constexpr String_View()       : m_data( nullptr ),         m_size( 0u )     {}      constexpr char const* begin() const noexcept     { return m_data; }     constexpr char const* end() const noexcept     { return m_data + m_size; } };  void static_foo() {     constexpr String_View sv;  //    static_assert( sv.begin() == sv.end() ); // this errors     static_assert( sv.begin() == nullptr ); //    static_assert( sv.end() == nullptr ); // this errors }  void dynamic_foo() {     String_View const sv;      assert( sv.begin() == sv.end() ); // this compiles & is optimized away     assert( sv.begin() == nullptr );     assert( sv.end() == nullptr ); // this compiles & is optimized away } 

Here's a Compiler Explorer link that I used to replicate the problem: https://godbolt.org/z/TpnE8o

From what I can tell, adding or subtracting 0 from any pointer value is always valid:

Workaround:

If I change my end method to the following, the failing static_asserts will pass.

constexpr char const* end() const noexcept { return ( m_data == nullptr            ? m_data            : m_data + m_size ); } 

Tinkering:

I thought maybe the expression m_data + m_size itself is UB, before the fact that m_size == 0 is evaluated. Yet, if I replace the implementation of end with the nonsensical return m_data + 0;, this still generates the two static_assert errors. :-/

 


I think that this is definitely a bug in the way MSVC evaluates constant expressions, since GCC and Clang have no issues with the code, and the standard is clear that adding 0 to a null pointer yields a null pointer ([expr.add]/7).

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: