Problem Description
- In C++, the initialization of class members that are references is mandatory. Omitting the inititialization yields a compile error.
- The default constructor of
__split_buffer_pointer_layoutdoes not initialize__alloc_, which is fine as long as the constructor is never used in cases where the_Allocatortemplate parameter is a reference type. std::vectoruses a reference type for_Allocator, which is fine because it does not call the default constructor of__split_buffer_pointer_layout.- When using the swift compiler’s C++ interoperability feature to import C++ symbols into swift, the compiler first produces a Swift module to bridge all exported symbols from C++. When one of the exported symbols uses a type that is a class template instance, the swift bridging module generation pass needs to instantiate all of the template instance’s implicitly instantiatable methods upfront because it is unable to predict which methods will be called from swift. This eager instantiation may cause compile errors if it attempts to instantiate a method that is dependent on type traits that are not available with the current template parameter values.
- When exporting a variable of type
std::vector<T>or a function or method that has a return type or argument of typestd::vector<T>, the swift compiler attempts to eagerly instantiate all the public implicitly instantiatable methods ofstd::vector<T>and the templates that it depends on, including the default constructor of__split_buffer_pointer_layout, just in case they’re needed for calling form swift. This cause a compile error. - This is never a problem in pure C++ builds because the default constructor of
__split_buffer_pointer_layoutis never used in cases where required type traits are missing. Also, explicit class template instantiations in C++ does not trigger this problem because the default constructor is annotated with the__exclude_from_explicit_instantiation__attribute.
Proposed solution
Adding the requires(!is_reference(allocator_type)) constraint to the default constructor of __split_buffer_pointer_layout. This excludes the constructor from the list of public methods that can be implicilty instantiated in cases where its instantiation would generate a compile error. Therefore, the swift compiler will no longer attempt to instantiate the problematic constructor eagerly when the constraint disallows it.
Open Issues
- Does/should libc++ have the mandate (or accept fixes) to ensure compatibility with the swift compiler’s C++ interoperability feature?
- How should this be tested? I tried writing a libc++ unit test for this, but I have not found a way to observe the effect of this requires constraint in C++. The only ways I have found to test this change involve using the swift compiler. My current proposal is to have this tested downstream (in swift-project) and to add a comment in the libc++ header indicating that the constraint is necessary for swift compiler compatibility, so that it won’t be removed or changed without understanding the risks and implications. Any better ideas?