1 | //===---------- llvm/unittest/Support/Casting.cpp - Casting tests ---------===// |
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 | #include "llvm/Support/Casting.h" |
10 | #include "llvm/IR/User.h" |
11 | #include "llvm/Support/Debug.h" |
12 | #include "llvm/Support/raw_ostream.h" |
13 | #include "gtest/gtest.h" |
14 | #include <cstdlib> |
15 | |
16 | namespace llvm { |
17 | // Used to test illegal cast. If a cast doesn't match any of the "real" ones, |
18 | // it will match this one. |
19 | struct IllegalCast; |
20 | template <typename T> IllegalCast *cast(...) { return nullptr; } |
21 | |
22 | // set up two example classes |
23 | // with conversion facility |
24 | // |
25 | struct bar { |
26 | bar() {} |
27 | struct foo *baz(); |
28 | struct foo *caz(); |
29 | struct foo *daz(); |
30 | struct foo *naz(); |
31 | |
32 | private: |
33 | bar(const bar &); |
34 | }; |
35 | struct foo { |
36 | foo(const bar &) {} |
37 | void ext() const; |
38 | }; |
39 | |
40 | struct base { |
41 | virtual ~base() {} |
42 | }; |
43 | |
44 | struct derived : public base { |
45 | static bool classof(const base *B) { return true; } |
46 | }; |
47 | |
48 | struct derived_nocast : public base { |
49 | static bool classof(const base *B) { return false; } |
50 | }; |
51 | |
52 | template <> struct isa_impl<foo, bar> { |
53 | static inline bool doit(const bar &Val) { |
54 | dbgs() << "Classof: " << &Val << "\n" ; |
55 | return true; |
56 | } |
57 | }; |
58 | |
59 | // Note for the future - please don't do this. isa_impl is an internal template |
60 | // for the implementation of `isa` and should not be exposed this way. |
61 | // Completely unrelated types *should* result in compiler errors if you try to |
62 | // cast between them. |
63 | template <typename T> struct isa_impl<foo, T> { |
64 | static inline bool doit(const T &Val) { return false; } |
65 | }; |
66 | |
67 | foo *bar::baz() { return cast<foo>(Val: this); } |
68 | |
69 | foo *bar::caz() { return cast_or_null<foo>(Val: this); } |
70 | |
71 | foo *bar::daz() { return dyn_cast<foo>(Val: this); } |
72 | |
73 | foo *bar::naz() { return dyn_cast_or_null<foo>(Val: this); } |
74 | |
75 | bar *fub(); |
76 | |
77 | template <> struct simplify_type<foo> { |
78 | typedef int SimpleType; |
79 | static SimpleType getSimplifiedValue(foo &Val) { return 0; } |
80 | }; |
81 | |
82 | struct T1 {}; |
83 | |
84 | struct T2 { |
85 | T2(const T1 &x) {} |
86 | static bool classof(const T1 *x) { return true; } |
87 | }; |
88 | |
89 | template <> struct CastInfo<T2, T1> : public OptionalValueCast<T2, T1> {}; |
90 | |
91 | struct T3 { |
92 | T3(const T1 *x) : hasValue(x != nullptr) {} |
93 | |
94 | static bool classof(const T1 *x) { return true; } |
95 | bool hasValue = false; |
96 | }; |
97 | |
98 | // T3 is convertible from a pointer to T1. |
99 | template <> struct CastInfo<T3, T1 *> : public ValueFromPointerCast<T3, T1> {}; |
100 | |
101 | struct T4 { |
102 | T4() : hasValue(false) {} |
103 | T4(const T3 &x) : hasValue(true) {} |
104 | |
105 | static bool classof(const T3 *x) { return true; } |
106 | bool hasValue = false; |
107 | }; |
108 | |
109 | template <> struct ValueIsPresent<T3> { |
110 | using UnwrappedType = T3; |
111 | static inline bool isPresent(const T3 &t) { return t.hasValue; } |
112 | static inline const T3 &unwrapValue(const T3 &t) { return t; } |
113 | }; |
114 | |
115 | template <> struct CastInfo<T4, T3> { |
116 | using CastResultType = T4; |
117 | static inline CastResultType doCast(const T3 &t) { return T4(t); } |
118 | static inline CastResultType castFailed() { return CastResultType(); } |
119 | static inline CastResultType doCastIfPossible(const T3 &f) { |
120 | return doCast(t: f); |
121 | } |
122 | }; |
123 | |
124 | } // namespace llvm |
125 | |
126 | using namespace llvm; |
127 | |
128 | // Test the peculiar behavior of Use in simplify_type. |
129 | static_assert(std::is_same_v<simplify_type<Use>::SimpleType, Value *>, |
130 | "Use doesn't simplify correctly!" ); |
131 | static_assert(std::is_same_v<simplify_type<Use *>::SimpleType, Value *>, |
132 | "Use doesn't simplify correctly!" ); |
133 | |
134 | // Test that a regular class behaves as expected. |
135 | static_assert(std::is_same_v<simplify_type<foo>::SimpleType, int>, |
136 | "Unexpected simplify_type result!" ); |
137 | static_assert(std::is_same_v<simplify_type<foo *>::SimpleType, foo *>, |
138 | "Unexpected simplify_type result!" ); |
139 | |
140 | namespace { |
141 | |
142 | const foo *null_foo = nullptr; |
143 | |
144 | bar B; |
145 | extern bar &B1; |
146 | bar &B1 = B; |
147 | extern const bar *B2; |
148 | // test various configurations of const |
149 | const bar &B3 = B1; |
150 | const bar *const B4 = B2; |
151 | |
152 | TEST(CastingTest, isa) { |
153 | EXPECT_TRUE(isa<foo>(B1)); |
154 | EXPECT_TRUE(isa<foo>(B2)); |
155 | EXPECT_TRUE(isa<foo>(B3)); |
156 | EXPECT_TRUE(isa<foo>(B4)); |
157 | } |
158 | |
159 | TEST(CastingTest, isa_and_nonnull) { |
160 | EXPECT_TRUE(isa_and_nonnull<foo>(B2)); |
161 | EXPECT_TRUE(isa_and_nonnull<foo>(B4)); |
162 | EXPECT_FALSE(isa_and_nonnull<foo>(fub())); |
163 | } |
164 | |
165 | TEST(CastingTest, cast) { |
166 | foo &F1 = cast<foo>(Val&: B1); |
167 | EXPECT_NE(&F1, null_foo); |
168 | const foo *F3 = cast<foo>(Val: B2); |
169 | EXPECT_NE(F3, null_foo); |
170 | const foo *F4 = cast<foo>(Val: B2); |
171 | EXPECT_NE(F4, null_foo); |
172 | const foo &F5 = cast<foo>(Val: B3); |
173 | EXPECT_NE(&F5, null_foo); |
174 | const foo *F6 = cast<foo>(Val: B4); |
175 | EXPECT_NE(F6, null_foo); |
176 | // Can't pass null pointer to cast<>. |
177 | // foo *F7 = cast<foo>(fub()); |
178 | // EXPECT_EQ(F7, null_foo); |
179 | foo *F8 = B1.baz(); |
180 | EXPECT_NE(F8, null_foo); |
181 | |
182 | std::unique_ptr<const bar> BP(B2); |
183 | auto FP = cast<foo>(Val: std::move(BP)); |
184 | static_assert(std::is_same_v<std::unique_ptr<const foo>, decltype(FP)>, |
185 | "Incorrect deduced return type!" ); |
186 | EXPECT_NE(FP.get(), null_foo); |
187 | FP.release(); |
188 | } |
189 | |
190 | TEST(CastingTest, cast_or_null) { |
191 | const foo *F11 = cast_or_null<foo>(Val: B2); |
192 | EXPECT_NE(F11, null_foo); |
193 | const foo *F12 = cast_or_null<foo>(Val: B2); |
194 | EXPECT_NE(F12, null_foo); |
195 | const foo *F13 = cast_or_null<foo>(Val: B4); |
196 | EXPECT_NE(F13, null_foo); |
197 | const foo *F14 = cast_or_null<foo>(Val: fub()); // Shouldn't print. |
198 | EXPECT_EQ(F14, null_foo); |
199 | foo *F15 = B1.caz(); |
200 | EXPECT_NE(F15, null_foo); |
201 | |
202 | std::unique_ptr<const bar> BP(fub()); |
203 | auto FP = cast_or_null<foo>(Val: std::move(BP)); |
204 | EXPECT_EQ(FP.get(), null_foo); |
205 | } |
206 | |
207 | TEST(CastingTest, dyn_cast) { |
208 | const foo *F1 = dyn_cast<foo>(Val: B2); |
209 | EXPECT_NE(F1, null_foo); |
210 | const foo *F2 = dyn_cast<foo>(Val: B2); |
211 | EXPECT_NE(F2, null_foo); |
212 | const foo *F3 = dyn_cast<foo>(Val: B4); |
213 | EXPECT_NE(F3, null_foo); |
214 | // Can't pass null pointer to dyn_cast<>. |
215 | // foo *F4 = dyn_cast<foo>(fub()); |
216 | // EXPECT_EQ(F4, null_foo); |
217 | foo *F5 = B1.daz(); |
218 | EXPECT_NE(F5, null_foo); |
219 | |
220 | auto BP = std::make_unique<const bar>(); |
221 | auto FP = dyn_cast<foo>(Val&: BP); |
222 | static_assert(std::is_same_v<std::unique_ptr<const foo>, decltype(FP)>, |
223 | "Incorrect deduced return type!" ); |
224 | EXPECT_NE(FP.get(), nullptr); |
225 | EXPECT_EQ(BP.get(), nullptr); |
226 | |
227 | auto BP2 = std::make_unique<base>(); |
228 | auto DP = dyn_cast<derived_nocast>(Val&: BP2); |
229 | EXPECT_EQ(DP.get(), nullptr); |
230 | EXPECT_NE(BP2.get(), nullptr); |
231 | } |
232 | |
233 | // All these tests forward to dyn_cast_if_present, so they also provde an |
234 | // effective test for its use cases. |
235 | TEST(CastingTest, dyn_cast_or_null) { |
236 | const foo *F1 = dyn_cast_or_null<foo>(Val: B2); |
237 | EXPECT_NE(F1, null_foo); |
238 | const foo *F2 = dyn_cast_or_null<foo>(Val: B2); |
239 | EXPECT_NE(F2, null_foo); |
240 | const foo *F3 = dyn_cast_or_null<foo>(Val: B4); |
241 | EXPECT_NE(F3, null_foo); |
242 | foo *F4 = dyn_cast_or_null<foo>(Val: fub()); |
243 | EXPECT_EQ(F4, null_foo); |
244 | foo *F5 = B1.naz(); |
245 | EXPECT_NE(F5, null_foo); |
246 | // dyn_cast_if_present should have exactly the same behavior as |
247 | // dyn_cast_or_null. |
248 | const foo *F6 = dyn_cast_if_present<foo>(Val: B2); |
249 | EXPECT_EQ(F6, F2); |
250 | } |
251 | |
252 | TEST(CastingTest, dyn_cast_value_types) { |
253 | T1 t1; |
254 | std::optional<T2> t2 = dyn_cast<T2>(Val&: t1); |
255 | EXPECT_TRUE(t2); |
256 | |
257 | T2 *t2ptr = dyn_cast<T2>(Val: &t1); |
258 | EXPECT_TRUE(t2ptr != nullptr); |
259 | |
260 | T3 t3 = dyn_cast<T3>(Val: &t1); |
261 | EXPECT_TRUE(t3.hasValue); |
262 | } |
263 | |
264 | TEST(CastingTest, dyn_cast_if_present) { |
265 | std::optional<T1> empty{}; |
266 | std::optional<T2> F1 = dyn_cast_if_present<T2>(Val&: empty); |
267 | EXPECT_FALSE(F1.has_value()); |
268 | |
269 | T1 t1; |
270 | std::optional<T2> F2 = dyn_cast_if_present<T2>(Val&: t1); |
271 | EXPECT_TRUE(F2.has_value()); |
272 | |
273 | T1 *t1Null = nullptr; |
274 | |
275 | // T3 should have hasValue == false because t1Null is nullptr. |
276 | T3 t3 = dyn_cast_if_present<T3>(Val: t1Null); |
277 | EXPECT_FALSE(t3.hasValue); |
278 | |
279 | // Now because of that, T4 should receive the castFailed implementation of its |
280 | // FallibleCastTraits, which default-constructs a T4, which has no value. |
281 | T4 t4 = dyn_cast_if_present<T4>(Val&: t3); |
282 | EXPECT_FALSE(t4.hasValue); |
283 | } |
284 | |
285 | TEST(CastingTest, isa_check_predicates) { |
286 | auto IsaFoo = IsaPred<foo>; |
287 | EXPECT_TRUE(IsaFoo(B1)); |
288 | EXPECT_TRUE(IsaFoo(B2)); |
289 | EXPECT_TRUE(IsaFoo(B3)); |
290 | EXPECT_TRUE(IsaPred<foo>(B4)); |
291 | EXPECT_TRUE((IsaPred<foo, bar>(B4))); |
292 | |
293 | auto IsaAndPresentFoo = IsaAndPresentPred<foo>; |
294 | EXPECT_TRUE(IsaAndPresentFoo(B2)); |
295 | EXPECT_TRUE(IsaAndPresentFoo(B4)); |
296 | EXPECT_FALSE(IsaAndPresentPred<foo>(fub())); |
297 | EXPECT_FALSE((IsaAndPresentPred<foo, bar>(fub()))); |
298 | } |
299 | |
300 | std::unique_ptr<derived> newd() { return std::make_unique<derived>(); } |
301 | std::unique_ptr<base> newb() { return std::make_unique<derived>(); } |
302 | |
303 | TEST(CastingTest, unique_dyn_cast) { |
304 | derived *OrigD = nullptr; |
305 | auto D = std::make_unique<derived>(); |
306 | OrigD = D.get(); |
307 | |
308 | // Converting from D to itself is valid, it should return a new unique_ptr |
309 | // and the old one should become nullptr. |
310 | auto NewD = unique_dyn_cast<derived>(Val&: D); |
311 | ASSERT_EQ(OrigD, NewD.get()); |
312 | ASSERT_EQ(nullptr, D); |
313 | |
314 | // Converting from D to B is valid, B should have a value and D should be |
315 | // nullptr. |
316 | auto B = unique_dyn_cast<base>(Val&: NewD); |
317 | ASSERT_EQ(OrigD, B.get()); |
318 | ASSERT_EQ(nullptr, NewD); |
319 | |
320 | // Converting from B to itself is valid, it should return a new unique_ptr |
321 | // and the old one should become nullptr. |
322 | auto NewB = unique_dyn_cast<base>(Val&: B); |
323 | ASSERT_EQ(OrigD, NewB.get()); |
324 | ASSERT_EQ(nullptr, B); |
325 | |
326 | // Converting from B to D is valid, D should have a value and B should be |
327 | // nullptr; |
328 | D = unique_dyn_cast<derived>(Val&: NewB); |
329 | ASSERT_EQ(OrigD, D.get()); |
330 | ASSERT_EQ(nullptr, NewB); |
331 | |
332 | // This is a very contrived test, casting between completely unrelated types |
333 | // should generally fail to compile. See the classof shenanigans we have in |
334 | // the definition of `foo` above. |
335 | auto F = unique_dyn_cast<foo>(Val&: D); |
336 | ASSERT_EQ(nullptr, F); |
337 | ASSERT_EQ(OrigD, D.get()); |
338 | |
339 | // All of the above should also hold for temporaries. |
340 | auto D2 = unique_dyn_cast<derived>(Val: newd()); |
341 | EXPECT_NE(nullptr, D2); |
342 | |
343 | auto B2 = unique_dyn_cast<derived>(Val: newb()); |
344 | EXPECT_NE(nullptr, B2); |
345 | |
346 | auto B3 = unique_dyn_cast<base>(Val: newb()); |
347 | EXPECT_NE(nullptr, B3); |
348 | |
349 | // This is a very contrived test, casting between completely unrelated types |
350 | // should generally fail to compile. See the classof shenanigans we have in |
351 | // the definition of `foo` above. |
352 | auto F2 = unique_dyn_cast<foo>(Val: newb()); |
353 | EXPECT_EQ(nullptr, F2); |
354 | } |
355 | |
356 | // These lines are errors... |
357 | // foo *F20 = cast<foo>(B2); // Yields const foo* |
358 | // foo &F21 = cast<foo>(B3); // Yields const foo& |
359 | // foo *F22 = cast<foo>(B4); // Yields const foo* |
360 | // foo &F23 = cast_or_null<foo>(B1); |
361 | // const foo &F24 = cast_or_null<foo>(B3); |
362 | |
363 | const bar *B2 = &B; |
364 | } // anonymous namespace |
365 | |
366 | bar *llvm::fub() { return nullptr; } |
367 | |
368 | namespace { |
369 | namespace inferred_upcasting { |
370 | // This test case verifies correct behavior of inferred upcasts when the |
371 | // types are statically known to be OK to upcast. This is the case when, |
372 | // for example, Derived inherits from Base, and we do `isa<Base>(Derived)`. |
373 | |
374 | // Note: This test will actually fail to compile without inferred |
375 | // upcasting. |
376 | |
377 | class Base { |
378 | public: |
379 | // No classof. We are testing that the upcast is inferred. |
380 | Base() {} |
381 | }; |
382 | |
383 | class Derived : public Base { |
384 | public: |
385 | Derived() {} |
386 | }; |
387 | |
388 | // Even with no explicit classof() in Base, we should still be able to cast |
389 | // Derived to its base class. |
390 | TEST(CastingTest, UpcastIsInferred) { |
391 | Derived D; |
392 | EXPECT_TRUE(isa<Base>(D)); |
393 | Base *BP = dyn_cast<Base>(Val: &D); |
394 | EXPECT_NE(BP, nullptr); |
395 | } |
396 | |
397 | // This test verifies that the inferred upcast takes precedence over an |
398 | // explicitly written one. This is important because it verifies that the |
399 | // dynamic check gets optimized away. |
400 | class UseInferredUpcast { |
401 | public: |
402 | int Dummy; |
403 | static bool classof(const UseInferredUpcast *) { return false; } |
404 | }; |
405 | |
406 | TEST(CastingTest, InferredUpcastTakesPrecedence) { |
407 | UseInferredUpcast UIU; |
408 | // Since the explicit classof() returns false, this will fail if the |
409 | // explicit one is used. |
410 | EXPECT_TRUE(isa<UseInferredUpcast>(&UIU)); |
411 | } |
412 | |
413 | } // end namespace inferred_upcasting |
414 | } // end anonymous namespace |
415 | |
416 | namespace { |
417 | namespace pointer_wrappers { |
418 | |
419 | struct Base { |
420 | bool IsDerived; |
421 | Base(bool IsDerived = false) : IsDerived(IsDerived) {} |
422 | }; |
423 | |
424 | struct Derived : Base { |
425 | Derived() : Base(true) {} |
426 | static bool classof(const Base *B) { return B->IsDerived; } |
427 | }; |
428 | |
429 | class PTy { |
430 | Base *B; |
431 | |
432 | public: |
433 | PTy(Base *B) : B(B) {} |
434 | explicit operator bool() const { return get(); } |
435 | Base *get() const { return B; } |
436 | }; |
437 | |
438 | } // end namespace pointer_wrappers |
439 | } // end namespace |
440 | |
441 | namespace llvm { |
442 | |
443 | template <> struct ValueIsPresent<pointer_wrappers::PTy> { |
444 | using UnwrappedType = pointer_wrappers::PTy; |
445 | static inline bool isPresent(const pointer_wrappers::PTy &P) { |
446 | return P.get() != nullptr; |
447 | } |
448 | static UnwrappedType &unwrapValue(pointer_wrappers::PTy &P) { return P; } |
449 | }; |
450 | |
451 | template <> struct ValueIsPresent<const pointer_wrappers::PTy> { |
452 | using UnwrappedType = pointer_wrappers::PTy; |
453 | static inline bool isPresent(const pointer_wrappers::PTy &P) { |
454 | return P.get() != nullptr; |
455 | } |
456 | |
457 | static UnwrappedType &unwrapValue(const pointer_wrappers::PTy &P) { |
458 | return const_cast<UnwrappedType &>(P); |
459 | } |
460 | }; |
461 | |
462 | template <> struct simplify_type<pointer_wrappers::PTy> { |
463 | typedef pointer_wrappers::Base *SimpleType; |
464 | static SimpleType getSimplifiedValue(pointer_wrappers::PTy &P) { |
465 | return P.get(); |
466 | } |
467 | }; |
468 | template <> struct simplify_type<const pointer_wrappers::PTy> { |
469 | typedef pointer_wrappers::Base *SimpleType; |
470 | static SimpleType getSimplifiedValue(const pointer_wrappers::PTy &P) { |
471 | return P.get(); |
472 | } |
473 | }; |
474 | |
475 | } // end namespace llvm |
476 | |
477 | namespace { |
478 | namespace pointer_wrappers { |
479 | |
480 | // Some objects. |
481 | pointer_wrappers::Base B; |
482 | pointer_wrappers::Derived D; |
483 | |
484 | // Mutable "smart" pointers. |
485 | pointer_wrappers::PTy MN(nullptr); |
486 | pointer_wrappers::PTy MB(&B); |
487 | pointer_wrappers::PTy MD(&D); |
488 | |
489 | // Const "smart" pointers. |
490 | const pointer_wrappers::PTy CN(nullptr); |
491 | const pointer_wrappers::PTy CB(&B); |
492 | const pointer_wrappers::PTy CD(&D); |
493 | |
494 | TEST(CastingTest, smart_isa) { |
495 | EXPECT_TRUE(!isa<pointer_wrappers::Derived>(MB)); |
496 | EXPECT_TRUE(!isa<pointer_wrappers::Derived>(CB)); |
497 | EXPECT_TRUE(isa<pointer_wrappers::Derived>(MD)); |
498 | EXPECT_TRUE(isa<pointer_wrappers::Derived>(CD)); |
499 | } |
500 | |
501 | TEST(CastingTest, smart_cast) { |
502 | EXPECT_EQ(cast<pointer_wrappers::Derived>(MD), &D); |
503 | EXPECT_EQ(cast<pointer_wrappers::Derived>(CD), &D); |
504 | } |
505 | |
506 | TEST(CastingTest, smart_cast_or_null) { |
507 | EXPECT_EQ(cast_or_null<pointer_wrappers::Derived>(MN), nullptr); |
508 | EXPECT_EQ(cast_or_null<pointer_wrappers::Derived>(CN), nullptr); |
509 | EXPECT_EQ(cast_or_null<pointer_wrappers::Derived>(MD), &D); |
510 | EXPECT_EQ(cast_or_null<pointer_wrappers::Derived>(CD), &D); |
511 | } |
512 | |
513 | TEST(CastingTest, smart_dyn_cast) { |
514 | EXPECT_EQ(dyn_cast<pointer_wrappers::Derived>(MB), nullptr); |
515 | EXPECT_EQ(dyn_cast<pointer_wrappers::Derived>(CB), nullptr); |
516 | EXPECT_EQ(dyn_cast<pointer_wrappers::Derived>(MD), &D); |
517 | EXPECT_EQ(dyn_cast<pointer_wrappers::Derived>(CD), &D); |
518 | } |
519 | |
520 | TEST(CastingTest, smart_dyn_cast_or_null) { |
521 | EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(MN), nullptr); |
522 | EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(CN), nullptr); |
523 | EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(MB), nullptr); |
524 | EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(CB), nullptr); |
525 | EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(MD), &D); |
526 | EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(CD), &D); |
527 | } |
528 | |
529 | } // end namespace pointer_wrappers |
530 | |
531 | #ifndef NDEBUG |
532 | namespace assertion_checks { |
533 | struct Base { |
534 | virtual ~Base() {} |
535 | }; |
536 | |
537 | struct Derived : public Base { |
538 | static bool classof(const Base *B) { return false; } |
539 | }; |
540 | |
541 | TEST(CastingTest, assertion_check_const_ref) { |
542 | const Base B; |
543 | EXPECT_DEATH((void)cast<Derived>(B), "argument of incompatible type" ) |
544 | << "Invalid cast of const ref did not cause an abort()" ; |
545 | } |
546 | |
547 | TEST(CastingTest, assertion_check_ref) { |
548 | Base B; |
549 | EXPECT_DEATH((void)cast<Derived>(B), "argument of incompatible type" ) |
550 | << "Invalid cast of const ref did not cause an abort()" ; |
551 | } |
552 | |
553 | TEST(CastingTest, assertion_check_ptr) { |
554 | Base B; |
555 | EXPECT_DEATH((void)cast<Derived>(&B), "argument of incompatible type" ) |
556 | << "Invalid cast of const ref did not cause an abort()" ; |
557 | } |
558 | |
559 | TEST(CastingTest, assertion_check_unique_ptr) { |
560 | auto B = std::make_unique<Base>(); |
561 | EXPECT_DEATH((void)cast<Derived>(std::move(B)), |
562 | "argument of incompatible type" ) |
563 | << "Invalid cast of const ref did not cause an abort()" ; |
564 | } |
565 | |
566 | } // end namespace assertion_checks |
567 | #endif |
568 | } // end namespace |
569 | |