2234. assert() should allow usage in constant expressions

Section: 22.3 [assertions] Status: C++17 Submitter: Daniel Krügler Opened: 2013-01-12 Last modified: 2017-07-30

Priority: 2

View other active issues in [assertions].

View all other issues in [assertions].

View all issues with C++17 status.

Discussion:

It is unclear from the current specification whether assert() expressions can be used in (potential) constant expressions. As an example consider the implementation of a constexpr function:

#include <cassert>

template<class T, unsigned N>
struct array {
  T data[N];
  constexpr const T& operator[](unsigned i) const {
    return assert(i < N), data[i];
  }
};

int main() {
  constexpr array<int, 3> ai = {1, 2, 3};
  constexpr int i = ai[0];
  int j = ai[0];
  // constexpr int k = ai[5];
}

The first question is whether this program is guaranteed well-formed? A second question is whether is would guaranteed to be ill-formed, if we uncomment the last code line in main()?

The wording in 22.3 [assertions] doesn't add anything significant to the C99 wording. From the C99 specification (7.2 p1 and 7.2.1.1 p2) we get already some valuable guarantees:

The current wording does not yet guarantee that assert expressions can be used in constant expressions, but all tested implementations (gcc, MSVC) would already support this use-case. It seems to me that this should be possible without giving assert a special meaning for the core language.

As a related comment it should be added, that there is a core language proposal that intents to relax some current constraints for constexpr functions and literal types. The most interesting one (making void a literal types and allowing for expression-statements) would simplify the motivating example implementation of operator[] to:

constexpr const T& operator[](unsigned i) const {
  assert(i < N);
  return data[i];
};

[2013-03-15 Issues Teleconference]

Moved to Open.

We are still gaining experience with constexpr as a language feature, and there may be work in Evolution that would help address some of these concerns. Defer discussion until we have a group familiar with any evolutionary direction.

[2014-06-08, Daniel comments and suggests wording]

After approval of N3652, void is now a literal type and constexpr functions can contain multiple statements, so this makes the guarantee that assert expressions are per-se constexpr-friendly even more relevant. A possible wording form could be along the lines of:

For every core constant expression e of scalar type that evaluates to true after being contextually converted to bool, the expression assert(e) shall be a prvalue core constant expression of type void.

Richard Smith pointed out some weaknesses of this wording form, for example it would not guarantee to require the following example to work:

constexpr void check(bool b) { assert(b); }

because b is not a core constant expression in this context.

He suggested improvements that lead to the wording form presented below (any defects mine).

[Lenexa 2015-05-05]

MC : ran into this
Z : Is it guaranteed to be an expression?
MC : clarifies that assert runs at runtime, not sure what it does at compile time
STL : c standard guarantees its an expression and not a whole statement, so comma chaining it is ok
HH : Some implementations work as author wants it to
STL : also doing this as constexpr
DK/STL : discussing how this can actually work
HH : GCC 5 also implements it. We have implementor convergence
MC : Wants to do this without giving assert a special meaning
STL : NDEBUG being defined where assert appears is not how assert works. This is bug in wording. Should be "when assert is defined" or something like that. ... is a constant subexpression if NDEBUG is defined at the point where assert is last defined or redefined."
Would like to strike the "either" because ok if both debug or assertion is true. We want inclusive-or here
MC : is redefined needed?
STL : my mental model is its defined once and then redefined
HH : wants to up to P2
Z/STL : discussing how wording takes care of how/when assert is defined/redefefined
STL/WB : discussing whether to move to ready or review. -> Want to move it to ready.
ask for updated wording
p3 -> p2
plan to go to ready after checking wording

[Telecon 2015-06-30]

HH: standardizing existing practice
MC: what about the comment from Lenexa about striking "either"?
HH: all three implementations accept it
MC: update issue to strike "either" and move to Tentatively Ready

Proposed resolution:

This wording is relative to N3936.

Previous resolution [SUPERSEDED]:
  1. Introduce the following new definition to the existing list in 20.3 [definitions]: [Drafting note: If LWG 2296 is accepted before this issue, the accepted wording for the new definition should be used instead — end drafting note]

    constant subexpression [defns.const.subexpr]

    an expression whose evaluation as subexpression of a conditional-expression CE (8.5.16 [expr.cond]) would not prevent CE from being a core constant expression (8.6 [expr.const]).

  2. Insert a new paragraph following 22.3 [assertions] p1 as indicated:

    -?- An expression assert(E) is a constant subexpression (20.3.6 [defns.const.subexpr]), if either

    • NDEBUG is defined at the point where assert(E) appears, or

    • E contextually converted to bool (7 [conv]), is a constant subexpression that evaluates to the value true.

  1. Introduce the following new definition to the existing list in 20.3 [definitions]: [Drafting note: If LWG 2296 is accepted before this issue, the accepted wording for the new definition should be used instead — end drafting note]

    constant subexpression [defns.const.subexpr]

    an expression whose evaluation as subexpression of a conditional-expression CE (8.5.16 [expr.cond]) would not prevent CE from being a core constant expression (8.6 [expr.const]).

  2. Insert a new paragraph following 22.3 [assertions] p1 as indicated:

    -?- An expression assert(E) is a constant subexpression (20.3.6 [defns.const.subexpr]), if

    • NDEBUG is defined at the point where assert(E) appears, or

    • E contextually converted to bool (7 [conv]), is a constant subexpression that evaluates to the value true.