constexpr virtual inheritance

This paper proposes allowing usage of virtual inheritance. This will allow in future constexpr-ification of std::ios_base, and streams, removing final limitation for making most of stream formatting constexpr.

This will also remove definition of constexpr-suitable as it will be meaningless and clean all references across draft of the standard (if this paper land after P3367: constexpr coroutines).

Changes

Motivation

Last language syntax thing which is disallowed in [dcl.constexpr] which blocks us from making std::stringstream constant evaluatable, which blocks us from making exception types for <chrono> (chrono::nonexistent_local_time and chrono::ambiguous_local_time). Stream formatting <chrono>. Using basic_istream_view in range code for compile-time parsing.

When we remove this limitations, language will only have limit on evaluation properties of code in [expr.const], and not syntactical (with exception of reinterpret_cast, but this is also defined as not constant evaluatable.)

Then keyword constexpr can be seen only as an opt-in into constant evaluatable and can be later removed and replaced with an opt-out attribute (this is not propesed here.)

Example of allowed code

struct Superbase {
	string id{"name"};
};

struct Common: Superbase {
	unsigned counter{0};
};

struct Left: virtual Common {
	unsigned value{0};
	constexpr const unsigned & get_counter() const {
		return Common::counter;
	}
};

struct Right: virtual Common {
	unsigned value{0};
	constexpr const unsigned & get_counter() const {
		return Common::counter;
	}
};

struct Child: Left, Right {
	unsigned x{0};
	unsigned y{0};
	// ...
};

constexpr auto ch = Child{}; // before: not allowed to even construct
                             // after: works as expected

static_assert(&ch.Left::get_counter() == &ch.Right::get_counter());

Hierarchy

counter value value y x name Child Right Left Common Superbase

Clang's representation

instance of Child bases virtualbases members members members members bases members subobject Left subobject Right subobject Common subobject Superbase x value value y counter name

Implementation experience

In progress in clang's fork. Currently structures are represented as APValue object with type field struct and pointer to an array of APValue-s containing n APValue-s representing base types and k APValue representing each member subobject.

Lookup for outermost object is already implemented in clang in order to implement virtual function calls. So only changes needed are:

  • Allow functions to be constexpr if they are within type with virtual bases (in SemaDeclCXX.cpp, note: clang currently disallows not just constructor/destructor but all member functions to be constexpr)
  • Allow default constructors for types with virtual bases (in SemaType.cpp)
  • Allow computing dynamic type for objects (in ExprConstant.cpp)
  • Extend APValue to contain number of virtual bases (in APValue.h)
  • Make construction of outermost object contain subobject representing virtual bases (in ExprConstant.cpp)
  • Handle destruction of outermost object to properly destroy also virtual bases (in ExprConstant.cpp)
  • Handle zero-initialization of outermost object (in ExprConstant.cpp)

I spoke with other implementors and they don't seem to have any major concern about implementability.

Library impact

Removing last limitation on constexpr-suitable as defined in [dcl.constexpr] will make it a tautology as every function will now be constexpr-suitable hence this paper contains changes to removal of it across wording.

But this term was used somehow badly to make specify what must be constant evaluatable. It's a subject of LWG issue 2833. Library needs to invent wording specifying something like "implementation must make sure this functionality is constant evaluatable by avoiding constructs disallowed in [expr.const]". As constexpr keyword doesn't mean something must be constant-evaluatable, it's just an opt-in into evaluation for some (or none) code-paths.

I still kept removal of all references to constexpr-suitable in this paper for the project editor's convenience but I guess LWG will do some changes there as they will resolve the issue 2833.

Proposed changes to wording

Paper contains two sets of wordings. One is based on current draft and one is based on changes in P3367 constexpr coroutines.

Based on current draft

9.2.6 The constexpr and consteval specifiers [dcl.constexpr]

A constexpr or consteval specifier used in the declaration of a function declares that function to be a constexpr function.
[Note 3: 
A function or constructor declared with the consteval specifier is an immediate function ([expr.const]).
— end note]
A destructor, an allocation function, or a deallocation function shall not be declared with the consteval specifier.
A function is constexpr-suitable if   >

Feature test macro

15.11 Predefined macro names [cpp.predefined]

__cpp_constexpr_virtual_inheritance 2025??L