| // RUN: %clang_cc1 -emit-llvm -fms-extensions %s -triple=x86_64-pc-windows-msvc -o - | FileCheck %s | |
| // Verify that a plain delete (no `::`) does NOT trigger __global_delete | |
| // forwarding body emission, but the VDD still uses the __global_delete wrapper. | |
| // This matches MSVC (validated against cl.exe): a plain delete that resolves to | |
| // a global operator delete never emits a forwarding body; only an explicit | |
| // `::delete` on a class type does. | |
| struct Base { | |
| void* operator new(__SIZE_TYPE__); | |
| void operator delete(void*); | |
| void operator delete[](void*); | |
| virtual ~Base(); | |
| }; | |
| struct Derived : Base { | |
| virtual ~Derived(); | |
| }; | |
| Base::~Base() {} | |
| Derived::~Derived() {} | |
| // new[] forces VDD emission; regular delete[], not ::delete[]. | |
| void test() { | |
| Base *p = new Derived[2]; | |
| delete[] p; | |
| } | |
| // Each wrapper defaults to a weak alias to the trapping __empty_global_delete | |
| // (matching MSVC's weak-external-with-default; this also works under Arm64EC, | |
| // unlike an /alternatename directive). These aliases are emitted before the | |
| // function definitions. | |
| // CHECK-DAG: @"?__global_delete@@YAXPEAX_K@Z" = weak alias void (ptr, i64), ptr @"?__empty_global_delete@@YAXPEAX_K@Z" | |
| // CHECK-DAG: @"?__global_array_delete@@YAXPEAX_K@Z" = weak alias void (ptr, i64), ptr @"?__empty_global_delete@@YAXPEAX_K@Z" | |
| // The VDD dispatches between class and global delete: the array path uses the | |
| // __global_array_delete wrapper, the scalar path uses __global_delete. | |
| // CHECK-LABEL: define weak dso_local noundef ptr @"??_EDerived@@UEAAPEAXI@Z" | |
| // CHECK: dtor.call_glob_delete_after_array_destroy: | |
| // CHECK: call void @"?__global_array_delete@@YAXPEAX_K@Z"(ptr noundef %{{.*}}, i64 noundef %{{.*}}) | |
| // CHECK: dtor.call_glob_delete: | |
| // CHECK-NEXT: call void @"?__global_delete@@YAXPEAX_K@Z"(ptr noundef %{{.*}}, i64 noundef 8) | |
| // CHECK: dtor.call_class_delete: | |
| // CHECK-NEXT: call void @"??3Base@@SAXPEAX@Z"(ptr noundef %{{.*}}) | |
| // __empty_global_delete should be emitted with a trap. | |
| // 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 | |
| // Neither wrapper should have a forwarding body (no `::delete` on a class | |
| // type in this TU, and no dllexport class). | |
| // CHECK-NOT: define {{.*}}void @"?__global_delete@@YAXPEAX_K@Z" | |
| // CHECK-NOT: define {{.*}}void @"?__global_array_delete@@YAXPEAX_K@Z" |