xref: /llvm-project/libcxxabi/test/catch_null_pointer_to_object_pr64953.pass.cpp (revision 3497500946c9b6a1b2e1452312a24c41ee412b34)
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 
29 // This test requires the fix to https://github.com/llvm/llvm-project/issues/64953,
30 // which landed in d5f84e6 and is in the libc++abi built library.
31 // XFAIL: using-built-library-before-llvm-18
32 
33 #include <exception>
34 #include <stdlib.h>
35 #include <assert.h>
36 #include <stdio.h>
37 
38 struct Base {
39   int b;
40 };
41 struct Base2 {
42   int b;
43 };
44 struct Derived1 : Base {
45   int b;
46 };
47 struct Derived2 : Base {
48   int b;
49 };
50 struct Derived3 : Base2 {
51   int b;
52 };
53 struct Private : private Base {
54   int b;
55 };
56 struct Protected : protected Base {
57   int b;
58 };
59 struct Virtual1 : virtual Base {
60   int b;
61 };
62 struct Virtual2 : virtual Base {
63   int b;
64 };
65 
66 struct Ambiguous1 : Derived1, Derived2 {
67   int b;
68 };
69 struct Ambiguous2 : Derived1, Private {
70   int b;
71 };
72 struct Ambiguous3 : Derived1, Protected {
73   int b;
74 };
75 
76 struct NoPublic1 : Private, Base2 {
77   int b;
78 };
79 struct NoPublic2 : Protected, Base2 {
80   int b;
81 };
82 
83 struct Catchable1 : Derived3, Derived1 {
84   int b;
85 };
86 struct Catchable2 : Virtual1, Virtual2 {
87   int b;
88 };
89 struct Catchable3 : virtual Base, Virtual2 {
90   int b;
91 };
92 
93 // Check that, when we have a null pointer-to-object that we catch a nullptr.
94 template <typename T // Handler type
95           ,
96           typename E // Thrown exception type
97           >
assert_catches()98 void assert_catches() {
99   try {
100     throw static_cast<E>(0);
101     printf("%s\n", __PRETTY_FUNCTION__);
102     assert(false && "Statements after throw must be unreachable");
103   } catch (T t) {
104     assert(t == nullptr);
105     return;
106   } catch (...) {
107     printf("%s\n", __PRETTY_FUNCTION__);
108     assert(false && "Should not have entered catch-all");
109   }
110 
111   printf("%s\n", __PRETTY_FUNCTION__);
112   assert(false && "The catch should have returned");
113 }
114 
115 template <typename T // Handler type
116           ,
117           typename E // Thrown exception type
118           >
assert_cannot_catch()119 void assert_cannot_catch() {
120   try {
121     throw static_cast<E>(0);
122     printf("%s\n", __PRETTY_FUNCTION__);
123     assert(false && "Statements after throw must be unreachable");
124   } catch (T t) {
125     printf("%s\n", __PRETTY_FUNCTION__);
126     assert(false && "Should not have entered the catch");
127   } catch (...) {
128     assert(true);
129     return;
130   }
131 
132   printf("%s\n", __PRETTY_FUNCTION__);
133   assert(false && "The catch-all should have returned");
134 }
135 
136 // Check that when we have a pointer-to-actual-object we, in fact, get the
137 // adjusted pointer to the base class.
138 template <typename T // Handler type
139           ,
140           typename O // Object type
141           >
assert_catches_bp()142 void assert_catches_bp() {
143   O* o = new (O);
144   try {
145     throw o;
146     printf("%s\n", __PRETTY_FUNCTION__);
147     assert(false && "Statements after throw must be unreachable");
148   } catch (T t) {
149     assert(t == static_cast<T>(o));
150     //__builtin_printf("o = %p t = %p\n", o, t);
151     delete o;
152     return;
153   } catch (...) {
154     printf("%s\n", __PRETTY_FUNCTION__);
155     assert(false && "Should not have entered catch-all");
156   }
157 
158   printf("%s\n", __PRETTY_FUNCTION__);
159   assert(false && "The catch should have returned");
160 }
161 
f1()162 void f1() {
163   assert_catches<Base*, Catchable1*>();
164   assert_catches<Base*, Catchable2*>();
165   assert_catches<Base*, Catchable3*>();
166 }
167 
f2()168 void f2() {
169   assert_cannot_catch<Base*, Ambiguous1*>();
170   assert_cannot_catch<Base*, Ambiguous2*>();
171   assert_cannot_catch<Base*, Ambiguous3*>();
172   assert_cannot_catch<Base*, NoPublic1*>();
173   assert_cannot_catch<Base*, NoPublic2*>();
174 }
175 
f3()176 void f3() {
177   assert_catches_bp<Base*, Catchable1>();
178   assert_catches_bp<Base*, Catchable2>();
179   assert_catches_bp<Base*, Catchable3>();
180 }
181 
main(int,char **)182 int main(int, char**) {
183   f1();
184   f2();
185   f3();
186   return 0;
187 }
188