diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt index eb24f29b14695..717b75290e052 100644 --- a/orc-rt/include/CMakeLists.txt +++ b/orc-rt/include/CMakeLists.txt @@ -1,5 +1,8 @@ set(ORC_RT_HEADERS orc-rt-c/orc-rt.h + orc-rt/bitmask-enum.h + orc-rt/math.h + orc-rt/rtti.h orc-rt/span.h ) diff --git a/orc-rt/include/orc-rt/bitmask-enum.h b/orc-rt/include/orc-rt/bitmask-enum.h new file mode 100644 index 0000000000000..adc95424b83a8 --- /dev/null +++ b/orc-rt/include/orc-rt/bitmask-enum.h @@ -0,0 +1,161 @@ +//===---- bitmask-enum.h - Enable bitmask operations on enums ---*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Provides utilities for easily adding bitmask operation support to enums. +// +// This code was derived from LLVM's include/llvm/ADT/BitmaskEnum.h header, and +// adapted for the ORC runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_BITMASK_ENUM_H +#define ORC_RT_BITMASK_ENUM_H + +#include "math.h" + +#include +#include + +namespace orc_rt { + +/// ORC_RT_MARK_AS_BITMASK_ENUM lets you opt in an individual enum type so you +/// can perform bitwise operations on it without putting static_cast everywhere. +/// +/// \code +/// enum MyEnum { +/// E1 = 1, E2 = 2, E3 = 4, E4 = 8, +/// ORC_RT_MARK_AS_BITMASK_ENUM(/* LargestValue = */ E4) +/// }; +/// +/// void Foo() { +/// MyEnum A = (E1 | E2) & E3 ^ ~E4; // Look, ma: No static_cast! +/// } +/// \endcode +/// +/// Normally when you do a bitwise operation on an enum value, you get back an +/// instance of the underlying type (e.g. int). But using this macro, bitwise +/// ops on your enum will return you back instances of the enum. This is +/// particularly useful for enums which represent a combination of flags. +/// +/// The parameter to ORC_RT_MARK_AS_BITMASK_ENUM should be the largest +/// individual value in your enum. +/// +/// All of the enum's values must be non-negative. +#define ORC_RT_MARK_AS_BITMASK_ENUM(LargestValue) \ + ORC_RT_BITMASK_LARGEST_ENUMERATOR = LargestValue + +/// ORC_RT_DECLARE_ENUM_AS_BITMASK can be used to declare an enum type as a bit +/// set, so that bitwise operation on such enum does not require static_cast. +/// +/// \code +/// enum MyEnum { E1 = 1, E2 = 2, E3 = 4, E4 = 8 }; +/// ORC_RT_DECLARE_ENUM_AS_BITMASK(MyEnum, E4); +/// +/// void Foo() { +/// MyEnum A = (E1 | E2) & E3 ^ ~E4; // No static_cast +/// } +/// \endcode +/// +/// The second parameter to ORC_RT_DECLARE_ENUM_AS_BITMASK specifies the largest +/// bit value of the enum type. +/// +/// ORC_RT_DECLARE_ENUM_AS_BITMASK should be used in __orc_rt namespace. +/// +/// This a non-intrusive alternative for ORC_RT_MARK_AS_BITMASK_ENUM. It allows +/// declaring more than one non-scoped enumerations as bitmask types in the same +/// scope. Otherwise it provides the same functionality as +/// ORC_RT_MARK_AS_BITMASK_ENUM. +#define ORC_RT_DECLARE_ENUM_AS_BITMASK(Enum, LargestValue) \ + template <> struct is_bitmask_enum : std::true_type {}; \ + template <> struct largest_bitmask_enum_bit { \ + static constexpr std::underlying_type_t value = LargestValue; \ + } + +/// Traits class to determine whether an enum has been declared as a bitwise +/// enum via ORC_RT_DECLARE_ENUM_AS_BITMASK. +template +struct is_bitmask_enum : std::false_type {}; + +template +struct is_bitmask_enum< + E, std::enable_if_t= 0>> + : std::true_type {}; + +template +inline constexpr bool is_bitmask_enum_v = is_bitmask_enum::value; + +/// Traits class to deermine bitmask enum largest bit. +template struct largest_bitmask_enum_bit; + +template +struct largest_bitmask_enum_bit< + E, std::enable_if_t= 0>> { + using UnderlyingTy = std::underlying_type_t; + static constexpr UnderlyingTy value = + static_cast(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR); +}; + +template +constexpr std::underlying_type_t bitmask_enum_mask() noexcept { + return nextPowerOf2(largest_bitmask_enum_bit::value) - 1; +} + +template +constexpr std::underlying_type_t bitmask_enum_to_underlying(E Val) noexcept { + auto U = static_cast>(Val); + assert(U >= 0 && "Negative enum values are not allowed"); + assert(U <= bitmask_enum_mask() && + "Enum value too large (or langest val too small"); + return U; +} + +template >> +constexpr E operator~(E Val) noexcept { + return static_cast(~bitmask_enum_to_underlying(Val) & + bitmask_enum_mask()); +} + +template >> +constexpr E operator|(E LHS, E RHS) noexcept { + return static_cast(bitmask_enum_to_underlying(LHS) | + bitmask_enum_to_underlying(RHS)); +} + +template >> +constexpr E operator&(E LHS, E RHS) noexcept { + return static_cast(bitmask_enum_to_underlying(LHS) & + bitmask_enum_to_underlying(RHS)); +} + +template >> +constexpr E operator^(E LHS, E RHS) noexcept { + return static_cast(bitmask_enum_to_underlying(LHS) ^ + bitmask_enum_to_underlying(RHS)); +} + +template >> +constexpr E &operator|=(E &LHS, E RHS) noexcept { + LHS = LHS | RHS; + return LHS; +} + +template >> +constexpr E &operator&=(E &LHS, E RHS) noexcept { + LHS = LHS & RHS; + return LHS; +} + +template >> +constexpr E &operator^=(E &LHS, E RHS) noexcept { + LHS = LHS ^ RHS; + return LHS; +} + +} // namespace orc_rt + +#endif // ORC_RT_BITMASK_ENUM_H diff --git a/orc-rt/include/orc-rt/math.h b/orc-rt/include/orc-rt/math.h new file mode 100644 index 0000000000000..4dd6ddd9e506c --- /dev/null +++ b/orc-rt/include/orc-rt/math.h @@ -0,0 +1,35 @@ +//===--------- math.h - Math helpers for the ORC runtime --------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Math helper functions for the ORC runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_MATH_H +#define ORC_RT_MATH_H + +#include +#include + +namespace orc_rt { + +/// Test whether the given value is a power of 2. +template constexpr bool isPowerOf2(T Val) noexcept { + return Val != 0 && (Val & (Val - 1)) == 0; +} + +/// Calculates the next power of 2. +template constexpr T nextPowerOf2(T Val) noexcept { + for (size_t I = 1; I < std::numeric_limits::digits; I <<= 1) + Val |= (Val >> I); + return Val + 1; +} + +} // namespace orc_rt + +#endif // ORC_RT_MATH_H diff --git a/orc-rt/include/orc-rt/rtti.h b/orc-rt/include/orc-rt/rtti.h new file mode 100644 index 0000000000000..3097edf68d7b2 --- /dev/null +++ b/orc-rt/include/orc-rt/rtti.h @@ -0,0 +1,138 @@ +//===------------- rtti.h - RTTI support for ORC RT -------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// \file +// +// Provides an extensible RTTI mechanism, that can be used regardless of whether +// the runtime is built with -frtti or not. This is predominantly used to +// support error handling. +// +// The RTTIRoot class defines methods for comparing type ids. Implementations +// of these methods can be injected into new classes using the RTTIExtends +// class template. +// +// E.g. +// +// @code{.cpp} +// class MyBaseClass : public RTTIExtends { +// public: +// virtual void foo() = 0; +// }; +// +// class MyDerivedClass1 : public RTTIExtends { +// public: +// void foo() override {} +// }; +// +// class MyDerivedClass2 : public RTTIExtends { +// public: +// void foo() override {} +// }; +// +// void fn() { +// std::unique_ptr B = std::make_unique(); +// outs() << isa(B) << "\n"; // Outputs "1". +// outs() << isa(B) << "\n"; // Outputs "1". +// outs() << isa(B) << "\n"; // Outputs "0'. +// } +// +// @endcode +// +// Note: +// This header was adapted from llvm/Support/ExtensibleRTTI.h, however the +// data structures are not shared and the code need not be kept in sync. +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_RTTI_H +#define ORC_RT_RTTI_H + +namespace orc_rt { + +template class RTTIExtends; + +/// Base class for the extensible RTTI hierarchy. +/// +/// This class defines virtual methods, dynamicClassID and isA, that enable +/// type comparisons. +class RTTIRoot { +public: + virtual ~RTTIRoot() = default; + + /// Returns the class ID for this type. + static const void *classID() noexcept { return &ID; } + + /// Returns the class ID for the dynamic type of this RTTIRoot instance. + virtual const void *dynamicClassID() const noexcept = 0; + + /// Returns true if this class's ID matches the given class ID. + virtual bool isA(const void *const ClassID) const noexcept { + return ClassID == classID(); + } + + /// Check whether this instance is a subclass of QueryT. + template bool isA() const noexcept { + return isA(QueryT::classID()); + } + + static bool classof(const RTTIRoot *R) noexcept { return R->isA(); } + +private: + virtual void anchor(); + + static char ID; +}; + +/// Inheritance utility for extensible RTTI. +/// +/// Supports single inheritance only: A class can only have one +/// ExtensibleRTTI-parent (i.e. a parent for which the isa<> test will work), +/// though it can have many non-ExtensibleRTTI parents. +/// +/// RTTIExtents uses CRTP so the first template argument to RTTIExtends is the +/// newly introduced type, and the *second* argument is the parent class. +/// +/// class MyType : public RTTIExtends { +/// ... +/// }; +/// +/// class MyDerivedType : public RTTIExtends { +/// ... +/// }; +/// +template class RTTIExtends : public ParentT { +public: + // Inherit constructors and isA methods from ParentT. + using ParentT::isA; + using ParentT::ParentT; + + static char ID; + + static const void *classID() noexcept { return &ThisT::ID; } + + const void *dynamicClassID() const noexcept override { return &ThisT::ID; } + + bool isA(const void *const ClassID) const noexcept override { + return ClassID == classID() || ParentT::isA(ClassID); + } + + static bool classof(const RTTIRoot *R) { return R->isA(); } +}; + +template +char RTTIExtends::ID = 0; + +/// Returns true if the given value is an instance of the template type +/// parameter. +template bool isa(const From &Value) noexcept { + return To::classof(&Value); +} + +} // namespace orc_rt + +#endif // ORC_RT_RTTI_H diff --git a/orc-rt/include/orc-rt/span.h b/orc-rt/include/orc-rt/span.h index 7584a4508b749..aa0b3cc66a628 100644 --- a/orc-rt/include/orc-rt/span.h +++ b/orc-rt/include/orc-rt/span.h @@ -6,7 +6,10 @@ // //===----------------------------------------------------------------------===// // -// TODO: Replace all uses with std::span once we can use C++20. +// A substitute for std::span that can be used until the ORC runtime is allowed +// to assume c++-20. +// +// TODO: Replace all uses with std::span once we can assume c++20. // //===----------------------------------------------------------------------===// diff --git a/orc-rt/lib/executor/CMakeLists.txt b/orc-rt/lib/executor/CMakeLists.txt index 82d29455eb6df..40035bb4dcffb 100644 --- a/orc-rt/lib/executor/CMakeLists.txt +++ b/orc-rt/lib/executor/CMakeLists.txt @@ -1,5 +1,6 @@ set(files orc-rt-executor.cpp + rtti.cpp ) add_library(orc-rt-executor STATIC ${files}) diff --git a/orc-rt/lib/executor/rtti.cpp b/orc-rt/lib/executor/rtti.cpp new file mode 100644 index 0000000000000..8c6d27fb25c73 --- /dev/null +++ b/orc-rt/lib/executor/rtti.cpp @@ -0,0 +1,24 @@ +//===- rtti.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 file is a part of the ORC runtime support library. +// +// Note: +// This source file was adapted from lib/Support/ExtensibleRTTI.cpp, however +// the data structures are not shared and the code need not be kept in sync. +// +//===----------------------------------------------------------------------===// + +#include "orc-rt/rtti.h" + +namespace orc_rt { + +char RTTIRoot::ID = 0; +void RTTIRoot::anchor() {} + +} // namespace orc_rt diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt index 98c28c9664c2a..00751e07742c6 100644 --- a/orc-rt/unittests/CMakeLists.txt +++ b/orc-rt/unittests/CMakeLists.txt @@ -12,6 +12,9 @@ function(add_orc_rt_unittest test_dirname) endfunction() add_orc_rt_unittest(CoreTests + bitmask-enum-test.cpp + math-test.cpp + rtti-test.cpp span-test.cpp DISABLE_LLVM_LINK_LLVM_DYLIB ) diff --git a/orc-rt/unittests/bitmask-enum-test.cpp b/orc-rt/unittests/bitmask-enum-test.cpp new file mode 100644 index 0000000000000..3367a8d4b040e --- /dev/null +++ b/orc-rt/unittests/bitmask-enum-test.cpp @@ -0,0 +1,143 @@ +//===-- bitmask-enum-test.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 file is a part of the ORC runtime. +// +//===----------------------------------------------------------------------===// + +#include "orc-rt/bitmask-enum.h" +#include "gtest/gtest.h" + +#include +#include + +using namespace orc_rt; + +namespace { + +enum Flags { F0 = 0, F1 = 1, F2 = 2, F3 = 4, F4 = 8 }; + +} // namespace + +namespace orc_rt { +ORC_RT_DECLARE_ENUM_AS_BITMASK(Flags, F4); +} // namespace orc_rt + +static_assert(is_bitmask_enum::value != 0); +static_assert(largest_bitmask_enum_bit::value == Flags::F4); + +namespace { + +static_assert(is_bitmask_enum::value != 0); +static_assert(largest_bitmask_enum_bit::value == Flags::F4); + +TEST(BitmaskEnumTest, BitwiseOr) { + Flags f = F1 | F2; + EXPECT_EQ(3, f); + + f = f | F3; + EXPECT_EQ(7, f); +} + +TEST(BitmaskEnumTest, BitwiseOrEquals) { + Flags f = F1; + f |= F3; + EXPECT_EQ(5, f); + + // |= should return a reference to the LHS. + f = F2; + (f |= F3) = F1; + EXPECT_EQ(F1, f); +} + +TEST(BitmaskEnumTest, BitwiseAnd) { + Flags f = static_cast(3) & F2; + EXPECT_EQ(F2, f); + + f = (f | F3) & (F1 | F2 | F3); + EXPECT_EQ(6, f); +} + +TEST(BitmaskEnumTest, BitwiseAndEquals) { + Flags f = F1 | F2 | F3; + f &= F1 | F2; + EXPECT_EQ(3, f); + + // &= should return a reference to the LHS. + (f &= F1) = F3; + EXPECT_EQ(F3, f); +} + +TEST(BitmaskEnumTest, BitwiseXor) { + Flags f = (F1 | F2) ^ (F2 | F3); + EXPECT_EQ(5, f); + + f = f ^ F1; + EXPECT_EQ(4, f); +} + +TEST(BitmaskEnumTest, BitwiseXorEquals) { + Flags f = (F1 | F2); + f ^= (F2 | F4); + EXPECT_EQ(9, f); + + // ^= should return a reference to the LHS. + (f ^= F4) = F3; + EXPECT_EQ(F3, f); +} + +TEST(BitmaskEnumTest, ConstantExpression) { + constexpr Flags f1 = ~F1; + constexpr Flags f2 = F1 | F2; + constexpr Flags f3 = F1 & F2; + constexpr Flags f4 = F1 ^ F2; + EXPECT_EQ(f1, ~F1); + EXPECT_EQ(f2, F1 | F2); + EXPECT_EQ(f3, F1 & F2); + EXPECT_EQ(f4, F1 ^ F2); +} + +TEST(BitmaskEnumTest, BitwiseNot) { + Flags f = ~F1; + EXPECT_EQ(14, f); // Largest value for f is 15. + EXPECT_EQ(15, ~F0); +} + +enum class FlagsClass { + F0 = 0, + F1 = 1, + F2 = 2, + F3 = 4, + ORC_RT_MARK_AS_BITMASK_ENUM(F3) +}; + +TEST(BitmaskEnumTest, ScopedEnum) { + FlagsClass f = (FlagsClass::F1 & ~FlagsClass::F0) | FlagsClass::F2; + f |= FlagsClass::F3; + EXPECT_EQ(7, static_cast(f)); +} + +struct Container { + enum Flags { + F0 = 0, + F1 = 1, + F2 = 2, + F3 = 4, + ORC_RT_MARK_AS_BITMASK_ENUM(F3) + }; + + static Flags getFlags() { + Flags f = F0 | F1; + f |= F2; + return f; + } +}; + +TEST(BitmaskEnumTest, EnumInStruct) { EXPECT_EQ(3, Container::getFlags()); } + +} // namespace diff --git a/orc-rt/unittests/math-test.cpp b/orc-rt/unittests/math-test.cpp new file mode 100644 index 0000000000000..491bbd8baa6ee --- /dev/null +++ b/orc-rt/unittests/math-test.cpp @@ -0,0 +1,78 @@ +//===- math-test.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 +// +//===----------------------------------------------------------------------===// +// +// Tests for orc-rt's math.h APIs. +// +//===----------------------------------------------------------------------===// + +#include "orc-rt/math.h" +#include "gtest/gtest.h" + +using namespace orc_rt; + +TEST(STLExtrasTest, isPowerOf2) { + // Test [0..16] + EXPECT_FALSE(isPowerOf2(0x00)); + EXPECT_TRUE(isPowerOf2(0x01)); + EXPECT_TRUE(isPowerOf2(0x02)); + EXPECT_FALSE(isPowerOf2(0x03)); + EXPECT_TRUE(isPowerOf2(0x04)); + EXPECT_FALSE(isPowerOf2(0x05)); + EXPECT_FALSE(isPowerOf2(0x06)); + EXPECT_FALSE(isPowerOf2(0x07)); + EXPECT_TRUE(isPowerOf2(0x08)); + EXPECT_FALSE(isPowerOf2(0x09)); + EXPECT_FALSE(isPowerOf2(0x0A)); + EXPECT_FALSE(isPowerOf2(0x0B)); + EXPECT_FALSE(isPowerOf2(0x0C)); + EXPECT_FALSE(isPowerOf2(0x0D)); + EXPECT_FALSE(isPowerOf2(0x0E)); + EXPECT_FALSE(isPowerOf2(0x0F)); + EXPECT_TRUE(isPowerOf2(0x10)); + + // Test some higher powers of two and their adjacent values. + EXPECT_FALSE(isPowerOf2(0x1F)); + EXPECT_TRUE(isPowerOf2(0x20)); + EXPECT_FALSE(isPowerOf2(0x21)); + + EXPECT_FALSE(isPowerOf2(0x3F)); + EXPECT_TRUE(isPowerOf2(0x40)); + EXPECT_FALSE(isPowerOf2(0x41)); + + EXPECT_FALSE(isPowerOf2(0x7F)); + EXPECT_TRUE(isPowerOf2(0x80)); + EXPECT_FALSE(isPowerOf2(0x81)); + + // Test larger values. + EXPECT_FALSE(isPowerOf2(0x3fffffff)); + EXPECT_TRUE(isPowerOf2(0x40000000)); + EXPECT_FALSE(isPowerOf2(0x40000001)); + + // Test negatives. + EXPECT_FALSE(isPowerOf2(-1)); +} + +TEST(STLExtrasTest, nextPowerOf2) { + EXPECT_EQ(nextPowerOf2(0x00), (1 << 0)); + EXPECT_EQ(nextPowerOf2(0x01), (1 << 1)); + EXPECT_EQ(nextPowerOf2(0x02), (1 << 2)); + EXPECT_EQ(nextPowerOf2(0x03), (1 << 2)); + EXPECT_EQ(nextPowerOf2(0x04), (1 << 3)); + EXPECT_EQ(nextPowerOf2(0x05), (1 << 3)); + EXPECT_EQ(nextPowerOf2(0x06), (1 << 3)); + EXPECT_EQ(nextPowerOf2(0x07), (1 << 3)); + EXPECT_EQ(nextPowerOf2(0x08), (1 << 4)); + EXPECT_EQ(nextPowerOf2(0x09), (1 << 4)); + EXPECT_EQ(nextPowerOf2(0x0a), (1 << 4)); + EXPECT_EQ(nextPowerOf2(0x0b), (1 << 4)); + EXPECT_EQ(nextPowerOf2(0x0c), (1 << 4)); + EXPECT_EQ(nextPowerOf2(0x0d), (1 << 4)); + EXPECT_EQ(nextPowerOf2(0x0e), (1 << 4)); + EXPECT_EQ(nextPowerOf2(0x0f), (1 << 4)); + EXPECT_EQ(nextPowerOf2(0x10), (1 << 5)); +} diff --git a/orc-rt/unittests/rtti-test.cpp b/orc-rt/unittests/rtti-test.cpp new file mode 100644 index 0000000000000..bb9fac6447010 --- /dev/null +++ b/orc-rt/unittests/rtti-test.cpp @@ -0,0 +1,52 @@ +//===-- rtti_test.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 +// +//===----------------------------------------------------------------------===// +// +// Note: +// This unit test was adapted from +// llvm/unittests/Support/ExtensibleRTTITest.cpp +// +//===----------------------------------------------------------------------===// + +#include "orc-rt/rtti.h" +#include "gtest/gtest.h" + +using namespace orc_rt; + +namespace { + +class MyBase : public RTTIExtends {}; + +class MyDerivedA : public RTTIExtends {}; + +class MyDerivedB : public RTTIExtends {}; + +} // end anonymous namespace + +TEST(ExtensibleRTTITest, BaseCheck) { + MyBase MB; + MyDerivedA MDA; + MyDerivedB MDB; + + // Check MB properties. + EXPECT_TRUE(isa(MB)); + EXPECT_TRUE(isa(MB)); + EXPECT_FALSE(isa(MB)); + EXPECT_FALSE(isa(MB)); + + // Check MDA properties. + EXPECT_TRUE(isa(MDA)); + EXPECT_TRUE(isa(MDA)); + EXPECT_TRUE(isa(MDA)); + EXPECT_FALSE(isa(MDA)); + + // Check MDB properties. + EXPECT_TRUE(isa(MDB)); + EXPECT_TRUE(isa(MDB)); + EXPECT_FALSE(isa(MDB)); + EXPECT_TRUE(isa(MDB)); +}