blob: 09941600defa955d06641c980f354686b7cf210d [file] [edit]
// RUN: %clang_cc1 -emit-llvm -fms-extensions %s -triple=x86_64-pc-windows-msvc -o - \
// RUN: | FileCheck %s --implicit-check-not='define{{.*}}"?__global_array_delete@@YAXPEAX_K@Z"'
// A `::delete` that does not run a non-trivial destructor must NOT cause a
// __global_delete forwarding body to be emitted, even when the TU also
// contains a class whose vector deleting destructor routes its global-delete
// path through __global_delete.
//
// This matches MSVC (validated against cl.exe 19.44): MSVC only engages the
// __global_delete machinery when a deleting destructor is involved, i.e. for a
// `::delete` on a class type with a non-trivial destructor. A `::delete` on a
// primitive or on a trivially-destructible class is lowered as a plain direct
// operator delete, so MSVC leaves __global_delete as a weak external that falls
// back to the trapping __empty_global_delete. Clang models this with a weak
// alias to __empty_global_delete. (The destructor's virtualness and whether the
// class has its own operator delete are irrelevant to this trigger.) Emitting a
// forwarding body here would add a hard reference to the global operator delete
// and could reintroduce LNK2001 in environments without one.
struct Base {
void *operator new[](__SIZE_TYPE__);
void operator delete[](void *);
virtual ~Base();
};
struct Derived : Base {
virtual ~Derived();
};
Base::~Base() {}
Derived::~Derived() {}
// Forces emission of Derived's vector deleting destructor, which creates the
// __global_delete wrapper (as a declaration) for its global-delete path.
void makeVDD() {
Base *p = new Derived[2];
delete[] p;
}
// Explicit global-scope delete on a primitive: no destructor involved, so it
// must not trigger forwarding-body emission.
void scopePrimitive(int *q) {
::delete q;
}
// Explicit global-scope delete on a trivially-destructible class: still no
// (non-trivial) destructor to run, so it must not trigger it either.
struct Trivial {
int x;
};
void scopeTrivial(Trivial *q) {
::delete q;
}
// The __global_array_delete wrapper defaults to a weak alias to the trapping
// fallback (no forwarding body of its own is emitted, asserted via the
// --implicit-check-not on the RUN line). The alias is emitted before the
// function definitions.
// CHECK: @"?__global_array_delete@@YAXPEAX_K@Z" = weak alias void (ptr, i64), ptr @"?__empty_global_delete@@YAXPEAX_K@Z"
// The vector deleting destructor still routes its global array-delete path
// through the __global_array_delete wrapper.
// CHECK: call void @"?__global_array_delete@@YAXPEAX_K@Z"
// __empty_global_delete is emitted as the trapping fallback (shared by the
// scalar and array wrappers of this signature).
// 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