blob: e4a254f6d2ae8ea09f5c390f04fbf47d0f27c511 [file] [edit]
// RUN: %clang_cc1 -emit-llvm -fms-extensions %s -triple=x86_64-pc-windows-msvc -o - | FileCheck %s
// A `::delete` on a class whose deleting destructor is NOT defined in this TU
// (only declared, e.g. defined in another TU) must still emit a strong
// __global_delete forwarding body HERE. MSVC emits the forwarder at every
// `::delete` site (validated against cl.exe). Without this, a TU that only
// performs `::delete` (with the vector deleting destructor emitted in a
// different TU) would emit no forwarder, leaving the wrapper bound to the
// trapping __empty_global_delete fallback and crashing at runtime.
struct W {
// Declared, but ~W() is defined in another TU, so no vector deleting
// destructor (and hence no __global_delete wrapper reference) is emitted in
// this TU. The forwarder must come from the ::delete site below.
virtual ~W();
void operator delete(void *);
int x;
};
void sink(W *p) { ::delete p; }
// The shared trapping fallback is emitted (kept alive via llvm.used).
// CHECK: define linkonce_odr void @"?__empty_global_delete@@YAXPEAX_K@Z"(ptr noundef %0, i64 noundef %1)
// CHECK-NEXT: call void @llvm.trap()
// CHECK-NEXT: unreachable
// The scalar wrapper gets a real forwarding body that calls the global
// ::operator delete (??3@), even though no deleting destructor in this TU
// references the wrapper.
// CHECK: define linkonce_odr void @"?__global_delete@@YAXPEAX_K@Z"(ptr noundef %0, i64 noundef %1)
// CHECK-NEXT: call void @"??3@YAXPEAX_K@Z"(ptr %0, i64 %1)
// No vector deleting destructor for W is emitted in this TU.
// CHECK-NOT: define {{.*}}@"??_EW@@