xref: /llvm-project/compiler-rt/lib/orc/tests/unit/error_test.cpp (revision dbd81ba2e85c2f244f22c983d96a106eae65c06a)
11169586dSLang Hames //===-- error_test.cpp --sssssssss-----------------------------------------===//
21169586dSLang Hames //
31169586dSLang Hames // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41169586dSLang Hames // See https://llvm.org/LICENSE.txt for license information.
51169586dSLang Hames // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61169586dSLang Hames //
71169586dSLang Hames //===----------------------------------------------------------------------===//
81169586dSLang Hames //
91169586dSLang Hames // This file is a part of the ORC runtime.
101169586dSLang Hames //
111169586dSLang Hames // Note:
121169586dSLang Hames //   This unit test was adapted from
131169586dSLang Hames //   llvm/unittests/Support/ExtensibleRTTITest.cpp
141169586dSLang Hames //
151169586dSLang Hames //===----------------------------------------------------------------------===//
161169586dSLang Hames 
171169586dSLang Hames #include "error.h"
181169586dSLang Hames #include "gtest/gtest.h"
191169586dSLang Hames 
20*dbd81ba2SMikhail Goncharov using namespace orc_rt;
211169586dSLang Hames 
221169586dSLang Hames namespace {
231169586dSLang Hames 
241169586dSLang Hames class CustomError : public RTTIExtends<CustomError, ErrorInfoBase> {
251169586dSLang Hames public:
261169586dSLang Hames   CustomError(int V1) : V1(V1) {}
271169586dSLang Hames   std::string toString() const override {
281169586dSLang Hames     return "CustomError V1 = " + std::to_string(V1);
291169586dSLang Hames   }
301169586dSLang Hames   int getV1() const { return V1; }
311169586dSLang Hames 
321169586dSLang Hames protected:
331169586dSLang Hames   int V1;
341169586dSLang Hames };
351169586dSLang Hames 
361169586dSLang Hames class CustomSubError : public RTTIExtends<CustomSubError, CustomError> {
371169586dSLang Hames public:
381169586dSLang Hames   CustomSubError(int V1, std::string V2)
391169586dSLang Hames       : RTTIExtends<CustomSubError, CustomError>(V1), V2(std::move(V2)) {}
401169586dSLang Hames   std::string toString() const override {
411169586dSLang Hames     return "CustomSubError V1 = " + std::to_string(V1) + ", " + V2;
421169586dSLang Hames   }
431169586dSLang Hames   const std::string &getV2() const { return V2; }
441169586dSLang Hames 
451169586dSLang Hames protected:
461169586dSLang Hames   std::string V2;
471169586dSLang Hames };
481169586dSLang Hames 
491169586dSLang Hames } // end anonymous namespace
501169586dSLang Hames 
511169586dSLang Hames // Test that a checked success value doesn't cause any issues.
521169586dSLang Hames TEST(Error, CheckedSuccess) {
531169586dSLang Hames   Error E = Error::success();
541169586dSLang Hames   EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'";
551169586dSLang Hames }
561169586dSLang Hames 
571169586dSLang Hames // Check that a consumed success value doesn't cause any issues.
581169586dSLang Hames TEST(Error, ConsumeSuccess) { consumeError(Error::success()); }
591169586dSLang Hames 
601169586dSLang Hames TEST(Error, ConsumeError) {
611169586dSLang Hames   Error E = make_error<CustomError>(42);
621169586dSLang Hames   if (E) {
631169586dSLang Hames     consumeError(std::move(E));
641169586dSLang Hames   } else
651169586dSLang Hames     ADD_FAILURE() << "Error failure value should convert to true";
661169586dSLang Hames }
671169586dSLang Hames 
681169586dSLang Hames // Test that unchecked success values cause an abort.
691169586dSLang Hames TEST(Error, UncheckedSuccess) {
701169586dSLang Hames   EXPECT_DEATH({ Error E = Error::success(); },
711169586dSLang Hames                "Error must be checked prior to destruction")
721169586dSLang Hames       << "Unchecked Error Succes value did not cause abort()";
731169586dSLang Hames }
741169586dSLang Hames 
751169586dSLang Hames // Test that a checked but unhandled error causes an abort.
761169586dSLang Hames TEST(Error, CheckedButUnhandledError) {
771169586dSLang Hames   auto DropUnhandledError = []() {
781169586dSLang Hames     Error E = make_error<CustomError>(42);
791169586dSLang Hames     (void)!E;
801169586dSLang Hames   };
811169586dSLang Hames   EXPECT_DEATH(DropUnhandledError(),
821169586dSLang Hames                "Error must be checked prior to destruction")
831169586dSLang Hames       << "Unhandled Error failure value did not cause an abort()";
841169586dSLang Hames }
851169586dSLang Hames 
861169586dSLang Hames // Test that error_cast works as expected.
871169586dSLang Hames TEST(Error, BasicErrorCast) {
881169586dSLang Hames   {
891169586dSLang Hames     // Check casting base error value to base error type.
901169586dSLang Hames     auto E = make_error<CustomError>(42);
911169586dSLang Hames     if (auto CSE = error_cast<CustomSubError>(E)) {
921169586dSLang Hames       ADD_FAILURE() << "Derived cast incorrectly matched base error";
931169586dSLang Hames     } else if (auto CE = error_cast<CustomError>(E)) {
941169586dSLang Hames       EXPECT_EQ(CE->getV1(), 42) << "Unexpected wrapped value";
951169586dSLang Hames     } else
961169586dSLang Hames       ADD_FAILURE() << "Unexpected error value";
971169586dSLang Hames   }
981169586dSLang Hames 
991169586dSLang Hames   {
1001169586dSLang Hames     // Check casting derived error value to base error type.
1011169586dSLang Hames     auto E = make_error<CustomSubError>(42, "foo");
1021169586dSLang Hames     if (auto CE = error_cast<CustomError>(E)) {
1031169586dSLang Hames       EXPECT_EQ(CE->getV1(), 42) << "Unexpected wrapped value";
1041169586dSLang Hames     } else
1051169586dSLang Hames       ADD_FAILURE() << "Unexpected error value";
1061169586dSLang Hames   }
1071169586dSLang Hames 
1081169586dSLang Hames   {
1091169586dSLang Hames     // Check casting derived error value to derived error type.
1101169586dSLang Hames     auto E = make_error<CustomSubError>(42, "foo");
1111169586dSLang Hames     if (auto CSE = error_cast<CustomSubError>(E)) {
1121169586dSLang Hames       EXPECT_EQ(CSE->getV1(), 42) << "Unexpected wrapped value";
1131169586dSLang Hames       EXPECT_EQ(CSE->getV2(), "foo") << "Unexpected wrapped value";
1141169586dSLang Hames     } else
1151169586dSLang Hames       ADD_FAILURE() << "Unexpected error value";
1161169586dSLang Hames   }
1171169586dSLang Hames }
1181169586dSLang Hames 
1191169586dSLang Hames // ErrorAsOutParameter tester.
1201169586dSLang Hames static void errAsOutParamHelper(Error &Err) {
1211169586dSLang Hames   ErrorAsOutParameter ErrAsOutParam(&Err);
1221169586dSLang Hames   // Verify that checked flag is raised - assignment should not crash.
1231169586dSLang Hames   Err = Error::success();
1241169586dSLang Hames   // Raise the checked bit manually - caller should still have to test the
1251169586dSLang Hames   // error.
1261169586dSLang Hames   (void)!!Err;
1271169586dSLang Hames }
1281169586dSLang Hames 
1291169586dSLang Hames // Test that ErrorAsOutParameter sets the checked flag on construction.
1301169586dSLang Hames TEST(Error, ErrorAsOutParameterChecked) {
1311169586dSLang Hames   Error E = Error::success();
1321169586dSLang Hames   errAsOutParamHelper(E);
1331169586dSLang Hames   (void)!!E;
1341169586dSLang Hames }
1351169586dSLang Hames 
1361169586dSLang Hames // Test that ErrorAsOutParameter clears the checked flag on destruction.
1371169586dSLang Hames TEST(Error, ErrorAsOutParameterUnchecked) {
1381169586dSLang Hames   EXPECT_DEATH(
1391169586dSLang Hames       {
1401169586dSLang Hames         Error E = Error::success();
1411169586dSLang Hames         errAsOutParamHelper(E);
1421169586dSLang Hames       },
1431169586dSLang Hames       "Error must be checked prior to destruction")
1441169586dSLang Hames       << "ErrorAsOutParameter did not clear the checked flag on destruction.";
1451169586dSLang Hames }
1461169586dSLang Hames 
1471169586dSLang Hames // Check 'Error::isA<T>' method handling.
1481169586dSLang Hames TEST(Error, IsAHandling) {
1491169586dSLang Hames   // Check 'isA' handling.
1501169586dSLang Hames   Error E = make_error<CustomError>(42);
1511169586dSLang Hames   Error F = make_error<CustomSubError>(42, "foo");
1521169586dSLang Hames   Error G = Error::success();
1531169586dSLang Hames 
1541169586dSLang Hames   EXPECT_TRUE(E.isA<CustomError>());
1551169586dSLang Hames   EXPECT_FALSE(E.isA<CustomSubError>());
1561169586dSLang Hames   EXPECT_TRUE(F.isA<CustomError>());
1571169586dSLang Hames   EXPECT_TRUE(F.isA<CustomSubError>());
1581169586dSLang Hames   EXPECT_FALSE(G.isA<CustomError>());
1591169586dSLang Hames 
1601169586dSLang Hames   consumeError(std::move(E));
1611169586dSLang Hames   consumeError(std::move(F));
1621169586dSLang Hames   consumeError(std::move(G));
1631169586dSLang Hames }
1641169586dSLang Hames 
1651169586dSLang Hames TEST(Error, StringError) {
1661169586dSLang Hames   auto E = make_error<StringError>("foo");
1671169586dSLang Hames   if (auto SE = error_cast<StringError>(E)) {
1681169586dSLang Hames     EXPECT_EQ(SE->toString(), "foo") << "Unexpected StringError value";
1691169586dSLang Hames   } else
1701169586dSLang Hames     ADD_FAILURE() << "Expected StringError value";
1711169586dSLang Hames }
1721169586dSLang Hames 
1731169586dSLang Hames // Test Checked Expected<T> in success mode.
1741169586dSLang Hames TEST(Error, CheckedExpectedInSuccessMode) {
1751169586dSLang Hames   Expected<int> A = 7;
1761169586dSLang Hames   EXPECT_TRUE(!!A) << "Expected with non-error value doesn't convert to 'true'";
1771169586dSLang Hames   // Access is safe in second test, since we checked the error in the first.
1781169586dSLang Hames   EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value";
1791169586dSLang Hames }
1801169586dSLang Hames 
1811169586dSLang Hames // Test Expected with reference type.
1821169586dSLang Hames TEST(Error, ExpectedWithReferenceType) {
1831169586dSLang Hames   int A = 7;
1841169586dSLang Hames   Expected<int &> B = A;
1851169586dSLang Hames   // 'Check' B.
1861169586dSLang Hames   (void)!!B;
1871169586dSLang Hames   int &C = *B;
1881169586dSLang Hames   EXPECT_EQ(&A, &C) << "Expected failed to propagate reference";
1891169586dSLang Hames }
1901169586dSLang Hames 
1911169586dSLang Hames // Test Unchecked Expected<T> in success mode.
1921169586dSLang Hames // We expect this to blow up the same way Error would.
1931169586dSLang Hames // Test runs in debug mode only.
1941169586dSLang Hames TEST(Error, UncheckedExpectedInSuccessModeDestruction) {
1951169586dSLang Hames   EXPECT_DEATH({ Expected<int> A = 7; },
1961169586dSLang Hames                "Expected<T> must be checked before access or destruction.")
1971169586dSLang Hames       << "Unchecekd Expected<T> success value did not cause an abort().";
1981169586dSLang Hames }
1991169586dSLang Hames 
2001169586dSLang Hames // Test Unchecked Expected<T> in success mode.
2011169586dSLang Hames // We expect this to blow up the same way Error would.
2021169586dSLang Hames // Test runs in debug mode only.
2031169586dSLang Hames TEST(Error, UncheckedExpectedInSuccessModeAccess) {
2041169586dSLang Hames   EXPECT_DEATH(
2051169586dSLang Hames       {
2061169586dSLang Hames         Expected<int> A = 7;
2071169586dSLang Hames         *A;
2081169586dSLang Hames       },
2091169586dSLang Hames       "Expected<T> must be checked before access or destruction.")
2101169586dSLang Hames       << "Unchecekd Expected<T> success value did not cause an abort().";
2111169586dSLang Hames }
2121169586dSLang Hames 
2131169586dSLang Hames // Test Unchecked Expected<T> in success mode.
2141169586dSLang Hames // We expect this to blow up the same way Error would.
2151169586dSLang Hames // Test runs in debug mode only.
2161169586dSLang Hames TEST(Error, UncheckedExpectedInSuccessModeAssignment) {
2171169586dSLang Hames   EXPECT_DEATH(
2181169586dSLang Hames       {
2191169586dSLang Hames         Expected<int> A = 7;
2201169586dSLang Hames         A = 7;
2211169586dSLang Hames       },
2221169586dSLang Hames       "Expected<T> must be checked before access or destruction.")
2231169586dSLang Hames       << "Unchecekd Expected<T> success value did not cause an abort().";
2241169586dSLang Hames }
2251169586dSLang Hames 
2261169586dSLang Hames // Test Expected<T> in failure mode.
2271169586dSLang Hames TEST(Error, ExpectedInFailureMode) {
2281169586dSLang Hames   Expected<int> A = make_error<CustomError>(42);
2291169586dSLang Hames   EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'";
2301169586dSLang Hames   Error E = A.takeError();
2311169586dSLang Hames   EXPECT_TRUE(E.isA<CustomError>()) << "Incorrect Expected error value";
2321169586dSLang Hames   consumeError(std::move(E));
2331169586dSLang Hames }
2341169586dSLang Hames 
2351169586dSLang Hames // Check that an Expected instance with an error value doesn't allow access to
2361169586dSLang Hames // operator*.
2371169586dSLang Hames // Test runs in debug mode only.
2381169586dSLang Hames TEST(Error, AccessExpectedInFailureMode) {
2391169586dSLang Hames   Expected<int> A = make_error<CustomError>(42);
2401169586dSLang Hames   EXPECT_DEATH(*A, "Expected<T> must be checked before access or destruction.")
2411169586dSLang Hames       << "Incorrect Expected error value";
2421169586dSLang Hames   consumeError(A.takeError());
2431169586dSLang Hames }
2441169586dSLang Hames 
2451169586dSLang Hames // Check that an Expected instance with an error triggers an abort if
2461169586dSLang Hames // unhandled.
2471169586dSLang Hames // Test runs in debug mode only.
2481169586dSLang Hames TEST(Error, UnhandledExpectedInFailureMode) {
2491169586dSLang Hames   EXPECT_DEATH({ Expected<int> A = make_error<CustomError>(42); },
2501169586dSLang Hames                "Expected<T> must be checked before access or destruction.")
2511169586dSLang Hames       << "Unchecked Expected<T> failure value did not cause an abort()";
2521169586dSLang Hames }
2531169586dSLang Hames 
2541169586dSLang Hames // Test covariance of Expected.
2551169586dSLang Hames TEST(Error, ExpectedCovariance) {
2561169586dSLang Hames   class B {};
2571169586dSLang Hames   class D : public B {};
2581169586dSLang Hames 
2591169586dSLang Hames   Expected<B *> A1(Expected<D *>(nullptr));
2601169586dSLang Hames   // Check A1 by converting to bool before assigning to it.
2611169586dSLang Hames   (void)!!A1;
2621169586dSLang Hames   A1 = Expected<D *>(nullptr);
2631169586dSLang Hames   // Check A1 again before destruction.
2641169586dSLang Hames   (void)!!A1;
2651169586dSLang Hames 
2661169586dSLang Hames   Expected<std::unique_ptr<B>> A2(Expected<std::unique_ptr<D>>(nullptr));
2671169586dSLang Hames   // Check A2 by converting to bool before assigning to it.
2681169586dSLang Hames   (void)!!A2;
2691169586dSLang Hames   A2 = Expected<std::unique_ptr<D>>(nullptr);
2701169586dSLang Hames   // Check A2 again before destruction.
2711169586dSLang Hames   (void)!!A2;
2721169586dSLang Hames }
2731169586dSLang Hames 
2741169586dSLang Hames // Test that the ExitOnError utility works as expected.
2751169586dSLang Hames TEST(Error, CantFailSuccess) {
2761169586dSLang Hames   cantFail(Error::success());
2771169586dSLang Hames 
2781169586dSLang Hames   int X = cantFail(Expected<int>(42));
2791169586dSLang Hames   EXPECT_EQ(X, 42) << "Expected value modified by cantFail";
2801169586dSLang Hames 
2811169586dSLang Hames   int Dummy = 42;
2821169586dSLang Hames   int &Y = cantFail(Expected<int &>(Dummy));
2831169586dSLang Hames   EXPECT_EQ(&Dummy, &Y) << "Reference mangled by cantFail";
2841169586dSLang Hames }
2851169586dSLang Hames 
2861169586dSLang Hames // Test that cantFail results in a crash if you pass it a failure value.
2871169586dSLang Hames TEST(Error, CantFailDeath) {
2881169586dSLang Hames   EXPECT_DEATH(cantFail(make_error<StringError>("foo")),
2891169586dSLang Hames                "cantFail called on failure value")
2901169586dSLang Hames       << "cantFail(Error) did not cause an abort for failure value";
2911169586dSLang Hames 
2921169586dSLang Hames   EXPECT_DEATH(cantFail(Expected<int>(make_error<StringError>("foo"))),
2931169586dSLang Hames                "cantFail called on failure value")
2941169586dSLang Hames       << "cantFail(Expected<int>) did not cause an abort for failure value";
2951169586dSLang Hames }
296