mosra / corrade

C++11 multiplatform utility library
https://magnum.graphics/corrade/
Other
486 stars 107 forks source link

Corrade with BUILD_TESTS=ON compilation error: call to non-‘constexpr’ function #136

Closed cmey closed 2 years ago

cmey commented 2 years ago

Hi!

cmake -DBUILD_TESTS=ON ../
cmake --build .

fails with:

/home/cmey/Code/cpp/wonderland_repos/corrade/src/Corrade/Utility/Assert.h:242:6: error: call to non-‘constexpr’ function ‘Corrade::Utility::Test::{anonymous}::divide(int, int)::<lambda()>’
  239 |     static_cast<void>((condition) ? 0 : ([&]() {                            \
      |                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  240 |         Corrade::Utility::Error{Corrade::Utility::Error::defaultOutput()} << message; \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  241 |         std::abort();                                                       \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  242 |     }(), 0))
      |     ~^~

/home/cmey/Code/cpp/wonderland_repos/corrade/src/Corrade/Utility/Assert.h:239:42: note: ‘Corrade::Utility::Test::{anonymous}::divide(int, int)::<lambda()>’ is not usable as a ‘constexpr’ function because:
  239 |     static_cast<void>((condition) ? 0 : ([&]() {                            \

/home/cmey/Code/cpp/wonderland_repos/corrade/src/Corrade/Utility/Assert.h:239:42: note: lambdas are implicitly ‘constexpr’ only in C++17 and later
  239 |     static_cast<void>((condition) ? 0 : ([&]() {                            \

This is with g++ version: g++ (Ubuntu 11.2.0-19ubuntu1) 11.2.0 on Corrade branch master git # ba75f71, no local change.

Note: the build finishes correctly without BUILD_TESTS=ON.

cmey commented 2 years ago

And with clang++:

Ubuntu clang version 14.0.0-1ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix

another constexpr-related error:

[ 63%] Building CXX object src/Corrade/Utility/Test/CMakeFiles/UtilityDebugAssertDisabledTest.dir/AssertDisabledTest.cpp.o
/home/cmey/Code/cpp/wonderland_repos/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:136:23: error: constexpr variable 'three' must be initialized by a constant expression
        constexpr int three = divide(15, 0);
                      ^       ~~~~~~~~~~~~~
/home/cmey/Code/cpp/wonderland_repos/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:123:12: note: non-literal type '(lambda at /home/cmey/Code/cpp/wonderland_repos/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:123:12)' cannot be used in a constant expression
    return TESTED_CONSTEXPR_ASSERT(b, "b can't be zero"), a/(b + 5);
mosra commented 2 years ago

Heh... unpossible. This has worked for ages on all compilers back to Clang 3.8, GCC 4.8 and MSVC 2015.

Both the GCC and the Clang error is about the same thing, a lambda that gets called if an expression fails. Which is a valid technique explained in this post from 2017. I don't know where the GCC error originates from, but I suspect it would be from AssertDisabledTest.cpp as well, since that's where this macro is mainly tested?

Like with #135, I have a suspicion there's something seriously broken with the toolchain you use :sweat_smile:

cmey commented 2 years ago

The compilation error is reproducible in a clean Docker environment (docker run -it ubuntu:22.04) using clang-11, clang-12, clang-13, clang-14, and gcc-11 (haven't tried more) and also reproducible in archlinux docker image (docker run -it archlinux) using gcc:

[root@0e7cc222fbdc build]# cmake -DBUILD_TESTS=ON ../
-- The CXX compiler identification is GNU 12.1.0

[root@0e7cc222fbdc build]# cmake --build . --parallel
In file included from /tmp/corrade/src/Corrade/Containers/Pointer.h:37,
                 from /tmp/corrade/src/Corrade/TestSuite/Tester.h:35,
                 from /tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:35:
/tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp: In member function ‘void Corrade::Utility::Test::{anonymous}::AssertDisabledTest::constexprTest()’:
/tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:136:37:   in ‘constexpr’ expansion of ‘Corrade::Utility::Test::{anonymous}::divide(15, 0)’
/tmp/corrade/src/Corrade/Utility/Assert.h:242:6: error: call to non-‘constexpr’ function ‘Corrade::Utility::Test::{anonymous}::divide(int, int)::<lambda()>’
  239 |     static_cast<void>((condition) ? 0 : ([&]() {                            \
      |                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  240 |         Corrade::Utility::Error{Corrade::Utility::Error::defaultOutput()} << message; \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  241 |         std::abort();                                                       \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  242 |     }(), 0))
      |     ~^~
/tmp/corrade/src/Corrade/Utility/DebugAssert.h:86:5: note: in expansion of macro ‘CORRADE_CONSTEXPR_ASSERT’
   86 |     CORRADE_CONSTEXPR_ASSERT(condition, message)
      |     ^~~~~~~~~~~~~~~~~~~~~~~~
/tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:41:33: note: in expansion of macro ‘CORRADE_CONSTEXPR_DEBUG_ASSERT’
   41 | #define TESTED_CONSTEXPR_ASSERT CORRADE_CONSTEXPR_DEBUG_ASSERT
      |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:123:12: note: in expansion of macro ‘TESTED_CONSTEXPR_ASSERT’
  123 |     return TESTED_CONSTEXPR_ASSERT(b, "b can't be zero"), a/(b + 5);
      |            ^~~~~~~~~~~~~~~~~~~~~~~
/tmp/corrade/src/Corrade/Utility/Assert.h:239:42: note: ‘Corrade::Utility::Test::{anonymous}::divide(int, int)::<lambda()>’ is not usable as a ‘constexpr’ function because:
  239 |     static_cast<void>((condition) ? 0 : ([&]() {                            \
      |                                          ^
/tmp/corrade/src/Corrade/Utility/DebugAssert.h:86:5: note: in expansion of macro ‘CORRADE_CONSTEXPR_ASSERT’
   86 |     CORRADE_CONSTEXPR_ASSERT(condition, message)
      |     ^~~~~~~~~~~~~~~~~~~~~~~~
/tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:41:33: note: in expansion of macro ‘CORRADE_CONSTEXPR_DEBUG_ASSERT’
   41 | #define TESTED_CONSTEXPR_ASSERT CORRADE_CONSTEXPR_DEBUG_ASSERT
      |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:123:12: note: in expansion of macro ‘TESTED_CONSTEXPR_ASSERT’
  123 |     return TESTED_CONSTEXPR_ASSERT(b, "b can't be zero"), a/(b + 5);
      |            ^~~~~~~~~~~~~~~~~~~~~~~
/tmp/corrade/src/Corrade/Utility/Assert.h:239:42: note: lambdas are implicitly ‘constexpr’ only in C++17 and later
  239 |     static_cast<void>((condition) ? 0 : ([&]() {                            \
      |                                          ^
/tmp/corrade/src/Corrade/Utility/DebugAssert.h:86:5: note: in expansion of macro ‘CORRADE_CONSTEXPR_ASSERT’
   86 |     CORRADE_CONSTEXPR_ASSERT(condition, message)
      |     ^~~~~~~~~~~~~~~~~~~~~~~~
/tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:41:33: note: in expansion of macro ‘CORRADE_CONSTEXPR_DEBUG_ASSERT’
   41 | #define TESTED_CONSTEXPR_ASSERT CORRADE_CONSTEXPR_DEBUG_ASSERT
      |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/corrade/src/Corrade/Utility/Test/AssertDisabledTest.cpp:123:12: note: in expansion of macro ‘TESTED_CONSTEXPR_ASSERT’
  123 |     return TESTED_CONSTEXPR_ASSERT(b, "b can't be zero"), a/(b + 5);
      |            ^~~~~~~~~~~~~~~~~~~~~~~
mosra commented 2 years ago

Wait, I'm stupid. Sorry, realized the actual reason just now.

Yes, it fails even for me. The key was that CMAKE_BUILD_TYPE is unset by default, and ... tests for the CORRADE_DEBUG_ASSERT() macro don't handle that case properly, expecting either NDEBUG or CORRADE_IS_DEBUG_BUILD being set, and there's neither if CMAKE_BUILD_TYPE is empty. The AssertDisabledTest then passes a failing statement to the assert macro to test it's indeed not doing anything, which in turn means that a constexpr variable is not initialized by a constexpr statement (as that's how the assert failure manifests at compile time).

TL;DR: if you build with -DCMAKE_BUILD_TYPE=Release or Debug, it'll go through. Working on a proper fix now, apologies for assuming it's your toolchain being cursed and not my code being stupid.

mosra commented 2 years ago

Should be fixed with 1fea951c12249be498b18d7430bc1132c94d5a08, sorry again :)