// Tests that an implicitly-declared std::align_val_t (created by // DeclareGlobalNewDelete when a virtual destructor is seen) merges correctly // with the module's explicit definition of std::align_val_t. // // Without the fix, the implicit align_val_t was not added to the std namespace // DeclContext, so the ASTReader's noload_lookup couldn't find it during module // deserialization. This resulted in two unmerged align_val_t types and a // "no matching function for call to '__builtin_operator_delete'" error when // module code passed the module's align_val_t to the implicitly declared // operator delete (which uses the implicit align_val_t). // // RUN: rm -rf %t // RUN: mkdir -p %t // RUN: split-file %s %t // // Build the module that provides std::align_val_t and a template using // __builtin_operator_delete. Crucially, the module does NOT declare // operator delete -- only the implicit declarations exist. // RUN: %clang_cc1 -std=c++17 -x c++ -fmodules -fno-implicit-modules \ // RUN: -faligned-allocation -fsized-deallocation \ // RUN: -emit-module -fmodule-name=alloc \ // RUN: -fmodule-map-file=%t/alloc.modulemap \ // RUN: %t/alloc.modulemap -o %t/alloc.pcm // // Compile a TU that has a virtual destructor (triggers DeclareGlobalNewDelete // and implicit align_val_t) BEFORE importing the module. // RUN: %clang_cc1 -std=c++17 -x c++ -fmodules -fno-implicit-modules \ // RUN: -faligned-allocation -fsized-deallocation \ // RUN: -fmodule-map-file=%t/alloc.modulemap \ // RUN: -fmodule-file=alloc=%t/alloc.pcm \ // RUN: -fsyntax-only -verify %t/use.cpp //--- alloc.modulemap module alloc { header "alloc.h" } //--- alloc.h #ifndef ALLOC_H #define ALLOC_H namespace std { using size_t = decltype(sizeof(0)); enum class align_val_t : size_t {}; } // No explicit operator delete declarations here -- we rely on the implicit // ones from DeclareGlobalNewDelete. This mirrors the libc++ setup after // the removal of the global_new_delete.h include from allocate.h. template void dealloc(T *p) { // __builtin_operator_delete resolves against the usual (implicit) // deallocation functions. Those use the implicit align_val_t. // The argument here uses the module's align_val_t. // If they're not merged, this fails with a type mismatch. __builtin_operator_delete(p, std::align_val_t(alignof(T))); } #endif //--- use.cpp // expected-no-diagnostics // Virtual destructor triggers DeclareGlobalNewDelete(), which implicitly // creates std::align_val_t before the module is loaded. class Foo { virtual ~Foo() {} }; #include "alloc.h" void test() { dealloc(nullptr); }