Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for STATIC_CHECK to test compile-time/constexpr expressions #60

Closed
cschreib opened this issue Jan 19, 2023 · 2 comments · Fixed by #76
Closed

Add support for STATIC_CHECK to test compile-time/constexpr expressions #60

cschreib opened this issue Jan 19, 2023 · 2 comments · Fixed by #76
Labels
enhancement New feature or request
Milestone

Comments

@cschreib
Copy link
Member

cschreib commented Jan 19, 2023

This has been requested on reddit.

Relevant bit of the discussion copied here:

Having reviewed this some more, Catch2 already defines STATIC_CHECK as just static_assert, and they have a pre-processor value to convert that to a runtime check instead. But they won't ever run both the compile-time and run-time check at the same time. I think it would be confusing to have snitch do both, for this reason.

I am thinking however that we could call it something new and different, like CONSTEXPR_CHECK(expr), which would expand to something equivalent to

CHECK(std::bool_constant<(expr)>::value); // compile-time eval, run-time reporting

and CONSTEXPR_REQUIRE(expr) would be

static_assert(expr); // compile-time eval, compile-time reporting
CHECK(expr); // run-time; for recording success
@cschreib cschreib added this to the v1.x milestone Jan 19, 2023
@cschreib cschreib added the enhancement New feature or request label Jan 19, 2023
@cschreib
Copy link
Member Author

cschreib commented Mar 18, 2023

After thinking about this some more, I was toying with the idea that:

  • CONSTEXPR_CHECK(expr) reports a runtime failure if expr is not constexpr, or if expr evaluates to false, then continues the test
  • CONSTEXPR_REQUIRE(expr) reports a runtime failure if expr is not constexpr, or if expr evaluates to false, then stops the test

So essentially identical to CHECK and REQUIRE, but with an extra (compile-time-computed but runtime-reported) check that the expression is a constant expression. I thought this would make the reporting nicer: rather than depend on the compiler-specific error messages generated when trying to use a non-constant expression in a constexpr context, we could output our own nice message. That would come at the expense of delayed reporting (runtime instead of compile time), which I wasn't sure I liked.

Regardless, it turned out to be impossible. There does not seem to exist a generic way to obtain a boolean that is true if a general expression is constexpr and false otherwise. Some solutions can be found here and there, but they are either broken (using the test suite from this solution):

or too limited (apply to functions only, and/or cannot use constexpr variables in the enclosing scope):

We will probably need to wait for reflection (that must be a meme by now) to be able to do this reliably.

So in the end I'm sticking with the original plan:

  • CONSTEXPR_CHECK(expr) reports a compile-time failure if expr is not constexpr, or a runtime failure if expr evaluates to false, then continues the test
  • CONSTEXPR_REQUIRE(expr) reports a compile-time failure if expr is not constexpr, or a runtime failure if expr evaluates to false, then stops the test

Messages if the expression is not constexpr don't look the greatest but at least they are straight to the point:

GCC 10:
Screenshot from 2023-03-18 13-17-37

Clang 10:
Screenshot from 2023-03-18 13-55-19

MSVC 19.33:

<source>(3601): error C2131: expression did not evaluate to a constant
<source>(3601): note: failure was caused by call of undefined function or one not declared 'constexpr'
<source>(3601): note: see usage of 'test_fun_0::not_fully_constexpr::foo'

And perhaps that is the best way, because the compiler may be able to say exactly what wasn't constexpr in the expression, which snitch definitely can't do.

@cschreib
Copy link
Member Author

I was also considering supporting expressions that evaluate to void in CONSTEXPR_CHECK and CONSTEXPR_REQUIRE. Say you had a constexpr_vector class, you might be tempted to write

CONSTEXPR_CHECK(constexpr_vector{10}.clear());

to test that constexpr_vector::clear() is indeed constexpr, without bothering about the return value of the expression. But this has proven impossible to implement. The expression has type void and this really doesn't play nice with the expression decomposer (can't overload operators with void operands). In principle we could use if constexpr based on the decltype of the expression and select what we want to do, but in practice we're not in a dependent context inside CONSTEXPR_CHECK, i.e., not inside a template, so if constexpr branches that don't compile generate errors even if they are not taken... This issue has been a major pain in implementing snitch, and I don't understand why the standard is written that way.

Anyway, perhaps the best way is to use another check macro CHECK_CONSTEXPR that does just this, since the purpose is different. I'm a bit annoyed at the similarity between CONSTEXPR_CHECK and CHECK_CONSTEXPR though. This is even more confusing because CONSTEXPR_CHECK is actually closer to if constexpr (...), while CHECK_CONSTEXPR is closer to if (constexpr(...)) (assuming we had a constexpr operator like noexcept). I might call this CHECK_IS_CONSTEXPR to avoid this issue, but this deviates from precedents like CHECK_FALSE, which should have been CHECK_IS_FALSE.

Nothing is ever easy! I'll open a ticket for this and decide later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant