1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This test case checks specifically the cases under bullet 3.3:
10//
11// C++ ABI 15.3:
12// A handler is a match for an exception object of type E if
13// * The handler is of type cv T or cv T& and E and T are the same type
14// (ignoring the top-level cv-qualifiers), or
15// * the handler is of type cv T or cv T& and T is an unambiguous base
16// class of E, or
17// > * the handler is of type cv1 T* cv2 and E is a pointer type that can <
18// > be converted to the type of the handler by either or both of <
19// > o a standard pointer conversion (4.10 [conv.ptr]) not involving <
20// > conversions to private or protected or ambiguous classes <
21// > o a qualification conversion <
22// * the handler is a pointer or pointer to member type and E is
23// std::nullptr_t
24//
25//===----------------------------------------------------------------------===//
26
27// UNSUPPORTED: no-exceptions
28// This test requires the fix to
29// https://github.com/llvm/llvm-project/issues/64953, which is in libc++abi.dylib.
30// The fix is not contained in older macOS system dylibs, so the test will fail
31// there.
32// FIXME: In the case that we are testing `natively` with the CI scripts we
33// currently pass the newly-built libraries to the execution, this leads to an
34// XPASS here so that we have to make these UNSUPPORTED for now (they should be
35// XFAILs when tested against current [macOS14] and previous installed libc++abi
36// as described above).
37// UNSUPPORTED: stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}{{.*}}
38// UNSUPPORTED: stdlib=apple-libc++ && target={{.+}}-apple-macosx{{11|12|13|14}}{{.*}}
39
40#include <exception>
41#include <stdlib.h>
42#include <assert.h>
43#include <stdio.h>
44
45struct Base {
46 int b;
47};
48struct Base2 {
49 int b;
50};
51struct Derived1 : Base {
52 int b;
53};
54struct Derived2 : Base {
55 int b;
56};
57struct Derived3 : Base2 {
58 int b;
59};
60struct Private : private Base {
61 int b;
62};
63struct Protected : protected Base {
64 int b;
65};
66struct Virtual1 : virtual Base {
67 int b;
68};
69struct Virtual2 : virtual Base {
70 int b;
71};
72
73struct Ambiguous1 : Derived1, Derived2 {
74 int b;
75};
76struct Ambiguous2 : Derived1, Private {
77 int b;
78};
79struct Ambiguous3 : Derived1, Protected {
80 int b;
81};
82
83struct NoPublic1 : Private, Base2 {
84 int b;
85};
86struct NoPublic2 : Protected, Base2 {
87 int b;
88};
89
90struct Catchable1 : Derived3, Derived1 {
91 int b;
92};
93struct Catchable2 : Virtual1, Virtual2 {
94 int b;
95};
96struct Catchable3 : virtual Base, Virtual2 {
97 int b;
98};
99
100// Check that, when we have a null pointer-to-object that we catch a nullptr.
101template <typename T // Handler type
102 ,
103 typename E // Thrown exception type
104 >
105void assert_catches() {
106 try {
107 throw static_cast<E>(0);
108 printf("%s\n", __PRETTY_FUNCTION__);
109 assert(false && "Statements after throw must be unreachable");
110 } catch (T t) {
111 assert(t == nullptr);
112 return;
113 } catch (...) {
114 printf("%s\n", __PRETTY_FUNCTION__);
115 assert(false && "Should not have entered catch-all");
116 }
117
118 printf("%s\n", __PRETTY_FUNCTION__);
119 assert(false && "The catch should have returned");
120}
121
122template <typename T // Handler type
123 ,
124 typename E // Thrown exception type
125 >
126void assert_cannot_catch() {
127 try {
128 throw static_cast<E>(0);
129 printf("%s\n", __PRETTY_FUNCTION__);
130 assert(false && "Statements after throw must be unreachable");
131 } catch (T t) {
132 printf("%s\n", __PRETTY_FUNCTION__);
133 assert(false && "Should not have entered the catch");
134 } catch (...) {
135 assert(true);
136 return;
137 }
138
139 printf("%s\n", __PRETTY_FUNCTION__);
140 assert(false && "The catch-all should have returned");
141}
142
143// Check that when we have a pointer-to-actual-object we, in fact, get the
144// adjusted pointer to the base class.
145template <typename T // Handler type
146 ,
147 typename O // Object type
148 >
149void assert_catches_bp() {
150 O* o = new (O);
151 try {
152 throw o;
153 printf("%s\n", __PRETTY_FUNCTION__);
154 assert(false && "Statements after throw must be unreachable");
155 } catch (T t) {
156 assert(t == static_cast<T>(o));
157 //__builtin_printf("o = %p t = %p\n", o, t);
158 delete o;
159 return;
160 } catch (...) {
161 printf("%s\n", __PRETTY_FUNCTION__);
162 assert(false && "Should not have entered catch-all");
163 }
164
165 printf("%s\n", __PRETTY_FUNCTION__);
166 assert(false && "The catch should have returned");
167}
168
169void f1() {
170 assert_catches<Base*, Catchable1*>();
171 assert_catches<Base*, Catchable2*>();
172 assert_catches<Base*, Catchable3*>();
173}
174
175void f2() {
176 assert_cannot_catch<Base*, Ambiguous1*>();
177 assert_cannot_catch<Base*, Ambiguous2*>();
178 assert_cannot_catch<Base*, Ambiguous3*>();
179 assert_cannot_catch<Base*, NoPublic1*>();
180 assert_cannot_catch<Base*, NoPublic2*>();
181}
182
183void f3() {
184 assert_catches_bp<Base*, Catchable1>();
185 assert_catches_bp<Base*, Catchable2>();
186 assert_catches_bp<Base*, Catchable3>();
187}
188
189int main(int, char**) {
190 f1();
191 f2();
192 f3();
193 return 0;
194}
195

source code of libcxxabi/test/catch_null_pointer_to_object_pr64953.pass.cpp