The rule about constexpr variables is [dcl.constexpr]/6,
[...] In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression ([expr.const]). A constexpr variable that is an object, as well as any temporary to which a constexpr reference is bound, shall have constant destruction.
Every constant expression must be a core constant expression, with some additional restrictions that are not relevant here, so I won't get into them ([expr.const]/13). If the initialization of a constexpr variable is not a core constant expression, the program is ill-formed.
The particular rule violated by the second example is [expr.const]/5.9.
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
- [...]
- an lvalue-to-rvalue conversion unless it is applied to
- a non-volatile glvalue that refers to an object that is usable in constant expressions, or
- a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
In the declaration
constexpr int x = incr(k);
the full-expression "initialize an int variable from incr(k)" fails to be a core constant expression because, when it is evaluated, it needs to perform an lvalue-to-rvalue conversion on k in order to get the value to initialize k1 with. The variable k is not usable in constant expressions, nor did its lifetime begin within the full-expression "initialize an int variable from incr(k)".
In the first example, which your compiler accepts, incr(k) is also being evaluated at compile time, but it is not required to be a core constant expression, so there is no problem. It's very confusing at first, but we need to remember that something that is not a core constant expression can be evaluated as part of a "bigger" evaluation that is a core constant expression. In this case, it is the enclosing constexpr variable initialization (that of y) that is required to be a core constant expression—and it is, because it creates the function parameter k (initializing it with the value 4) and then reads from it. To put it another way, if E is "initialize y with foo(4)", then the lifetime of k begins within E, so the read of k can occur without preventing E from being a core constant expression.