blob: 5260749b31aaed173615fca1a8aa547cf3ec44c8 [file]
// Copyright 2019 The IREE Authors
//
// Licensed 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
#include "iree/vm/ref.h"
#include <cstddef>
#include "iree/base/api.h"
#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.h"
#include "iree/vm/instance.h"
#include "iree/vm/ref.h"
namespace {
using InstancePtr =
std::unique_ptr<iree_vm_instance_t, decltype(&iree_vm_instance_release)>;
static InstancePtr MakeInstance() {
iree_vm_instance_t* instance = NULL;
IREE_CHECK_OK(iree_vm_instance_create(IREE_VM_TYPE_CAPACITY_DEFAULT,
iree_allocator_system(), &instance));
return InstancePtr(instance, iree_vm_instance_release);
}
class A : public iree::vm::RefObject<A> {
public:
static iree_vm_ref_type_t kTypeID;
int data() const { return data_; }
private:
int data_ = 1;
};
iree_vm_ref_type_t A::kTypeID = IREE_VM_REF_TYPE_NULL;
class B : public iree::vm::RefObject<B> {
public:
static iree_vm_ref_type_t kTypeID;
int data() const { return data_; }
private:
int data_ = 2;
};
iree_vm_ref_type_t B::kTypeID = IREE_VM_REF_TYPE_NULL;
struct ref_object_c_t {
iree_vm_ref_object_t ref_object = {1};
int data = 1;
};
template <typename T>
static iree_vm_ref_t MakeRef(InstancePtr& instance, const char* type_name) {
// Safe to do multiple times, so we do it to ensure the tests don't care what
// order they run in/don't need to preregister types.
static iree_vm_ref_type_descriptor_t descriptor = {0};
static iree_vm_ref_type_t registration = 0;
descriptor.type_name = iree_make_cstring_view(type_name);
descriptor.offsetof_counter = T::offsetof_counter();
descriptor.destroy = T::DirectDestroy;
IREE_CHECK_OK(iree_vm_instance_register_type(instance.get(), &descriptor,
&registration));
T::kTypeID = registration;
iree_vm_ref_t ref = {0};
IREE_CHECK_OK(iree_vm_ref_wrap_assign(new T(), T::kTypeID, &ref));
return ref;
}
// WARNING: this is an implementation detail and must never be relied on - it's
// only here to test the expected behavior.
static int32_t ReadCounter(iree_vm_ref_t* ref) {
return iree_atomic_load((iree_atomic_ref_count_t*)ref->ptr +
(ref->type & IREE_VM_REF_TYPE_TAG_BIT_MASK),
iree_memory_order_seq_cst);
}
} // namespace
IREE_VM_DECLARE_TYPE_ADAPTERS(ref_object_c, ref_object_c_t);
IREE_VM_DEFINE_TYPE_ADAPTERS(ref_object_c, ref_object_c_t);
namespace {
static void RegisterTypeC(InstancePtr& instance) {
static iree_vm_ref_type_descriptor_t descriptor = {0};
descriptor.type_name = iree_make_cstring_view("CType");
descriptor.offsetof_counter = offsetof(ref_object_c_t, ref_object.counter) /
IREE_VM_REF_COUNTER_ALIGNMENT;
descriptor.destroy =
+[](void* ptr) { delete reinterpret_cast<ref_object_c_t*>(ptr); };
IREE_CHECK_OK(iree_vm_instance_register_type(instance.get(), &descriptor,
&ref_object_c_registration));
}
// Tests type registration and lookup.
TEST(VMRefTest, TypeRegistration) {
auto instance = MakeInstance();
RegisterTypeC(instance);
ASSERT_NE(0, iree_vm_instance_lookup_type(instance.get(),
iree_make_cstring_view("CType")));
ASSERT_EQ(
0, iree_vm_instance_lookup_type(
instance.get(), iree_make_cstring_view("asodjfaoisdjfaoisdfj")));
}
// Tests wrapping a simple C struct.
TEST(VMRefTest, WrappingCStruct) {
auto instance = MakeInstance();
RegisterTypeC(instance);
iree_vm_ref_t ref = {0};
IREE_EXPECT_OK(iree_vm_ref_wrap_assign(new ref_object_c_t(),
ref_object_c_registration, &ref));
EXPECT_EQ(1, ReadCounter(&ref));
iree_vm_ref_release(&ref);
}
// Tests wrapping a C++ RefObject with a vtable.
TEST(VMRefTest, WrappingSubclassedRefObject) {
struct BaseType : public iree::vm::RefObject<BaseType> {
virtual ~BaseType() = default;
virtual int DoSomething() = 0;
};
static int allocated_derived_types = 0;
struct DerivedType : public BaseType {
DerivedType() { ++allocated_derived_types; }
~DerivedType() override { --allocated_derived_types; }
int DoSomething() override { return 123 + allocated_derived_types; }
};
auto instance = MakeInstance();
static iree_vm_ref_type_descriptor_t descriptor = {0};
static iree_vm_ref_type_t registration = 0;
descriptor.type_name = iree_make_cstring_view("BaseType");
descriptor.offsetof_counter = BaseType::offsetof_counter();
descriptor.destroy = BaseType::DirectDestroy;
IREE_ASSERT_OK(iree_vm_instance_register_type(instance.get(), &descriptor,
&registration));
allocated_derived_types = 0;
iree_vm_ref_t ref = {0};
IREE_EXPECT_OK(
iree_vm_ref_wrap_assign(new DerivedType(), registration, &ref));
EXPECT_EQ(1, ReadCounter(&ref));
EXPECT_EQ(1, allocated_derived_types);
EXPECT_EQ(123 + 1, reinterpret_cast<BaseType*>(ref.ptr)->DoSomething());
iree_vm_ref_release(&ref);
EXPECT_EQ(0, allocated_derived_types);
}
// Tests that wrapping releases any existing ref in out_ref.
TEST(VMRefTest, WrappingReleasesExisting) {
auto instance = MakeInstance();
RegisterTypeC(instance);
iree_vm_ref_t ref = {0};
iree_vm_ref_wrap_assign(new ref_object_c_t(), ref_object_c_registration,
&ref);
EXPECT_EQ(1, ReadCounter(&ref));
iree_vm_ref_release(&ref);
}
// Tests that wrapping releases any existing ref in out_ref.
TEST(VMRefTest, WrappingRetainExisting) {
auto instance = MakeInstance();
RegisterTypeC(instance);
iree_vm_ref_t ref = {0};
iree_vm_ref_wrap_retain(new ref_object_c_t(), ref_object_c_registration,
&ref);
EXPECT_EQ(2, ReadCounter(&ref));
iree_vm_ref_t ref_t = ref;
iree_vm_ref_release(&ref);
iree_vm_ref_release(&ref_t);
}
// Tests that wrapping with the existing ref in out_ref being the same does not
// change the counter (as wrap_retain is a retain+release).
TEST(VMRefTest, WrappingRetainExistingSame) {
auto instance = MakeInstance();
RegisterTypeC(instance);
iree_vm_ref_t ref = {0};
iree_vm_ref_wrap_assign(new ref_object_c_t(), ref_object_c_registration,
&ref);
EXPECT_EQ(1, ReadCounter(&ref));
iree_vm_ref_wrap_retain(ref.ptr, ref_object_c_registration, &ref);
EXPECT_EQ(1, ReadCounter(&ref));
iree_vm_ref_release(&ref);
}
// Checking null refs is fine.
TEST(VMRefTest, CheckNull) {
iree_vm_ref_t null_ref = {0};
IREE_EXPECT_OK(iree_vm_ref_check(null_ref, IREE_VM_REF_TYPE_NULL));
iree_status_t status =
iree_vm_ref_check(null_ref, static_cast<iree_vm_ref_type_t>(1234));
IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, status);
iree_status_free(status);
}
// Tests type checks.
TEST(VMRefTest, Check) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
IREE_EXPECT_OK(iree_vm_ref_check(a_ref, A::kTypeID));
iree_status_t status = iree_vm_ref_check(a_ref, B::kTypeID);
IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, status);
iree_status_free(status);
iree_vm_ref_release(&a_ref);
}
// Tests retaining a null ref does nothing.
TEST(VMRefTest, RetainNull) {
iree_vm_ref_t null_ref_0 = {0};
iree_vm_ref_t null_ref_1 = {0};
iree_vm_ref_retain(&null_ref_0, &null_ref_1);
}
// Tests that retaining into itself is a no-op.
TEST(VMRefTest, RetainIntoSelf) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
EXPECT_EQ(1, ReadCounter(&a_ref));
iree_vm_ref_retain(&a_ref, &a_ref);
EXPECT_EQ(1, ReadCounter(&a_ref));
iree_vm_ref_release(&a_ref);
}
// Tests that retaining into out_ref releases the existing contents.
TEST(VMRefTest, RetainReleasesExisting) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_t b_ref = MakeRef<B>(instance, "BType");
iree_vm_ref_retain(&a_ref, &b_ref);
EXPECT_EQ(1, iree_vm_ref_equal(&a_ref, &b_ref));
EXPECT_EQ(2, ReadCounter(&a_ref));
iree_vm_ref_release(&a_ref);
iree_vm_ref_release(&b_ref);
}
// Tests that null refs are always fine.
TEST(VMRefTest, RetainCheckedNull) {
iree_vm_ref_t null_ref_0 = {0};
iree_vm_ref_t null_ref_1 = {0};
IREE_EXPECT_OK(
iree_vm_ref_retain_checked(&null_ref_0, A::kTypeID, &null_ref_1));
}
// Tests that types are verified and retains fail if types don't match.
TEST(VMRefTest, RetainChecked) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = {0};
IREE_EXPECT_OK(iree_vm_ref_retain_checked(&a_ref_0, A::kTypeID, &a_ref_1));
iree_vm_ref_release(&a_ref_0);
iree_vm_ref_release(&a_ref_1);
}
// Tests that working with null refs is fine.
TEST(VMRefTest, RetainOrMoveNull) {
iree_vm_ref_t null_ref_0 = {0};
iree_vm_ref_t null_ref_1 = {0};
iree_vm_ref_retain_or_move(/*is_move=*/0, &null_ref_0, &null_ref_1);
iree_vm_ref_retain_or_move(/*is_move=*/1, &null_ref_0, &null_ref_1);
}
// Tests that is_move=false increments the ref count.
TEST(VMRefTest, RetainOrMoveRetaining) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = {0};
iree_vm_ref_retain_or_move(/*is_move=*/0, &a_ref_0, &a_ref_1);
EXPECT_EQ(1, iree_vm_ref_equal(&a_ref_0, &a_ref_1));
EXPECT_EQ(2, ReadCounter(&a_ref_0));
iree_vm_ref_release(&a_ref_0);
iree_vm_ref_release(&a_ref_1);
}
// Tests that is_move=true does not increment the ref count.
TEST(VMRefTest, RetainOrMoveMoving) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = {0};
iree_vm_ref_retain_or_move(/*is_move=*/1, &a_ref_0, &a_ref_1);
IREE_EXPECT_OK(iree_vm_ref_check(a_ref_0, IREE_VM_REF_TYPE_NULL));
iree_vm_ref_release(&a_ref_1);
}
// Tests that retaining into itself just increments the ref count.
TEST(VMRefTest, RetainOrMoveRetainingIntoSelf) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
EXPECT_EQ(1, ReadCounter(&a_ref));
iree_vm_ref_retain_or_move(/*is_move=*/0, &a_ref, &a_ref);
EXPECT_EQ(1, ReadCounter(&a_ref));
iree_vm_ref_release(&a_ref);
}
// Tests that moving into itself is a no-op.
TEST(VMRefTest, RetainOrMoveMovingIntoSelf) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_retain_or_move(/*is_move=*/1, &a_ref, &a_ref);
IREE_EXPECT_OK(iree_vm_ref_check(a_ref, A::kTypeID));
iree_vm_ref_release(&a_ref);
}
// Tests that retaining into out_ref releases the existing contents.
TEST(VMRefTest, RetainOrMoveRetainingReleasesExisting) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_t b_ref = MakeRef<B>(instance, "BType");
iree_vm_ref_retain_or_move(/*is_move=*/0, &a_ref, &b_ref);
EXPECT_EQ(1, iree_vm_ref_equal(&a_ref, &b_ref));
EXPECT_EQ(2, ReadCounter(&a_ref));
iree_vm_ref_release(&a_ref);
iree_vm_ref_release(&b_ref);
}
// Tests that moving into out_ref releases the existing contents.
TEST(VMRefTest, RetainOrMoveMovingReleasesExisting) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_t b_ref = MakeRef<B>(instance, "BType");
iree_vm_ref_retain_or_move(/*is_move=*/1, &a_ref, &b_ref);
EXPECT_EQ(0, iree_vm_ref_equal(&a_ref, &b_ref));
EXPECT_EQ(1, ReadCounter(&b_ref));
iree_vm_ref_release(&b_ref);
}
// Tests that null refs are always fine.
TEST(VMRefTest, RetainOrMoveCheckedNull) {
iree_vm_ref_t null_ref_0 = {0};
iree_vm_ref_t null_ref_1 = {0};
IREE_EXPECT_OK(iree_vm_ref_retain_or_move_checked(
/*is_move=*/0, &null_ref_0, A::kTypeID, &null_ref_1));
IREE_EXPECT_OK(iree_vm_ref_retain_or_move_checked(
/*is_move=*/1, &null_ref_0, A::kTypeID, &null_ref_1));
}
// Tests that retains/moves work when types match.
TEST(VMRefTest, RetainOrMoveCheckedMatch) {
auto instance = MakeInstance();
// Retain.
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = {0};
IREE_EXPECT_OK(iree_vm_ref_retain_or_move_checked(
/*is_move=*/0, &a_ref_0, A::kTypeID, &a_ref_1));
EXPECT_EQ(1, iree_vm_ref_equal(&a_ref_0, &a_ref_1));
EXPECT_EQ(2, ReadCounter(&a_ref_0));
iree_vm_ref_release(&a_ref_0);
iree_vm_ref_release(&a_ref_1);
// Move.
iree_vm_ref_t b_ref_0 = MakeRef<B>(instance, "BType");
iree_vm_ref_t b_ref_1 = {0};
IREE_EXPECT_OK(iree_vm_ref_retain_or_move_checked(
/*is_move=*/1, &b_ref_0, B::kTypeID, &b_ref_1));
EXPECT_EQ(0, iree_vm_ref_equal(&b_ref_0, &b_ref_1));
EXPECT_EQ(1, ReadCounter(&b_ref_1));
iree_vm_ref_release(&b_ref_1);
}
// Tests that types are verified and retains/moves fail if types don't match.
TEST(VMRefTest, RetainOrMoveCheckedMismatch) {
auto instance = MakeInstance();
// Retain.
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = {0};
iree_status_t status = iree_vm_ref_retain_or_move_checked(
/*is_move=*/0, &a_ref_0, B::kTypeID, &a_ref_1);
IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, status);
iree_status_free(status);
EXPECT_EQ(0, iree_vm_ref_equal(&a_ref_0, &a_ref_1));
EXPECT_EQ(1, ReadCounter(&a_ref_0));
iree_vm_ref_release(&a_ref_0);
// Move.
iree_vm_ref_t b_ref_0 = MakeRef<B>(instance, "BType");
iree_vm_ref_t b_ref_1 = {0};
status = iree_vm_ref_retain_or_move_checked(
/*is_move=*/1, &b_ref_0, A::kTypeID, &b_ref_1);
IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, status);
iree_status_free(status);
EXPECT_EQ(1, ReadCounter(&b_ref_0));
iree_vm_ref_release(&b_ref_0);
}
// Tests that existing references are released when being overwritten.
TEST(VMRefTest, RetainOrMoveCheckedReleasesExistingNull) {
auto instance = MakeInstance();
iree_vm_ref_t null_ref = {0};
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
IREE_EXPECT_OK(iree_vm_ref_retain_or_move_checked(
/*is_move=*/0, &null_ref, A::kTypeID, &a_ref));
}
// Tests that existing references are released when being overwritten.
TEST(VMRefTest, RetainOrMoveCheckedReleasesExisting) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = MakeRef<A>(instance, "AType");
IREE_EXPECT_OK(iree_vm_ref_retain_or_move_checked(
/*is_move=*/1, &a_ref_0, A::kTypeID, &a_ref_1));
iree_vm_ref_release(&a_ref_1);
}
// Checks that assigning null refs is fine.
TEST(VMRefTest, AssignNull) {
iree_vm_ref_t null_ref_0 = {0};
iree_vm_ref_t null_ref_1 = {0};
iree_vm_ref_assign(&null_ref_0, &null_ref_1);
}
// Tests that assigning does not reset the source ref nor inc the ref count.
TEST(VMRefTest, Assign) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = {0};
iree_vm_ref_assign(&a_ref_0, &a_ref_1);
EXPECT_EQ(1, iree_vm_ref_equal(&a_ref_0, &a_ref_1));
EXPECT_EQ(1, ReadCounter(&a_ref_0));
iree_vm_ref_release(&a_ref_0);
}
// Tests that assigning into itself is a no-op.
TEST(VMRefTest, AssignSelf) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_assign(&a_ref, &a_ref);
EXPECT_EQ(1, ReadCounter(&a_ref));
iree_vm_ref_release(&a_ref);
}
// Tests that assigning into out_ref releases the existing contents.
TEST(VMRefTest, AssignReleasesExisting) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_t b_ref = MakeRef<B>(instance, "BType");
iree_vm_ref_assign(&a_ref, &b_ref);
EXPECT_EQ(1, iree_vm_ref_equal(&a_ref, &b_ref));
EXPECT_EQ(1, ReadCounter(&a_ref));
iree_vm_ref_release(&a_ref);
// NOTE: do not release b - it was just assigned!
}
// Checks that moving null refs is fine.
TEST(VMRefTest, MovingNull) {
iree_vm_ref_t null_ref_0 = {0};
iree_vm_ref_t null_ref_1 = {0};
iree_vm_ref_move(&null_ref_0, &null_ref_1);
}
// Tests that moving resets the source ref.
TEST(VMRefTest, MovingResetsSource) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = {0};
iree_vm_ref_move(&a_ref_0, &a_ref_1);
IREE_EXPECT_OK(iree_vm_ref_check(a_ref_0, IREE_VM_REF_TYPE_NULL));
iree_vm_ref_release(&a_ref_1);
}
// Tests that moving into itself is a no-op.
TEST(VMRefTest, MovingIntoSelf) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_move(&a_ref, &a_ref);
IREE_EXPECT_OK(iree_vm_ref_check(a_ref, A::kTypeID));
iree_vm_ref_release(&a_ref);
}
// Tests that moving into out_ref releases the existing contents.
TEST(VMRefTest, MovingReleasesExisting) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref_0 = MakeRef<A>(instance, "AType");
iree_vm_ref_t a_ref_1 = MakeRef<A>(instance, "AType");
iree_vm_ref_move(&a_ref_0, &a_ref_1);
iree_vm_ref_release(&a_ref_1);
}
// Null references should always be equal.
TEST(VMRefTest, EqualityNull) {
iree_vm_ref_t null_ref_0 = {0};
iree_vm_ref_t null_ref_1 = {0};
EXPECT_EQ(1, iree_vm_ref_equal(&null_ref_0, &null_ref_0));
EXPECT_EQ(1, iree_vm_ref_equal(&null_ref_0, &null_ref_1));
EXPECT_EQ(1, iree_vm_ref_equal(&null_ref_1, &null_ref_0));
}
// Tests comparing with self and against null.
TEST(VMRefTest, EqualitySelfOrNull) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_t null_ref = {0};
EXPECT_EQ(1, iree_vm_ref_equal(&a_ref, &a_ref));
EXPECT_EQ(0, iree_vm_ref_equal(&a_ref, &null_ref));
EXPECT_EQ(0, iree_vm_ref_equal(&null_ref, &a_ref));
iree_vm_ref_release(&a_ref);
}
// Tests comparing between different types.
TEST(VMRefTest, EqualityDifferentTypes) {
auto instance = MakeInstance();
iree_vm_ref_t a_ref = MakeRef<A>(instance, "AType");
iree_vm_ref_t b_ref = MakeRef<B>(instance, "BType");
EXPECT_EQ(0, iree_vm_ref_equal(&a_ref, &b_ref));
EXPECT_EQ(0, iree_vm_ref_equal(&b_ref, &a_ref));
iree_vm_ref_release(&b_ref);
iree_vm_ref_release(&a_ref);
}
// Tests that in-place assignment of vm::ref when used with C create functions
// properly tracks both the pointer and the iree_vm_ref_type_t.
TEST(VMRefTest, InPlaceAssignment) {
auto instance = MakeInstance();
RegisterTypeC(instance);
auto create = [&](ref_object_c_t** out_object) {
*out_object = new ref_object_c_t();
};
iree::vm::ref<ref_object_c_t> ref;
EXPECT_FALSE(ref);
EXPECT_EQ(nullptr, ref.get());
EXPECT_EQ(IREE_VM_REF_TYPE_NULL, ref.type());
create(&ref);
EXPECT_TRUE(ref);
EXPECT_NE(nullptr, ref.get());
EXPECT_EQ(ref_object_c_type(), ref.type());
ref.reset();
EXPECT_FALSE(ref);
EXPECT_EQ(nullptr, ref.get());
EXPECT_EQ(IREE_VM_REF_TYPE_NULL, ref.type());
}
} // namespace
struct ref_object_d_t {
iree_vm_ref_object_t ref_object = {1};
int data = 1;
};
IREE_VM_DECLARE_TYPE_ADAPTERS(ref_object_d, ref_object_d_t);
IREE_VM_DEFINE_TYPE_ADAPTERS(ref_object_d, ref_object_d_t);
namespace {
// Tests that C++ ref<T> instances can be initialized even if the type is not
// yet registered. This happens if whatever C++ object/scope owns the instance
// has fields that are initialized prior to the instance/module loading.
TEST(VMRefTest, UnregisteredType) {
iree::vm::ref<ref_object_d_t> null_ref;
EXPECT_FALSE(null_ref);
EXPECT_EQ(nullptr, null_ref.get());
EXPECT_EQ(nullptr, null_ref.release());
EXPECT_EQ(IREE_VM_REF_TYPE_NULL, null_ref.type());
null_ref.reset(); // don't die
auto retained_ref = iree::vm::retain_ref<ref_object_d_t>(nullptr);
EXPECT_FALSE(retained_ref);
auto assigned_ref = iree::vm::assign_ref<ref_object_d_t>(nullptr);
EXPECT_FALSE(assigned_ref);
}
} // namespace