// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/wasm/canonical-types.h" #include "src/wasm/wasm-subtyping.h" #include "test/common/flag-utils.h" #include "test/common/wasm/flag-utils.h" #include "test/unittests/test-utils.h" namespace v8::internal::wasm::subtyping_unittest { class WasmSubtypingTest : public TestWithPlatform {}; using FieldInit = std::pair; using Idx = ModuleTypeIndex; constexpr bool kShared = true; constexpr ValueType refS(uint32_t index, bool shared = kNotShared) { return ValueType::Ref(Idx{index}, shared, RefTypeKind::kStruct); } constexpr ValueType refA(uint32_t index, bool shared = kNotShared) { return ValueType::Ref(Idx{index}, shared, RefTypeKind::kArray); } constexpr ValueType refF(uint32_t index, bool shared = kNotShared) { return ValueType::Ref(Idx{index}, shared, RefTypeKind::kFunction); } constexpr ValueType refC(uint32_t index, bool shared = kNotShared) { return ValueType::Ref(Idx{index}, shared, RefTypeKind::kCont); } constexpr ValueType refNullS(uint32_t index, bool shared = kNotShared) { return ValueType::RefNull(Idx{index}, shared, RefTypeKind::kStruct); } constexpr ValueType refNullA(uint32_t index, bool shared = kNotShared) { return ValueType::RefNull(Idx{index}, shared, RefTypeKind::kArray); } constexpr ValueType refNullF(uint32_t index, bool shared = kNotShared) { return ValueType::RefNull(Idx{index}, shared, RefTypeKind::kFunction); } constexpr ValueType refNullC(uint32_t index, bool shared = kNotShared) { return ValueType::RefNull(Idx{index}, shared, RefTypeKind::kCont); } FieldInit mut(ValueType type) { return FieldInit(type, true); } FieldInit immut(ValueType type) { return FieldInit(type, false); } void DefineStruct(WasmModule* module, std::initializer_list fields, ModuleTypeIndex supertype = kNoSuperType, bool is_final = false, bool is_shared = false, bool in_singleton_rec_group = true) { StructType::Builder builder(&module->signature_zone, static_cast(fields.size()), false); for (FieldInit field : fields) { builder.AddField(field.first, field.second); } module->AddStructTypeForTesting(builder.Build(), supertype, is_final, is_shared); if (in_singleton_rec_group) { GetTypeCanonicalizer()->AddRecursiveSingletonGroup(module); } } void DefineArray(WasmModule* module, FieldInit element_type, ModuleTypeIndex supertype = kNoSuperType, bool is_final = false, bool is_shared = false, bool in_singleton_rec_group = true) { module->AddArrayTypeForTesting(module->signature_zone.New( element_type.first, element_type.second), supertype, is_final, is_shared); if (in_singleton_rec_group) { GetTypeCanonicalizer()->AddRecursiveSingletonGroup(module); } } void DefineSignature(WasmModule* module, std::initializer_list params, std::initializer_list returns, ModuleTypeIndex supertype = kNoSuperType, bool is_final = false, bool is_shared = false, bool in_singleton_rec_group = true) { module->AddSignatureForTesting( FunctionSig::Build(&module->signature_zone, returns, params), supertype, is_final, is_shared); if (in_singleton_rec_group) { GetTypeCanonicalizer()->AddRecursiveGroup(module, 1); } } void DefineCont(WasmModule* module, ModuleTypeIndex cont, ModuleTypeIndex supertype = kNoSuperType, bool is_final = false, bool is_shared = false) { module->AddContTypeForTesting(module->signature_zone.New(cont), supertype, is_final, is_shared); } TEST_F(WasmSubtypingTest, Subtyping) { v8::internal::AccountingAllocator allocator; WasmModule module1_; WasmModule module2_; WasmModule* module1 = &module1_; WasmModule* module2 = &module2_; // Set up two identical modules. for (WasmModule* module : {module1, module2}) { // Three mutually recursive types. /* 0 */ DefineStruct(module, {mut(refA(2)), immut(refNullA(2))}, kNoSuperType, false, false, false); /* 1 */ DefineStruct(module, {mut(refA(2)), immut(refA(2))}, Idx{0}, false, false, false); /* 2 */ DefineArray(module, immut(refS(0)), kNoSuperType, false, false, false); GetTypeCanonicalizer()->AddRecursiveGroup(module, 3); /* 3 */ DefineArray(module, immut(refS(1)), Idx{2}); /* 4 */ DefineStruct( module, {mut(refA(2)), immut(refA(3)), immut(kWasmF64)}, Idx{1}); /* 5 */ DefineStruct(module, {mut(refNullA(2)), immut(refA(2))}); /* 6 */ DefineArray(module, mut(kWasmI32)); /* 7 */ DefineArray(module, immut(kWasmI32)); /* 8 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(8))}); /* 9 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(8))}, Idx{8}); /* 10 */ DefineSignature(module, {}, {}); /* 11 */ DefineSignature(module, {kWasmI32}, {kWasmI32}); /* 12 */ DefineSignature(module, {kWasmI32, kWasmI32}, {kWasmI32}); /* 13 */ DefineSignature(module, {refS(1)}, {kWasmI32}); /* 14 */ DefineSignature(module, {refS(0)}, {kWasmI32}, Idx{13}); /* 15 */ DefineSignature(module, {refS(0)}, {refS(0)}); /* 16 */ DefineSignature(module, {refS(0)}, {refS(4)}, Idx{15}); /* 17 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(17))}); // Rec. group. /* 18 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(17))}, Idx{17}, false, false, false); /* 19 */ DefineArray(module, {mut(refNullF(21))}, kNoSuperType, false, false, false); /* 20 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType, false, false, false); /* 21 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, Idx{20}, false, false, false); GetTypeCanonicalizer()->AddRecursiveGroup(module, 4); // Identical rec. group. /* 22 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(17))}, Idx{17}, false, false, false); /* 23 */ DefineArray(module, {mut(refNullF(25))}, kNoSuperType, false, false, false); /* 24 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType, false, false, false); /* 25 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, Idx{24}, false, false, false); GetTypeCanonicalizer()->AddRecursiveGroup(module, 4); // Nonidentical rec. group: the last function extends a type outside the // recursive group. /* 26 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(17))}, Idx{17}, false, false, false); /* 27 */ DefineArray(module, {mut(refNullF(29))}, kNoSuperType, false, false, false); /* 28 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType, false, false, false); /* 29 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, Idx{20}, false, false, false); GetTypeCanonicalizer()->AddRecursiveGroup(module, 4); /* 30 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(18))}, Idx{18}); /* 31 */ DefineStruct( module, {mut(refA(2)), immut(refNullA(2)), immut(kWasmS128)}, Idx{1}); // Final types /* 32 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, true); /* 33 */ DefineStruct(module, {mut(kWasmI32), mut(kWasmI64)}, Idx{32}, true); /* 34 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, true); /* 35 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, false); // Shared types. /* 36 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType); /* 37 */ DefineStruct(module, {mut(kWasmI32), mut(kWasmI64)}, Idx{36}); /* 38 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, false, true); /* 39 */ DefineStruct(module, {mut(kWasmI32), mut(kWasmI64)}, Idx{38}, false, true); /* 40 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, false, true); /* 41 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType, false, true, true); // Continuation types (switching group) /* 42 */ DefineSignature(module, {kWasmI32}, {refNullC(45)}, kNoSuperType, false, false, false); /* 43 */ DefineSignature(module, {refNullC(44)}, {kWasmI32}, kNoSuperType, false, false, false); /* 44 */ DefineCont(module, ModuleTypeIndex{42}); /* 45 */ DefineCont(module, ModuleTypeIndex{43}); GetTypeCanonicalizer()->AddRecursiveGroup(module, 4); // Continuation types, functions outside the group /* 46 */ DefineCont(module, ModuleTypeIndex{42}); /* 47 */ DefineCont(module, ModuleTypeIndex{43}, Idx{45}); GetTypeCanonicalizer()->AddRecursiveGroup(module, 2); } constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64, kWasmS128}; constexpr ValueType ref_types[] = { kWasmFuncRef, kWasmEqRef, kWasmStructRef, kWasmArrayRef, kWasmI31Ref, kWasmAnyRef, kWasmExternRef, kWasmNullExternRef, kWasmNullRef, kWasmNullFuncRef, kWasmStringRef, kWasmStringViewIter, kWasmExnRef, kWasmNullExnRef, kWasmRefNullExternString, kWasmContRef, kWasmNullContRef, refNullS(0), // struct refS(0), // struct refNullA(2), // array refA(2), // array refNullF(11), // function refF(11), // function refNullC(44), // continuation refC(44) // continuation }; // Some macros to help managing types and modules. #define SUBTYPE(type1, type2) \ EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module)) #define SUBTYPE_IFF(type1, type2, condition) \ EXPECT_EQ(IsSubtypeOf(type1, type2, module1, module), condition) #define NOT_SUBTYPE(type1, type2) \ EXPECT_FALSE(IsSubtypeOf(type1, type2, module1, module)) // Use only with indexed types. #define VALID_SUBTYPE(type1, type2) \ EXPECT_TRUE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \ module1, module)); \ EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module)); #define NOT_VALID_SUBTYPE(type1, type2) \ EXPECT_FALSE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \ module1, module)); #define IDENTICAL(kind, index1, index2) \ EXPECT_TRUE(EquivalentTypes(refNull##kind(index1), refNull##kind(index2), \ module1, module)); #define DISTINCT(kind, index1, index2) \ EXPECT_FALSE(EquivalentTypes(refNull##kind(index1), refNull##kind(index2), \ module1, module)); #define DISTINCT_SHARED(kind, index1, shared1, index2, shared2) \ EXPECT_FALSE(EquivalentTypes(refNull##kind(index1, shared1), \ refNull##kind(index2, shared2), module1, \ module)); // For union and intersection, we have a version that also checks the module, // and one that does not. #define UNION(type1, type2, type_result) \ EXPECT_EQ(Union(type1, type2, module1, module).type, type_result) #define UNION_M(type1, type2, type_result, module_result) \ EXPECT_EQ(Union(type1, type2, module1, module), \ TypeInModule(type_result, module_result)) #define INTERSECTION(type1, type2, type_result) \ EXPECT_EQ(Intersection(type1, type2, module1, module).type, type_result) #define INTERSECTION_M(type1, type2, type_result, module_result) \ EXPECT_EQ(Intersection(type1, type2, module1, module), \ TypeInModule(type_result, module_result)) for (WasmModule* module : {module1, module2}) { // Type judgements across modules should work the same as within one module. // Value types are unrelated, except if they are equal. for (ValueType subtype : numeric_types) { for (ValueType supertype : numeric_types) { SUBTYPE_IFF(subtype, supertype, subtype == supertype); } } // Value types are unrelated with reference types. for (ValueType value_type : numeric_types) { for (ValueType ref_type : ref_types) { NOT_SUBTYPE(value_type, ref_type); NOT_SUBTYPE(ref_type, value_type); } } for (ValueType ref_type : ref_types) { const bool is_extern = ref_type == kWasmExternRef || ref_type == kWasmNullExternRef || ref_type == kWasmRefNullExternString; const bool is_any_func = ref_type == kWasmFuncRef || ref_type == kWasmNullFuncRef || ref_type == refNullF(11) || ref_type == refF(11); const bool is_string_view = ref_type == kWasmStringViewIter || ref_type == kWasmStringViewWtf8 || ref_type == kWasmStringViewWtf16; const bool is_any_cont = ref_type == kWasmContRef || ref_type == kWasmNullContRef || ref_type == refNullC(44) || ref_type == refC(44); const bool is_exn = ref_type == kWasmExnRef || ref_type == kWasmNullExnRef; SCOPED_TRACE("ref_type: " + ref_type.name()); // Concrete reference types, i31ref, structref and arrayref are subtypes // of eqref, externref/funcref/anyref/exnref/functions are not. SUBTYPE_IFF(ref_type, kWasmEqRef, ref_type != kWasmAnyRef && !is_any_func && !is_extern && !is_string_view && ref_type != kWasmStringRef && !is_exn && !is_any_cont); // Struct types are subtypes of structref. SUBTYPE_IFF(ref_type, kWasmStructRef, ref_type == kWasmStructRef || ref_type == kWasmNullRef || ref_type == refS(0) || ref_type == refNullS(0)); // Array types are subtypes of arrayref. SUBTYPE_IFF(ref_type, kWasmArrayRef, ref_type == kWasmArrayRef || ref_type == refA(2) || ref_type == kWasmNullRef || ref_type == refNullA(2)); // Functions are subtypes of funcref. SUBTYPE_IFF(ref_type, kWasmFuncRef, is_any_func); // Each reference type is a subtype of itself. SUBTYPE(ref_type, ref_type); // Each non-func, non-extern, non-string-view, non-string-iter reference // type is a subtype of anyref. SUBTYPE_IFF(ref_type, kWasmAnyRef, !is_any_func && !is_extern && !is_string_view && !is_exn && !is_any_cont); // Only anyref is a subtype of anyref. SUBTYPE_IFF(kWasmAnyRef, ref_type, ref_type == kWasmAnyRef); // Only externref and nullexternref are subtypes of externref. SUBTYPE_IFF(ref_type, kWasmExternRef, is_extern); // Only nullexternref is a subtype of nullexternref. SUBTYPE_IFF(ref_type, kWasmNullExternRef, ref_type == kWasmNullExternRef); // Each nullable non-func, non-extern reference type is a supertype of // nullref. SUBTYPE_IFF(kWasmNullRef, ref_type, ref_type.is_nullable() && !is_any_func && !is_extern && !is_exn && !is_any_cont); // Only nullref is a subtype of nullref. SUBTYPE_IFF(ref_type, kWasmNullRef, ref_type == kWasmNullRef); // Only nullable funcs are supertypes of nofunc. SUBTYPE_IFF(kWasmNullFuncRef, ref_type, ref_type.is_nullable() && is_any_func); // Only nullfuncref is a subtype of nullfuncref. SUBTYPE_IFF(ref_type, kWasmNullFuncRef, ref_type == kWasmNullFuncRef); // Make sure symmetric relations are symmetric. for (ValueType ref_type2 : ref_types) { if (ref_type == ref_type2) { EXPECT_TRUE(EquivalentTypes(ref_type, ref_type2, module, module1)); EXPECT_TRUE(EquivalentTypes(ref_type2, ref_type, module1, module)); } else { EXPECT_FALSE(EquivalentTypes(ref_type, ref_type2, module, module1)); EXPECT_FALSE(EquivalentTypes(ref_type2, ref_type, module1, module)); } } } // The rest of ref. types are unrelated. for (ValueType type_1 : {kWasmFuncRef, kWasmI31Ref, kWasmArrayRef, kWasmExnRef}) { for (ValueType type_2 : {kWasmFuncRef, kWasmI31Ref, kWasmArrayRef, kWasmExnRef}) { SUBTYPE_IFF(type_1, type_2, type_1 == type_2); } } // Unrelated refs are unrelated. NOT_VALID_SUBTYPE(refS(0), refA(2)); NOT_VALID_SUBTYPE(refNullA(3), refNullS(1)); // ref is a subtype of ref null for the same struct/array. VALID_SUBTYPE(refS(0), refNullS(0)); VALID_SUBTYPE(refA(2), refNullA(2)); // ref null is not a subtype of ref for the same struct/array. NOT_SUBTYPE(refNullS(0), refS(0)); NOT_SUBTYPE(refNullA(2), refA(2)); // ref is a subtype of ref null if the same is true for the underlying // structs/arrays. VALID_SUBTYPE(refA(3), refNullA(2)); // Prefix subtyping for structs. VALID_SUBTYPE(refNullS(4), refNullS(0)); // Mutable fields are invariant. NOT_VALID_SUBTYPE(refS(0), refS(5)); // Immutable fields are covariant. VALID_SUBTYPE(refS(1), refS(0)); // Prefix subtyping + immutable field covariance for structs. VALID_SUBTYPE(refNullS(4), refNullS(1)); // No subtyping between mutable/immutable fields. NOT_VALID_SUBTYPE(refA(7), refA(6)); NOT_VALID_SUBTYPE(refA(6), refA(7)); // Recursive types. VALID_SUBTYPE(refS(9), refS(8)); // Function subtyping; // Unrelated function types are unrelated. NOT_VALID_SUBTYPE(refF(10), refF(11)); // Function type with different parameter counts are unrelated. NOT_VALID_SUBTYPE(refF(12), refF(11)); // Parameter contravariance holds. VALID_SUBTYPE(refF(14), refF(13)); // Return type covariance holds. VALID_SUBTYPE(refF(16), refF(15)); // Identical types are subtype-related. VALID_SUBTYPE(refF(10), refF(10)); VALID_SUBTYPE(refF(11), refF(11)); // Continuation subtyping: VALID_SUBTYPE(refC(44), refC(44)); NOT_VALID_SUBTYPE(refC(44), refC(45)); VALID_SUBTYPE(refC(45), refC(45)); NOT_VALID_SUBTYPE(refC(45), refC(44)); INTERSECTION(refF(11), refC(44), kWasmBottom); // Just checking ... INTERSECTION(refNullC(44), refNullC(45), kWasmNullContRef); INTERSECTION(refNullC(44), kWasmContRef, refNullC(44)); INTERSECTION(refC(44), kWasmContRef, refC(44)); INTERSECTION(refC(44), refC(45), kWasmBottom); INTERSECTION(refNullC(44), kWasmNullContRef, kWasmNullContRef); INTERSECTION(kWasmContRef, kWasmNullContRef, kWasmNullContRef); UNION(kWasmNullContRef, kWasmContRef, kWasmContRef); UNION(refNullC(44), kWasmContRef, kWasmContRef); UNION(refC(44), kWasmContRef, kWasmContRef); UNION(refNullC(44), refNullC(45), kWasmContRef); UNION(refC(44), refC(45), kWasmContRef.AsNonNull()); UNION(refNullC(44), kWasmNullContRef, refNullC(44)); UNION(kWasmContRef, kWasmNullContRef, kWasmContRef); // Canonicalization tests. // Groups should only be canonicalized to identical groups. IDENTICAL(S, 18, 22); IDENTICAL(A, 19, 23); IDENTICAL(F, 20, 24); IDENTICAL(F, 21, 25); DISTINCT(S, 18, 26); DISTINCT(A, 19, 27); DISTINCT(F, 20, 28); DISTINCT(F, 21, 29); // A type should not be canonicalized to an identical one with a different // group structure. DISTINCT(S, 18, 17); // A subtype should also be subtype of an equivalent type. VALID_SUBTYPE(refS(30), refS(18)); VALID_SUBTYPE(refS(30), refS(22)); NOT_SUBTYPE(refS(30), refS(26)); // Final types // A type is not a valid subtype of a final type. NOT_VALID_SUBTYPE(refS(33), refS(32)); IDENTICAL(S, 32, 34); // A final and a non-final type are distinct. DISTINCT(S, 32, 35); /* Shared types */ // A shared type can be a subtype of a shared type. VALID_SUBTYPE(refS(39, kShared), refS(38, kShared)); // A shared type is not a valid subtype of a non-shared type and vice versa. NOT_VALID_SUBTYPE(refS(39, kShared), refS(36)); NOT_VALID_SUBTYPE(refS(37), refS(38, kShared)); // Two shared types are identical. A shared and non-shared type are // distinct. IDENTICAL(S, 38, 40); DISTINCT_SHARED(S, 36, kNotShared, 38, kShared); // Abstract types. auto Gen = ValueType::Generic; using G = GenericKind; ValueType kRefAny = kWasmAnyRef.AsNonNull(); ValueType kRefAnyShared = Gen(G::kAny, kNonNullable, kShared); ValueType kRefEq = kWasmEqRef.AsNonNull(); ValueType kRefEqShared = Gen(G::kEq, kNonNullable, kShared); ValueType kRefI31Shared = Gen(G::kI31, kNonNullable, kShared); ValueType kRefStructShared = Gen(G::kStruct, kNonNullable, kShared); ValueType kRefArrayShared = Gen(G::kArray, kNonNullable, kShared); ValueType kRefNoneShared = Gen(G::kNone, kNonNullable, kShared); ValueType kRefFunc = kWasmFuncRef.AsNonNull(); ValueType kRefFuncShared = Gen(G::kFunc, kNonNullable, kShared); ValueType kRefNoFuncShared = Gen(G::kNoFunc, kNonNullable, kShared); ValueType kRefNoExternShared = Gen(G::kNoExtern, kNonNullable, kShared); ValueType kRefNullAnyShared = Gen(G::kAny, kNullable, kShared); ValueType kRefNullFuncShared = Gen(G::kFunc, kNullable, kShared); ValueType kRefNullEqShared = Gen(G::kEq, kNullable, kShared); ValueType kRefNullExternShared = Gen(G::kExtern, kNullable, kShared); ValueType kRefNullNoneShared = Gen(G::kNone, kNullable, kShared); ValueType kRefNullNoFuncShared = Gen(G::kNoFunc, kNullable, kShared); ValueType kRefNullI31Shared = Gen(G::kI31, kNullable, kShared); SUBTYPE(kRefEqShared, kRefAnyShared); NOT_SUBTYPE(kRefEqShared, kRefAny); NOT_SUBTYPE(kRefEq, kRefAnyShared); NOT_SUBTYPE(kRefFuncShared, kRefAnyShared); SUBTYPE(kRefNullNoneShared, kRefNullI31Shared); SUBTYPE(kRefNullNoFuncShared, kRefNullFuncShared); SUBTYPE(refS(40, kShared), kRefNullEqShared); SUBTYPE(kRefNullNoneShared, refNullS(40, kShared)); NOT_SUBTYPE(refS(40, kShared), kWasmEqRef); NOT_SUBTYPE(refS(40, kShared), kRefNullExternShared); SUBTYPE(refF(41, kShared), kRefNullFuncShared); SUBTYPE(kRefNullNoFuncShared, refNullF(41, kShared)); NOT_SUBTYPE(kRefNullNoFuncShared, refF(41, kShared)); NOT_SUBTYPE(refF(41, kShared), kRefNullAnyShared); NOT_SUBTYPE(refF(41, kShared), kWasmFuncRef); NOT_SUBTYPE(refS(0), kRefStructShared); NOT_SUBTYPE(refA(2), kRefArrayShared); NOT_SUBTYPE(refF(10), kRefFuncShared); // Unions and intersections. // Distinct numeric types are unrelated. for (ValueType type1 : numeric_types) { for (ValueType type2 : numeric_types) { UNION(type1, type2, (type1 == type2 ? type1 : kWasmTop)); INTERSECTION(type1, type2, (type1 == type2 ? type1 : kWasmBottom)); } } // Numeric and reference types are unrelated. for (ValueType type1 : numeric_types) { for (ValueType type2 : ref_types) { UNION(type1, type2, kWasmTop); INTERSECTION(type1, type2, kWasmBottom); } } // Reference type vs. itself and anyref. for (ValueType type : ref_types) { SCOPED_TRACE(type.name()); if (type == kWasmStringViewIter || type == kWasmStringViewWtf8 || type == kWasmStringViewWtf16) { // String views aren't subtypes of any nor supertypes of null. INTERSECTION(type, kWasmAnyRef, kWasmBottom); INTERSECTION(type, kWasmNullRef, kWasmBottom); } else if (type == kWasmFuncRef || type == kWasmNullFuncRef || type == refF(11) || type == refNullF(11) || type == kWasmExternRef || type == kWasmNullExternRef || type == kWasmRefNullExternString || type == kWasmContRef || type == kWasmNullContRef || type == refNullC(44) || type == refC(44)) { // func, cont and extern types don't share the same type hierarchy as // anyref. INTERSECTION(type, kWasmAnyRef, kWasmBottom); } else { bool is_exn = type == kWasmExnRef || type == kWasmNullExnRef; UNION(kWasmAnyRef, type, is_exn ? kWasmTop : kWasmAnyRef); INTERSECTION(kWasmAnyRef, type, is_exn ? kWasmBottom : type); UNION(kWasmAnyRef.AsNonNull(), type, is_exn ? kWasmTop : type.is_nullable() ? kWasmAnyRef : kWasmAnyRef.AsNonNull()); INTERSECTION(kWasmAnyRef.AsNonNull(), type, is_exn ? kWasmBottom : type != kWasmNullRef ? type.AsNonNull() : kWasmBottom); } } // Abstract types vs abstract types. UNION(kWasmEqRef, kWasmStructRef, kWasmEqRef); UNION(kWasmEqRef, kWasmI31Ref, kWasmEqRef); UNION(kWasmEqRef, kWasmArrayRef, kWasmEqRef); UNION(kWasmEqRef, kWasmNullRef, kWasmEqRef); UNION(kWasmStructRef, kWasmI31Ref, kWasmEqRef); UNION(kWasmStructRef, kWasmArrayRef, kWasmEqRef); UNION(kWasmStructRef, kWasmNullRef, kWasmStructRef); UNION(kWasmI31Ref.AsNonNull(), kWasmArrayRef.AsNonNull(), kWasmEqRef.AsNonNull()); UNION(kWasmI31Ref, kWasmNullRef, kWasmI31Ref); UNION(kWasmArrayRef, kWasmNullRef, kWasmArrayRef); UNION(kWasmStructRef.AsNonNull(), kWasmI31Ref.AsNonNull(), kWasmEqRef.AsNonNull()); UNION(kWasmI31Ref.AsNonNull(), kWasmArrayRef, kWasmEqRef); UNION(kWasmAnyRef, kWasmNullRef, kWasmAnyRef); UNION(kWasmExternRef, kWasmNullExternRef, kWasmExternRef); UNION(kWasmRefNullExternString, kWasmNullExternRef, kWasmRefNullExternString); UNION(kWasmRefNullExternString.AsNonNull(), kWasmNullExternRef, kWasmRefNullExternString); UNION(kWasmRefNullExternString, kWasmExternRef, kWasmExternRef); UNION(kWasmRefNullExternString, kWasmAnyRef, kWasmTop); UNION(kWasmRefNullExternString, kWasmFuncRef, kWasmTop); // Imported strings and stringref represent the same values. Still, they are // in different type hierarchies and therefore incompatible (e.g. due to // different null representation). // (There is no interoperability between stringref and imported strings as // they are competing proposals.) UNION(kWasmRefNullExternString, kWasmStringRef, kWasmTop); UNION(kWasmRefNullExternString.AsNonNull(), kWasmStringRef.AsNonNull(), kWasmTop); UNION(kWasmFuncRef, kWasmNullFuncRef, kWasmFuncRef); UNION(kWasmFuncRef, kWasmStructRef, kWasmTop); UNION(kWasmFuncRef, kWasmArrayRef, kWasmTop); UNION(kWasmFuncRef, kWasmAnyRef, kWasmTop); UNION(kWasmFuncRef, kWasmEqRef, kWasmTop); UNION(kWasmStringRef, kWasmAnyRef, kWasmAnyRef); UNION(kWasmStringRef, kWasmStructRef, kWasmAnyRef); UNION(kWasmStringRef, kWasmArrayRef, kWasmAnyRef); UNION(kWasmStringRef, kWasmFuncRef, kWasmTop); UNION(kWasmStringViewIter, kWasmStringRef, kWasmTop); UNION(kWasmStringViewWtf8, kWasmStringRef, kWasmTop); UNION(kWasmStringViewWtf16, kWasmStringRef, kWasmTop); UNION(kWasmStringViewIter, kWasmAnyRef, kWasmTop); UNION(kWasmStringViewWtf8, kWasmAnyRef, kWasmTop); UNION(kWasmStringViewWtf16, kWasmAnyRef, kWasmTop); UNION(kWasmNullFuncRef, kWasmEqRef, kWasmTop); INTERSECTION(kWasmExternRef, kWasmEqRef, kWasmBottom); INTERSECTION(kWasmExternRef, kWasmStructRef, kWasmBottom); INTERSECTION(kWasmExternRef, kWasmI31Ref.AsNonNull(), kWasmBottom); INTERSECTION(kWasmExternRef, kWasmArrayRef, kWasmBottom); INTERSECTION(kWasmExternRef, kWasmNullRef, kWasmBottom); INTERSECTION(kWasmExternRef, kWasmFuncRef, kWasmBottom); INTERSECTION(kWasmNullExternRef, kWasmEqRef, kWasmBottom); INTERSECTION(kWasmNullExternRef, kWasmStructRef, kWasmBottom); INTERSECTION(kWasmNullExternRef, kWasmI31Ref, kWasmBottom); INTERSECTION(kWasmNullExternRef, kWasmArrayRef, kWasmBottom); INTERSECTION(kWasmNullExternRef, kWasmNullRef, kWasmBottom); INTERSECTION(kWasmNullExternRef, kWasmExternRef, kWasmNullExternRef); INTERSECTION(kWasmNullExternRef, kWasmExternRef.AsNonNull(), kWasmBottom); INTERSECTION(kWasmRefNullExternString, kWasmEqRef, kWasmBottom); INTERSECTION(kWasmRefNullExternString, kWasmAnyRef, kWasmBottom); INTERSECTION(kWasmRefNullExternString, kWasmFuncRef.AsNonNull(), kWasmBottom); INTERSECTION(kWasmRefNullExternString, kWasmNullRef, kWasmBottom); INTERSECTION(kWasmRefNullExternString, kWasmNullExternRef, kWasmNullExternRef); INTERSECTION(kWasmRefNullExternString.AsNonNull(), kWasmNullExternRef, kWasmBottom); INTERSECTION(kWasmRefNullExternString, kWasmExternRef, kWasmRefNullExternString); INTERSECTION(kWasmRefNullExternString, kWasmExternRef.AsNonNull(), kWasmRefNullExternString.AsNonNull()); INTERSECTION(kWasmFuncRef, kWasmEqRef, kWasmBottom); INTERSECTION(kWasmFuncRef, kWasmStructRef, kWasmBottom); INTERSECTION(kWasmFuncRef, kWasmI31Ref.AsNonNull(), kWasmBottom); INTERSECTION(kWasmFuncRef, kWasmArrayRef, kWasmBottom); INTERSECTION(kWasmFuncRef, kWasmNullRef, kWasmBottom); INTERSECTION(kWasmFuncRef, kWasmNullExternRef, kWasmBottom); INTERSECTION(kWasmNullFuncRef, kWasmEqRef, kWasmBottom); INTERSECTION(kWasmNullFuncRef, kWasmStructRef, kWasmBottom); INTERSECTION(kWasmNullFuncRef, kWasmI31Ref, kWasmBottom); INTERSECTION(kWasmNullFuncRef, kWasmArrayRef, kWasmBottom); INTERSECTION(kWasmNullFuncRef, kWasmNullRef, kWasmBottom); INTERSECTION(kWasmNullFuncRef, kWasmFuncRef, kWasmNullFuncRef); INTERSECTION(kWasmNullFuncRef, kWasmFuncRef.AsNonNull(), kWasmBottom); INTERSECTION(kWasmNullFuncRef, kWasmNullExternRef, kWasmBottom); INTERSECTION(kWasmEqRef, kWasmStructRef, kWasmStructRef); INTERSECTION(kWasmEqRef, kWasmI31Ref, kWasmI31Ref); INTERSECTION(kWasmEqRef, kWasmArrayRef, kWasmArrayRef); INTERSECTION(kWasmEqRef, kWasmNullRef, kWasmNullRef); INTERSECTION(kWasmEqRef, kWasmFuncRef, kWasmBottom); INTERSECTION(kWasmStructRef, kWasmI31Ref, kWasmNullRef); INTERSECTION(kWasmStructRef, kWasmArrayRef, kWasmNullRef); INTERSECTION(kWasmStructRef, kWasmNullRef, kWasmNullRef); INTERSECTION(kWasmI31Ref, kWasmArrayRef, kWasmNullRef); INTERSECTION(kWasmI31Ref.AsNonNull(), kWasmNullRef, kWasmBottom); INTERSECTION(kWasmArrayRef.AsNonNull(), kWasmNullRef, kWasmBottom); ValueType struct_type = refS(0); ValueType array_type = refA(2); ValueType function_type = refF(11); // Abstract vs indexed types. UNION(kWasmFuncRef, function_type, kWasmFuncRef); UNION(kWasmFuncRef, struct_type, kWasmTop); UNION(kWasmFuncRef, array_type, kWasmTop); INTERSECTION(kWasmFuncRef, struct_type, kWasmBottom); INTERSECTION(kWasmFuncRef, array_type, kWasmBottom); INTERSECTION_M(kWasmFuncRef, function_type, function_type, module); UNION(kWasmExnRef, struct_type, kWasmTop); UNION(kWasmExnRef, array_type, kWasmTop); UNION(kWasmExnRef, function_type, kWasmTop); INTERSECTION(kWasmExnRef, struct_type, kWasmBottom); INTERSECTION(kWasmExnRef, array_type, kWasmBottom); INTERSECTION(kWasmExnRef, function_type, kWasmBottom); UNION(kWasmNullFuncRef, function_type, function_type.AsNullable()); UNION(kWasmNullFuncRef, struct_type, kWasmTop); UNION(kWasmNullFuncRef, array_type, kWasmTop); INTERSECTION(kWasmNullFuncRef, struct_type, kWasmBottom); INTERSECTION(kWasmNullFuncRef, struct_type.AsNullable(), kWasmBottom); INTERSECTION(kWasmNullFuncRef, array_type, kWasmBottom); INTERSECTION(kWasmNullFuncRef, array_type.AsNullable(), kWasmBottom); INTERSECTION(kWasmNullFuncRef, function_type, kWasmBottom); INTERSECTION(kWasmNullFuncRef, function_type.AsNullable(), kWasmNullFuncRef); UNION(kWasmEqRef, struct_type, kWasmEqRef); UNION(kWasmEqRef, array_type, kWasmEqRef); INTERSECTION(kWasmEqRef, struct_type, struct_type); INTERSECTION(kWasmEqRef, array_type, array_type); INTERSECTION(kWasmEqRef, function_type, kWasmBottom); UNION(kWasmStructRef, struct_type, kWasmStructRef); UNION(kWasmStructRef, array_type, kWasmEqRef); UNION(kWasmStructRef, function_type, kWasmTop); INTERSECTION_M(kWasmStructRef, struct_type, struct_type, module); INTERSECTION(kWasmStructRef, array_type, kWasmBottom); INTERSECTION(kWasmStructRef, function_type, kWasmBottom); UNION(kWasmI31Ref, struct_type, kWasmEqRef); UNION(kWasmI31Ref, array_type, kWasmEqRef); INTERSECTION(kWasmI31Ref, struct_type, kWasmBottom); INTERSECTION(kWasmI31Ref, array_type, kWasmBottom); INTERSECTION(kWasmI31Ref, function_type, kWasmBottom); UNION(kWasmArrayRef, struct_type, kWasmEqRef); UNION(kWasmArrayRef, array_type, kWasmArrayRef); UNION(kWasmArrayRef, function_type, kWasmTop); INTERSECTION(kWasmArrayRef, struct_type, kWasmBottom); INTERSECTION_M(kWasmArrayRef, array_type, array_type, module); INTERSECTION(kWasmArrayRef, function_type, kWasmBottom); UNION_M(kWasmNullRef, struct_type, struct_type.AsNullable(), module); UNION_M(kWasmNullRef, array_type, array_type.AsNullable(), module); UNION(kWasmNullRef, function_type, kWasmTop); INTERSECTION(kWasmNullRef, struct_type, kWasmBottom); INTERSECTION(kWasmNullRef, array_type, kWasmBottom); INTERSECTION(kWasmNullRef, function_type, kWasmBottom); INTERSECTION(kWasmNullRef, struct_type.AsNullable(), kWasmNullRef); INTERSECTION(kWasmNullRef, array_type.AsNullable(), kWasmNullRef); INTERSECTION(kWasmNullRef, function_type.AsNullable(), kWasmBottom); UNION(struct_type, kWasmStringRef, kWasmAnyRef); UNION(array_type, kWasmStringRef, kWasmAnyRef); UNION(function_type, kWasmStringRef, kWasmTop); UNION(struct_type, kWasmRefNullExternString, kWasmTop); UNION(array_type, kWasmRefNullExternString, kWasmTop); UNION(function_type, kWasmRefNullExternString, kWasmTop); // Indexed types of different kinds. UNION(struct_type, array_type, kRefEq); INTERSECTION(struct_type, array_type, kWasmBottom); INTERSECTION(struct_type, function_type, kWasmBottom); INTERSECTION(array_type, function_type, kWasmBottom); // Nullable vs. non-nullable. UNION(struct_type, struct_type.AsNullable(), struct_type.AsNullable()); INTERSECTION(struct_type, struct_type.AsNullable(), struct_type); UNION(kWasmStructRef, kWasmStructRef, kWasmStructRef); INTERSECTION(kWasmStructRef, kWasmStructRef, kWasmStructRef); // Concrete types of the same kind. // Subtyping relation. UNION_M(refNullS(4), refS(1), refNullS(1), module1); INTERSECTION_M(refNullS(4), refS(1), refS(4), module1); INTERSECTION_M(refNullS(1), refNullS(4), refNullS(4), module); // Common ancestor. UNION_M(refS(4), refS(31), refS(1), module1); INTERSECTION(refS(4), refS(31), kWasmBottom); // No common ancestor. UNION(refA(6), refNullA(2), kWasmArrayRef); INTERSECTION(refA(6), refNullA(2), kWasmBottom); UNION(refS(0), refS(17), kWasmStructRef.AsNonNull()); INTERSECTION(refS(0), refS(17), kWasmBottom); UNION(refF(10), refNullF(11), kWasmFuncRef); INTERSECTION(refF(10), refNullF(11), kWasmBottom); // Shared types ValueType struct_shared = refS(40, kShared); ValueType function_shared = refF(41, kShared); UNION(struct_shared, struct_shared.AsNullable(), struct_shared.AsNullable()); UNION(struct_shared, struct_type, kWasmTop); UNION(struct_shared, function_shared, kWasmTop); UNION(struct_shared, kRefI31Shared, kRefEqShared); UNION(struct_shared, kRefAnyShared, kRefAnyShared); UNION(struct_shared, kRefNoneShared, struct_shared); UNION(struct_shared, kRefAny, kWasmTop); INTERSECTION(struct_shared, struct_shared.AsNullable(), struct_shared); INTERSECTION(struct_shared, struct_type, kWasmBottom); INTERSECTION(struct_shared, function_shared, kWasmBottom); INTERSECTION(struct_shared.AsNullable(), kRefNullI31Shared, kRefNullNoneShared); INTERSECTION(struct_shared, kRefAnyShared, struct_shared); INTERSECTION(struct_shared.AsNullable(), kRefNullNoneShared, kRefNullNoneShared); INTERSECTION(struct_shared, kRefAny, kWasmBottom); UNION(function_shared, kRefFuncShared, kRefFuncShared); UNION(function_shared, kRefFunc, kWasmTop); UNION(function_shared, kRefEqShared, kWasmTop); UNION(function_shared, kRefNoFuncShared, function_shared); UNION(function_shared, kRefNoExternShared, kWasmTop); INTERSECTION(function_shared, kRefFuncShared, function_shared); INTERSECTION(function_shared, kRefFunc, kWasmBottom); INTERSECTION(function_shared, kRefEqShared, kWasmBottom); INTERSECTION(function_shared.AsNullable(), kRefNullNoFuncShared, kRefNullNoFuncShared); INTERSECTION(function_shared, kRefNoExternShared, kWasmBottom); } // Generic test covering all kinds of always applicable rules (like // commutativity). const WasmModule* module = module2; std::vector test_types; test_types.reserve(arraysize(numeric_types) + arraysize(ref_types)); test_types.insert(test_types.end(), std::begin(numeric_types), std::end(numeric_types)); test_types.insert(test_types.end(), std::begin(ref_types), std::end(ref_types)); test_types.push_back(kWasmBottom); test_types.push_back(kWasmTop); for (const ValueType type_a : test_types) { SCOPED_TRACE("a = " + type_a.name()); TypeInModule a(type_a, module1); // Neutral elements: kWasmTop wrt. intersection, kWasmBottom wrt. union. INTERSECTION(type_a, kWasmTop, type_a); UNION(type_a, kWasmBottom, type_a); // Absorbing element: kWasmTop wrt. union, kWasmBottom wrt. intersection. UNION(type_a, kWasmTop, kWasmTop); INTERSECTION(type_a, kWasmBottom, kWasmBottom); UNION(type_a, type_a, type_a); // idempotency INTERSECTION(type_a, type_a, type_a); // idempotency for (const ValueType type_b : test_types) { SCOPED_TRACE("b = " + type_b.name()); TypeInModule b(type_b, module2); // There may not be any "cycles" in the type hierarchy. if (IsSubtypeOf(a.type, b.type, module1) && a.type != b.type) { EXPECT_FALSE(IsSubtypeOf(b.type, a.type, module1)); } // The union of two types is always a super type of both types. TypeInModule union_ab = Union(a, b); EXPECT_TRUE(IsSubtypeOf(a.type, union_ab.type, module1)); EXPECT_TRUE(IsSubtypeOf(b.type, union_ab.type, module1)); // Test commutativity. EXPECT_EQ(Union(a, b).type, Union(b, a).type); EXPECT_EQ(Intersection(a, b).type, Intersection(b, a).type); // If the union of a and b is b, then a is a subtype of b, so the // intersection has to be a. EXPECT_EQ(Union(a, b).type == b.type, Intersection(a, b).type == a.type); for (const ValueType type_c : test_types) { SCOPED_TRACE("c = " + type_c.name()); TypeInModule c(type_c, module1); // Test associativity. EXPECT_EQ(Union(a, Union(b, c)).type, Union(Union(a, b), c).type); EXPECT_EQ(Intersection(a, Intersection(b, c)).type, Intersection(Intersection(a, b), c).type); // Test transitivity. if (IsSubtypeOf(a.type, b.type, module1) && IsSubtypeOf(b.type, c.type, module1)) { EXPECT_TRUE(IsSubtypeOf(a.type, c.type, module1)); } // The Union(a, b) is the most specific supertype of a and b. // Therefore there may not be any type c that is a supertype of a and b // but not a supertype of c. if (IsSubtypeOf(a.type, c.type, module1) && IsSubtypeOf(b.type, c.type, module1)) { EXPECT_TRUE(IsSubtypeOf(union_ab.type, c.type, module1)); } } } } #undef SUBTYPE #undef NOT_SUBTYPE #undef SUBTYPE_IFF #undef VALID_SUBTYPE #undef NOT_VALID_SUBTYPE #undef IDENTICAL #undef DISTINCT #undef UNION #undef UNION_M #undef INTERSECTION #undef INTERSECTION_M } } // namespace v8::internal::wasm::subtyping_unittest