//===--------------------- inherited_exception.cpp ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This test case checks specifically the cases under C++ ABI 15.3.1, and 15.3.2
//
//  C++ ABI 15.3:
//  A handler is a match for an exception object of type E if
//  /  *  The handler is of type cv T or cv T& and E and T are the same type   \
//  |     (ignoring the top-level cv-qualifiers), or                           |
//  |  *  the handler is of type cv T or cv T& and T is an unambiguous base    |
//  \     class of E, or                                                       /
//     *  the handler is of type cv1 T* cv2 and E is a pointer type that can
//        be converted to the type of the handler by either or both of
//          o  a standard pointer conversion (4.10 [conv.ptr]) not involving
//             conversions to private or protected or ambiguous classes
//          o  a qualification conversion
//     *  the handler is a pointer or pointer to member type and E is
//        std::nullptr_t
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: libcxxabi-no-exceptions

// Clang emits  warnings about exceptions of type 'Child' being caught by
// an earlier handler of type 'Base'. Congrats clang, you've just
// diagnosed the behavior under test.
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wexceptions"
#endif

#include <assert.h>

struct Base {
  int b1;
};

struct Base2 {
  int b2;
};

struct Child : public Base, public Base2 {
  int c;
};

void f1() {
  Child child;
  child.b1 = 10;
  child.b2 = 11;
  child.c = 12;
  throw child;
}

void f2() {
  Child child;
  child.b1 = 10;
  child.b2 = 11;
  child.c = 12;
  throw static_cast<Base2&>(child);
}

void f3() {
  static Child child;
  child.b1 = 10;
  child.b2 = 11;
  child.c = 12;
  throw static_cast<Base2*>(&child);
}

int main()
{
    try
    {
        f1();
        assert(false);
    }
    catch (const Child& c)
    {
        assert(true);
    }
    catch (const Base& b)
    {
        assert(false);
    }
    catch (...)
    {
        assert(false);
    }

    try
    {
        f1();
        assert(false);
    }
    catch (const Base& c)
    {
        assert(true);
    }
    catch (const Child& b)
    {
        assert(false);
    }
    catch (...)
    {
        assert(false);
    }

    try
    {
        f1();
        assert(false);
    }
    catch (const Base2& c)
    {
        assert(true);
    }
    catch (const Child& b)
    {
        assert(false);
    }
    catch (...)
    {
        assert(false);
    }

    try
    {
        f2();
        assert(false);
    }
    catch (const Child& c)
    {
        assert(false);
    }
    catch (const Base& b)
    {
        assert(false);
    }
    catch (const Base2& b)
    {
        assert(true);
    }
    catch (...)
    {
        assert(false);
    }

    try
    {
        f3();
        assert(false);
    }
    catch (const Base* c)
    {
        assert(false);
    }
    catch (const Child* b)
    {
        assert(false);
    }
    catch (const Base2* c)
    {
        assert(true);
    }
    catch (...)
    {
        assert(false);
    }
}
