From 3d50a5b204aa3e5c3268e931472eb1698ef067e6 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 21 Feb 2025 13:25:16 -0800 Subject: [PATCH 1/2] Implement exact reference types Introduced in the Custom RTTs proposal, exact reference types are referencdes to a particular heap type and not any of its subtypes. The exception is that exact references to the bottom type of each heap type hierarchy are subtypes of exact references to any other type in the hierarchy. This maintains the property that each hierarchy of reference types is a lattice. Implement, construction, equality, hashing, subtyping, LUB, and GLB operations for exact reference types, but do not yet use them in the IR or implement parsing for them. --- src/wasm-type.h | 19 ++- src/wasm/wasm-type.cpp | 110 ++++++++++---- test/gtest/type-builder.cpp | 276 ++++++++++++++++++++++++++++++++++++ 3 files changed, 372 insertions(+), 33 deletions(-) diff --git a/src/wasm-type.h b/src/wasm-type.h index 9e3419190d3..f09e1e440fe 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -62,6 +62,7 @@ using Tuple = TypeList; enum Nullability { NonNullable, Nullable }; enum Mutability { Immutable, Mutable }; +enum Exactness { Inexact, Exact }; // HeapType name information used for printing. struct TypeNames { @@ -314,10 +315,10 @@ class Type { // Construct from a heap type description. Also covers construction from // Signature, Struct or Array via implicit conversion to HeapType. - Type(HeapType heapType, Nullability nullable) - : Type(heapType.getID() | (nullable == Nullable ? NullMask : 0)) { - assert(heapType.isBasic() || - !(heapType.getID() & (TupleMask | NullMask | ExactMask))); + Type(HeapType heapType, Nullability nullable, Exactness exact = Inexact) + : Type(heapType.getID() | (nullable == Nullable ? NullMask : 0) | + (exact == Exact ? ExactMask : 0)) { + assert(!(heapType.getID() & (TupleMask | NullMask | ExactMask))); } // Predicates @@ -366,6 +367,8 @@ class Type { bool isRef() const { return !isBasic() && !(id & TupleMask); } bool isNullable() const { return isRef() && (id & NullMask); } bool isNonNullable() const { return isRef() && !(id & NullMask); } + bool isExact() const { return isRef() && (id & ExactMask); } + bool isInexact() const { return isRef() && !(id & ExactMask); } HeapType getHeapType() const { assert(isRef()); return HeapType(id & ~(NullMask | ExactMask)); @@ -390,6 +393,10 @@ class Type { Nullability getNullability() const { return isNullable() ? Nullable : NonNullable; } + Exactness getExactness() const { + assert(isRef()); + return isExact() ? Exact : Inexact; + } private: template bool hasPredicate() { @@ -755,7 +762,9 @@ struct TypeBuilder { // TypeBuilder's HeapTypes. For Ref types, the HeapType may be a temporary // HeapType owned by this builder or a canonical HeapType. Type getTempTupleType(const Tuple&); - Type getTempRefType(HeapType heapType, Nullability nullable); + Type getTempRefType(HeapType heapType, + Nullability nullable, + Exactness exact = Inexact); // Declare the HeapType being built at index `i` to be an immediate subtype of // the given HeapType. diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 8710a0619d5..d35d1537c47 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -776,11 +776,19 @@ Type Type::getLeastUpperBound(Type a, Type b) { return Type(elems); } if (a.isRef() && b.isRef()) { - if (auto heapType = - HeapType::getLeastUpperBound(a.getHeapType(), b.getHeapType())) { + auto heapTypeA = a.getHeapType(); + auto heapTypeB = b.getHeapType(); + if (auto heapType = HeapType::getLeastUpperBound(heapTypeA, heapTypeB)) { auto nullability = (a.isNullable() || b.isNullable()) ? Nullable : NonNullable; - return Type(*heapType, nullability); + auto exactness = (a.isInexact() || b.isInexact()) ? Inexact : Exact; + // The LUB can only be exact if the heap types are the same or one of them + // is bottom. + if (heapTypeA != heapTypeB && !heapTypeA.isBottom() && + !heapTypeB.isBottom()) { + exactness = Inexact; + } + return Type(*heapType, nullability, exactness); } } return Type::none; @@ -814,6 +822,7 @@ Type Type::getGreatestLowerBound(Type a, Type b) { } auto nullability = (a.isNonNullable() || b.isNonNullable()) ? NonNullable : Nullable; + auto exactness = (a.isExact() || b.isExact()) ? Exact : Inexact; HeapType heapType; if (HeapType::isSubType(heapA, heapB)) { heapType = heapA; @@ -822,7 +831,13 @@ Type Type::getGreatestLowerBound(Type a, Type b) { } else { heapType = heapA.getBottom(); } - return Type(heapType, nullability); + // If one of the types is exact, but the GLB heap type is different than its + // heap type, then we must make the GLB heap type bottom. + if ((a.isExact() && heapType != heapA) || + (b.isExact() && heapType != heapB)) { + heapType = heapA.getBottom(); + } + return Type(heapType, nullability, exactness); } const Type& Type::Iterator::operator*() const { @@ -1432,14 +1447,24 @@ bool SubTyper::isSubType(Type a, Type b) { if (a == Type::unreachable) { return true; } - if (a.isRef() && b.isRef()) { - return (a.isNullable() == b.isNullable() || !a.isNullable()) && - isSubType(a.getHeapType(), b.getHeapType()); - } if (a.isTuple() && b.isTuple()) { return isSubType(a.getTuple(), b.getTuple()); } - return false; + if (!a.isRef() || !b.isRef()) { + return false; + } + if (a.isNullable() && !b.isNullable()) { + return false; + } + if (a.isInexact() && !b.isInexact()) { + return false; + } + auto heapTypeA = a.getHeapType(); + auto heapTypeB = b.getHeapType(); + if (b.isExact() && !heapTypeA.isBottom()) { + return heapTypeA == heapTypeB; + } + return isSubType(heapTypeA, heapTypeB); } bool SubTyper::isSubType(HeapType a, HeapType b) { @@ -1586,44 +1611,69 @@ std::ostream& TypePrinter::print(Type type) { } else if (type.isRef()) { auto heapType = type.getHeapType(); if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) { + if (type.isExact()) { + os << "(exact "; + } // Print shorthands for certain basic heap types. switch (heapType.getBasic(Unshared)) { case HeapType::ext: - return os << "externref"; + os << "externref"; + break; case HeapType::func: - return os << "funcref"; + os << "funcref"; + break; case HeapType::cont: - return os << "contref"; + os << "contref"; + break; case HeapType::any: - return os << "anyref"; + os << "anyref"; + break; case HeapType::eq: - return os << "eqref"; + os << "eqref"; + break; case HeapType::i31: - return os << "i31ref"; + os << "i31ref"; + break; case HeapType::struct_: - return os << "structref"; + os << "structref"; + break; case HeapType::array: - return os << "arrayref"; + os << "arrayref"; + break; case HeapType::exn: - return os << "exnref"; + os << "exnref"; + break; case HeapType::string: - return os << "stringref"; + os << "stringref"; + break; case HeapType::none: - return os << "nullref"; + os << "nullref"; + break; case HeapType::noext: - return os << "nullexternref"; + os << "nullexternref"; + break; case HeapType::nofunc: - return os << "nullfuncref"; + os << "nullfuncref"; + break; case HeapType::nocont: - return os << "nullcontref"; + os << "nullcontref"; + break; case HeapType::noexn: - return os << "nullexnref"; + os << "nullexnref"; + break; } + if (type.isExact()) { + os << ')'; + } + return os; } os << "(ref "; if (type.isNullable()) { os << "null "; } + if (type.isExact()) { + os << "exact "; + } printHeapTypeName(heapType); os << ')'; } else { @@ -1851,8 +1901,9 @@ size_t RecGroupHasher::hash(Type type) const { return digest; } assert(type.isRef()); - rehash(digest, type.getNullability()); - rehash(digest, hash(type.getHeapType())); + wasm::rehash(digest, type.getNullability()); + wasm::rehash(digest, type.getExactness()); + hash_combine(digest, hash(type.getHeapType())); return digest; } @@ -1974,6 +2025,7 @@ bool RecGroupEquator::eq(Type a, Type b) const { } if (a.isRef() && b.isRef()) { return a.getNullability() == b.getNullability() && + a.getExactness() == b.getExactness() && eq(a.getHeapType(), b.getHeapType()); } return false; @@ -2164,8 +2216,10 @@ Type TypeBuilder::getTempTupleType(const Tuple& tuple) { return impl->tupleStore.insert(tuple); } -Type TypeBuilder::getTempRefType(HeapType type, Nullability nullable) { - return Type(type, nullable); +Type TypeBuilder::getTempRefType(HeapType type, + Nullability nullable, + Exactness exact) { + return Type(type, nullable, exact); } void TypeBuilder::setSubType(size_t i, std::optional super) { diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index ac95d0e11b4..9f8239c5761 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -428,6 +428,32 @@ TEST_F(TypeTest, CanonicalizeUses) { EXPECT_NE(built[4], built[6]); } +TEST_F(TypeTest, CanonicalizeExactRefs) { + TypeBuilder builder(4); + + // Types that vary in exactness or nullability of references are different. + Type a = builder.getTempRefType(builder[0], Nullable, Inexact); + Type b = builder.getTempRefType(builder[1], NonNullable, Inexact); + Type c = builder.getTempRefType(builder[2], Nullable, Exact); + Type d = builder.getTempRefType(builder[3], NonNullable, Exact); + + builder[0] = Struct({Field(a, Mutable)}); + builder[1] = Struct({Field(b, Mutable)}); + builder[2] = Struct({Field(c, Mutable)}); + builder[3] = Struct({Field(d, Mutable)}); + + auto result = builder.build(); + ASSERT_TRUE(result); + auto built = *result; + + EXPECT_NE(built[0], built[1]); + EXPECT_NE(built[0], built[2]); + EXPECT_NE(built[0], built[3]); + EXPECT_NE(built[1], built[2]); + EXPECT_NE(built[1], built[3]); + EXPECT_NE(built[2], built[3]); +} + TEST_F(TypeTest, CanonicalizeSelfReferences) { TypeBuilder builder(5); // Single self-reference @@ -1095,6 +1121,256 @@ FUZZ_TEST(TypeFuzzTest, TestHeapTypeRelationsFuzz) #endif // FUZZTEST +TEST_F(TypeTest, TestTypeRelations) { + Type any = Type(HeapType::any, NonNullable, Inexact); + Type nullAny = Type(HeapType::any, Nullable, Inexact); + Type exactAny = Type(HeapType::any, NonNullable, Exact); + Type nullExactAny = Type(HeapType::any, Nullable, Exact); + + HeapType defined = Struct(); + Type def = Type(defined, NonNullable, Inexact); + Type nullDef = Type(defined, Nullable, Inexact); + Type exactDef = Type(defined, NonNullable, Exact); + Type nullExactDef = Type(defined, Nullable, Exact); + + Type none = Type(HeapType::none, NonNullable, Inexact); + Type nullNone = Type(HeapType::none, Nullable, Inexact); + Type exactNone = Type(HeapType::none, NonNullable, Exact); + Type nullExactNone = Type(HeapType::none, Nullable, Exact); + + Type func = Type(HeapType::func, NonNullable, Inexact); + Type nullFunc = Type(HeapType::func, Nullable, Inexact); + Type exactFunc = Type(HeapType::func, NonNullable, Exact); + Type nullExactFunc = Type(HeapType::func, Nullable, Exact); + + Type i32 = Type::i32; + Type unreachable = Type::unreachable; + +#define assertLUB(a, b, lub, glb) \ + { \ + auto lub1 = Type::getLeastUpperBound(a, b); \ + auto lub2 = Type::getLeastUpperBound(b, a); \ + EXPECT_EQ(lub, lub1); \ + EXPECT_EQ(lub1, lub2); \ + if (lub != Type::none) { \ + EXPECT_TRUE(Type::isSubType(a, lub)); \ + EXPECT_TRUE(Type::isSubType(b, lub)); \ + } \ + auto glb1 = Type::getGreatestLowerBound(a, b); \ + auto glb2 = Type::getGreatestLowerBound(b, a); \ + EXPECT_EQ(glb, glb1); \ + EXPECT_EQ(glb, glb2); \ + EXPECT_TRUE(Type::isSubType(glb, a)); \ + EXPECT_TRUE(Type::isSubType(glb, b)); \ + if (a == b) { \ + EXPECT_TRUE(Type::isSubType(a, b)); \ + EXPECT_TRUE(Type::isSubType(b, a)); \ + EXPECT_EQ(lub, a); \ + EXPECT_EQ(glb, a); \ + } else if (lub == b) { \ + EXPECT_TRUE(Type::isSubType(a, b)); \ + EXPECT_FALSE(Type::isSubType(b, a)); \ + EXPECT_EQ(glb, a); \ + } else if (lub == a) { \ + EXPECT_FALSE(Type::isSubType(a, b)); \ + EXPECT_TRUE(Type::isSubType(b, a)); \ + EXPECT_EQ(glb, b); \ + } else if (lub != Type::none) { \ + EXPECT_FALSE(Type::isSubType(a, b)); \ + EXPECT_FALSE(Type::isSubType(b, a)); \ + EXPECT_NE(glb, a); \ + EXPECT_NE(glb, b); \ + } else { \ + EXPECT_FALSE(Type::isSubType(a, b)); \ + EXPECT_FALSE(Type::isSubType(b, a)); \ + } \ + \ + if (a.isRef() && b.isRef()) { \ + auto htA = a.getHeapType(); \ + auto htB = b.getHeapType(); \ + \ + if (a == b) { \ + EXPECT_EQ(htA.getTop(), htB.getTop()); \ + EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ + } else if (lub == b) { \ + EXPECT_EQ(htA.getTop(), htB.getTop()); \ + EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ + } else if (lub == a) { \ + EXPECT_EQ(htA.getTop(), htB.getTop()); \ + EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ + } else if (lub != Type::none) { \ + EXPECT_EQ(htA.getTop(), htB.getTop()); \ + EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ + } else { \ + EXPECT_NE(htA.getTop(), htB.getTop()); \ + EXPECT_NE(htA.getBottom(), htB.getBottom()); \ + } \ + } \ + } + + assertLUB(any, any, any, any); + assertLUB(any, nullAny, nullAny, any); + assertLUB(any, exactAny, any, exactAny); + assertLUB(any, nullExactAny, nullAny, exactAny); + assertLUB(any, def, any, def); + assertLUB(any, nullDef, nullAny, def); + assertLUB(any, exactDef, any, exactDef); + assertLUB(any, nullExactDef, nullAny, exactDef); + assertLUB(any, none, any, none); + assertLUB(any, nullNone, nullAny, none); + assertLUB(any, exactNone, any, exactNone); + assertLUB(any, nullExactNone, nullAny, exactNone); + assertLUB(any, func, Type(Type::none), unreachable); + assertLUB(any, nullFunc, Type(Type::none), unreachable); + assertLUB(any, exactFunc, Type(Type::none), unreachable); + assertLUB(any, nullExactFunc, Type(Type::none), unreachable); + assertLUB(any, i32, Type(Type::none), unreachable); + assertLUB(any, unreachable, any, unreachable); + + assertLUB(nullAny, nullAny, nullAny, nullAny); + assertLUB(nullAny, exactAny, nullAny, exactAny); + assertLUB(nullAny, nullExactAny, nullAny, nullExactAny); + assertLUB(nullAny, def, nullAny, def); + assertLUB(nullAny, nullDef, nullAny, nullDef); + assertLUB(nullAny, exactDef, nullAny, exactDef); + assertLUB(nullAny, nullExactDef, nullAny, nullExactDef); + assertLUB(nullAny, none, nullAny, none); + assertLUB(nullAny, nullNone, nullAny, nullNone); + assertLUB(nullAny, exactNone, nullAny, exactNone); + assertLUB(nullAny, nullExactNone, nullAny, nullExactNone); + assertLUB(nullAny, func, Type(Type::none), unreachable); + assertLUB(nullAny, nullFunc, Type(Type::none), unreachable); + assertLUB(nullAny, exactFunc, Type(Type::none), unreachable); + assertLUB(nullAny, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullAny, i32, Type(Type::none), unreachable); + assertLUB(nullAny, unreachable, nullAny, unreachable); + + assertLUB(exactAny, exactAny, exactAny, exactAny); + assertLUB(exactAny, nullExactAny, nullExactAny, exactAny); + assertLUB(exactAny, def, any, exactNone); + assertLUB(exactAny, nullDef, nullAny, exactNone); + assertLUB(exactAny, exactDef, any, exactNone); + assertLUB(exactAny, nullExactDef, nullAny, exactNone); + assertLUB(exactAny, none, any, exactNone); + assertLUB(exactAny, nullNone, nullAny, exactNone); + assertLUB(exactAny, exactNone, exactAny, exactNone); + assertLUB(exactAny, nullExactNone, nullExactAny, exactNone); + assertLUB(exactAny, func, Type(Type::none), unreachable); + assertLUB(exactAny, nullFunc, Type(Type::none), unreachable); + assertLUB(exactAny, exactFunc, Type(Type::none), unreachable); + assertLUB(exactAny, nullExactFunc, Type(Type::none), unreachable); + assertLUB(exactAny, i32, Type(Type::none), unreachable); + assertLUB(exactAny, unreachable, exactAny, unreachable); + + assertLUB(nullExactAny, nullExactAny, nullExactAny, nullExactAny); + assertLUB(nullExactAny, def, nullAny, exactNone); + assertLUB(nullExactAny, nullDef, nullAny, nullExactNone); + assertLUB(nullExactAny, exactDef, nullAny, exactNone); + assertLUB(nullExactAny, nullExactDef, nullAny, nullExactNone); + assertLUB(nullExactAny, none, nullAny, exactNone); + assertLUB(nullExactAny, nullNone, nullAny, nullExactNone); + assertLUB(nullExactAny, exactNone, nullExactAny, exactNone); + assertLUB(nullExactAny, nullExactNone, nullExactAny, nullExactNone); + assertLUB(nullExactAny, func, Type(Type::none), unreachable); + assertLUB(nullExactAny, nullFunc, Type(Type::none), unreachable); + assertLUB(nullExactAny, exactFunc, Type(Type::none), unreachable); + assertLUB(nullExactAny, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullExactAny, i32, Type(Type::none), unreachable); + assertLUB(nullExactAny, unreachable, nullExactAny, unreachable); + + assertLUB(def, def, def, def); + assertLUB(def, nullDef, nullDef, def); + assertLUB(def, exactDef, def, exactDef); + assertLUB(def, nullExactDef, nullDef, exactDef); + assertLUB(def, none, def, none); + assertLUB(def, nullNone, nullDef, none); + assertLUB(def, exactNone, def, exactNone); + assertLUB(def, nullExactNone, nullDef, exactNone); + assertLUB(def, func, Type(Type::none), unreachable); + assertLUB(def, nullFunc, Type(Type::none), unreachable); + assertLUB(def, exactFunc, Type(Type::none), unreachable); + assertLUB(def, nullExactFunc, Type(Type::none), unreachable); + assertLUB(def, i32, Type(Type::none), unreachable); + assertLUB(def, unreachable, def, unreachable); + + assertLUB(nullDef, nullDef, nullDef, nullDef); + assertLUB(nullDef, exactDef, nullDef, exactDef); + assertLUB(nullDef, nullExactDef, nullDef, nullExactDef); + assertLUB(nullDef, none, nullDef, none); + assertLUB(nullDef, nullNone, nullDef, nullNone); + assertLUB(nullDef, exactNone, nullDef, exactNone); + assertLUB(nullDef, nullExactNone, nullDef, nullExactNone); + assertLUB(nullDef, func, Type(Type::none), unreachable); + assertLUB(nullDef, nullFunc, Type(Type::none), unreachable); + assertLUB(nullDef, exactFunc, Type(Type::none), unreachable); + assertLUB(nullDef, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullDef, i32, Type(Type::none), unreachable); + assertLUB(nullDef, unreachable, nullDef, unreachable); + + assertLUB(exactDef, exactDef, exactDef, exactDef); + assertLUB(exactDef, nullExactDef, nullExactDef, exactDef); + assertLUB(exactDef, none, def, exactNone); + assertLUB(exactDef, nullNone, nullDef, exactNone); + assertLUB(exactDef, exactNone, exactDef, exactNone); + assertLUB(exactDef, nullExactNone, nullExactDef, exactNone); + assertLUB(exactDef, func, Type(Type::none), unreachable); + assertLUB(exactDef, nullFunc, Type(Type::none), unreachable); + assertLUB(exactDef, exactFunc, Type(Type::none), unreachable); + assertLUB(exactDef, nullExactFunc, Type(Type::none), unreachable); + assertLUB(exactDef, i32, Type(Type::none), unreachable); + assertLUB(exactDef, unreachable, exactDef, unreachable); + + assertLUB(nullExactDef, nullExactDef, nullExactDef, nullExactDef); + assertLUB(nullExactDef, none, nullDef, exactNone); + assertLUB(nullExactDef, nullNone, nullDef, nullExactNone); + assertLUB(nullExactDef, exactNone, nullExactDef, exactNone); + assertLUB(nullExactDef, nullExactNone, nullExactDef, nullExactNone); + assertLUB(nullExactDef, func, Type(Type::none), unreachable); + assertLUB(nullExactDef, nullFunc, Type(Type::none), unreachable); + assertLUB(nullExactDef, exactFunc, Type(Type::none), unreachable); + assertLUB(nullExactDef, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullExactDef, i32, Type(Type::none), unreachable); + assertLUB(nullExactDef, unreachable, nullExactDef, unreachable); + + assertLUB(none, none, none, none); + assertLUB(none, nullNone, nullNone, none); + assertLUB(none, exactNone, none, exactNone); + assertLUB(none, nullExactNone, nullNone, exactNone); + assertLUB(none, func, Type(Type::none), unreachable); + assertLUB(none, nullFunc, Type(Type::none), unreachable); + assertLUB(none, exactFunc, Type(Type::none), unreachable); + assertLUB(none, nullExactFunc, Type(Type::none), unreachable); + assertLUB(none, i32, Type(Type::none), unreachable); + assertLUB(none, unreachable, none, unreachable); + + assertLUB(nullNone, nullNone, nullNone, nullNone); + assertLUB(nullNone, exactNone, nullNone, exactNone); + assertLUB(nullNone, nullExactNone, nullNone, nullExactNone); + assertLUB(nullNone, func, Type(Type::none), unreachable); + assertLUB(nullNone, nullFunc, Type(Type::none), unreachable); + assertLUB(nullNone, exactFunc, Type(Type::none), unreachable); + assertLUB(nullNone, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullNone, i32, Type(Type::none), unreachable); + assertLUB(nullNone, unreachable, nullNone, unreachable); + + assertLUB(exactNone, exactNone, exactNone, exactNone); + assertLUB(exactNone, nullExactNone, nullExactNone, exactNone); + assertLUB(exactNone, func, Type(Type::none), unreachable); + assertLUB(exactNone, nullFunc, Type(Type::none), unreachable); + assertLUB(exactNone, exactFunc, Type(Type::none), unreachable); + assertLUB(exactNone, nullExactFunc, Type(Type::none), unreachable); + assertLUB(exactNone, i32, Type(Type::none), unreachable); + assertLUB(exactNone, unreachable, exactNone, unreachable); + + assertLUB(nullExactNone, nullExactNone, nullExactNone, nullExactNone); + assertLUB(nullExactNone, func, Type(Type::none), unreachable); + assertLUB(nullExactNone, nullFunc, Type(Type::none), unreachable); + assertLUB(nullExactNone, exactFunc, Type(Type::none), unreachable); + assertLUB(nullExactNone, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullExactNone, i32, Type(Type::none), unreachable); + assertLUB(nullExactNone, unreachable, nullExactNone, unreachable); +} + TEST_F(TypeTest, TestSubtypeErrors) { Type anyref = Type(HeapType::any, Nullable); Type eqref = Type(HeapType::eq, Nullable); From 0a74f9a0338c2bafecb0fd7949ea43a1e9f1f90a Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 26 Feb 2025 11:48:10 -0800 Subject: [PATCH 2/2] deduplicate checks --- test/gtest/type-builder.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index 9f8239c5761..feb3b69c652 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -1189,21 +1189,12 @@ TEST_F(TypeTest, TestTypeRelations) { auto htA = a.getHeapType(); \ auto htB = b.getHeapType(); \ \ - if (a == b) { \ - EXPECT_EQ(htA.getTop(), htB.getTop()); \ - EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ - } else if (lub == b) { \ - EXPECT_EQ(htA.getTop(), htB.getTop()); \ - EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ - } else if (lub == a) { \ - EXPECT_EQ(htA.getTop(), htB.getTop()); \ - EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ - } else if (lub != Type::none) { \ - EXPECT_EQ(htA.getTop(), htB.getTop()); \ - EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ - } else { \ + if (lub == Type::none) { \ EXPECT_NE(htA.getTop(), htB.getTop()); \ EXPECT_NE(htA.getBottom(), htB.getBottom()); \ + } else { \ + EXPECT_EQ(htA.getTop(), htB.getTop()); \ + EXPECT_EQ(htA.getBottom(), htB.getBottom()); \ } \ } \ }