Clang 14 implements if consteval for mixed evaluation paths
if consteval lets a constexpr function call immediate functions on its constant-evaluation path. Clang 14 also implements alias declarations in statement initializers, correcting a smaller grammar inconsistency.
A constexpr function may need one implementation during constant evaluation and another at runtime. C++20 supplies a query for distinguishing those contexts, but testing that query does not give the selected block all the rules needed to invoke an immediate function with a parameter. Clang 14 implements if consteval, a C++23 construct designed to address that composition problem.
The Clang 14 release notes identify P1938R3 as the implemented proposal. The LLVM release became available on March 23. This is an implementation milestone for a particular language facility, not a claim that Clang or its accompanying standard library implements every part of C++23.
C++ often accumulates facilities whose individual rules look reasonable but whose interactions take more effort to understand. That is part of my reservation about the language. This change interests me because its proposal starts with a concrete failure of composition and defines a direct language mechanism for it.
A branch can establish an immediate context
P1938R3 explains the difference between an immediate invocation and an immediate function context. A consteval function called outside such a context must have an invocation that is itself a constant expression. Within another immediate function, its parameters can participate under the rules for that surrounding evaluation.
An ordinary if using std::is_constant_evaluated() does not establish the required context. A call to a consteval function with a parameter of the surrounding constexpr function can therefore be ill-formed, even when control flow would reach it only during constant evaluation. Knowing which branch runs and defining the language context of that branch are separate matters.
The new statement makes its constant-evaluation block an immediate function context. This example follows the proposal's model and uses the new language facility implemented by Clang 14.
consteval int increment(int value)
{
return value + 1;
}
constexpr int select_value(int value)
{
if consteval {
return increment(value);
} else {
return value;
}
}
static_assert(select_value(7) == 8);
The static assertion evaluates the call as a constant expression, so the constant-evaluation branch invokes increment. An ordinary runtime call uses the other branch. The enclosing function remains constexpr, with both forms of evaluation available. Adding if consteval does not turn every call to it into a mandatory compile-time computation.
The example intentionally gives the paths different results to make selection visible. A production algorithm may instead preserve one result through different implementations, such as an expression suitable for the evaluator and another suitable for runtime machinery. The language facility chooses the context-dependent path without prescribing the algorithm's policy.
if constexpr asks a different question
The proposal also addresses a common misuse of std::is_constant_evaluated(). Placing the query in an if constexpr condition evaluates that condition in a constant-evaluated context. The result therefore does not distinguish a runtime invocation of the surrounding function in the way a reader may expect.
That creates an attractive but incorrect spelling for a familiar intention. The paper discusses compiler diagnostics for the tautological condition, but warnings still require the developer to understand why the apparently natural form is wrong. if consteval expresses the intended context test without placing a library query inside another constant-expression requirement.
The new form has no ordinary parenthesized condition. Its body requires braces, as does an optional else body. P1938R3 explains that braces make the changed context visible. It also documents grammar work needed to avoid surprising association of an else with an outer statement.
Negated forms are provided through if ! consteval or the alternative token not. They execute the first block outside manifest constant evaluation, with the opposite branch corresponding to the constant-evaluation context. This is useful for runtime-only operations without requiring the programmer to invert a library query manually.
The library query remains useful
P1938R3 does not deprecate std::is_constant_evaluated(). Its discussion gives cases that combine the query with another condition, and notes that expression-level uses still need a function-like facility. The new statement solves a particular control-flow and context problem without eliminating every use of the existing query.
That distinction avoids overstating the change. An ordinary query can still answer a question about evaluation where an expression is appropriate. The statement additionally supplies a context in which immediate-function calls can be valid. Replacing all uses mechanically would ignore the different roles the paper preserves.
I prefer the direct spelling for the common branch case because the required context is visible in the source. The benefit is specific. It reduces the need to infer an immediate-function rule from a boolean expression that never established that rule in the first place.
P1938R3 includes a feature-test macro named __cpp_if_consteval. That gives code a named facility to test when it needs to select between implementations for different compiler capabilities. The macro concerns this construct, not the status of every constant-evaluation facility. A project still needs to distinguish support for its chosen language syntax from availability of a particular library operation in a constant expression.
The paper also deliberately omits an additional condition on the statement. A condition needed only within the constant-evaluation path can be expressed as a nested ordinary if. Keeping the context boundary separate avoids asking the compiler to infer it from an arbitrary combined boolean expression.
Alias declarations get the same scope as typedefs
Clang 14 also implements P2360R0, allowing alias declarations in init-statements. Jens Maurer's proposal identifies an inconsistency in the grammar. A statement initializer could already contain a typedef declaration, but the equivalent using alias declaration was excluded.
The paper's motivating example is a range-based loop whose initializer names the element type. Its proposal permits for (using T = int; T e : v) where an equivalent typedef was already allowed. This keeps the alias's scope associated with the statement and permits the preferred declaration spelling without moving the name into a wider block.
No separate feature-test macro is proposed for that addition. Maurer argues that code requiring compatibility can continue to use the typedef form. The feature consequently changes which equivalent declaration spelling the grammar accepts, while if consteval changes the context rules within a selected branch. Clang 14's release notes identify both paper revisions, giving developers precise implementation claims to compare with the source constructs they intend to use.