Commit ee0f0d54 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[wasm] Introduce type union and intersection

Change-Id: Ic150f990ac7329bf93b0f9c1c87c4c13be3e3c06
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3669252Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80811}
parent 3ba66cd2
...@@ -4962,7 +4962,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> { ...@@ -4962,7 +4962,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
if (this->failed()) return 0; \ if (this->failed()) return 0; \
Value result = CreateValue(kWasmI32); \ Value result = CreateValue(kWasmI32); \
if (V8_LIKELY(current_code_reachable_and_ok_)) { \ if (V8_LIKELY(current_code_reachable_and_ok_)) { \
if (IsHeapSubtypeOf(arg.type.heap_representation(), HeapType::k##h_type, \ if (IsHeapSubtypeOf(arg.type.heap_type(), HeapType(HeapType::k##h_type), \
this->module_)) { \ this->module_)) { \
if (arg.type.is_nullable()) { \ if (arg.type.is_nullable()) { \
/* We abuse ref.as_non_null, which isn't otherwise used as a unary \ /* We abuse ref.as_non_null, which isn't otherwise used as a unary \
...@@ -4972,9 +4972,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> { ...@@ -4972,9 +4972,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
CALL_INTERFACE(Drop); \ CALL_INTERFACE(Drop); \
CALL_INTERFACE(I32Const, &result, 1); \ CALL_INTERFACE(I32Const, &result, 1); \
} \ } \
} else if (!IsHeapSubtypeOf(HeapType::k##h_type, \ } else if (!IsHeapSubtypeOf(HeapType(HeapType::k##h_type), \
arg.type.heap_representation(), \ arg.type.heap_type(), this->module_)) { \
this->module_)) { \
CALL_INTERFACE(Drop); \ CALL_INTERFACE(Drop); \
CALL_INTERFACE(I32Const, &result, 0); \ CALL_INTERFACE(I32Const, &result, 0); \
} else { \ } else { \
...@@ -4999,16 +4998,15 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> { ...@@ -4999,16 +4998,15 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
ValueType::Ref(HeapType::k##h_type, kNonNullable); \ ValueType::Ref(HeapType::k##h_type, kNonNullable); \
Value result = CreateValue(non_nullable_abstract_type); \ Value result = CreateValue(non_nullable_abstract_type); \
if (V8_LIKELY(current_code_reachable_and_ok_)) { \ if (V8_LIKELY(current_code_reachable_and_ok_)) { \
if (IsHeapSubtypeOf(arg.type.heap_representation(), HeapType::k##h_type, \ if (IsHeapSubtypeOf(arg.type.heap_type(), HeapType(HeapType::k##h_type), \
this->module_)) { \ this->module_)) { \
if (arg.type.is_nullable()) { \ if (arg.type.is_nullable()) { \
CALL_INTERFACE(RefAsNonNull, arg, &result); \ CALL_INTERFACE(RefAsNonNull, arg, &result); \
} else { \ } else { \
CALL_INTERFACE(Forward, arg, &result); \ CALL_INTERFACE(Forward, arg, &result); \
} \ } \
} else if (!IsHeapSubtypeOf(HeapType::k##h_type, \ } else if (!IsHeapSubtypeOf(HeapType(HeapType::k##h_type), \
arg.type.heap_representation(), \ arg.type.heap_type(), this->module_)) { \
this->module_)) { \
CALL_INTERFACE(Trap, TrapReason::kTrapIllegalCast); \ CALL_INTERFACE(Trap, TrapReason::kTrapIllegalCast); \
/* We know that the following code is not reachable, but according */ \ /* We know that the following code is not reachable, but according */ \
/* to the spec it technically is. Set it to spec-only reachable. */ \ /* to the spec it technically is. Set it to spec-only reachable. */ \
......
...@@ -367,6 +367,7 @@ class ValueType { ...@@ -367,6 +367,7 @@ class ValueType {
} }
constexpr bool is_nullable() const { return kind() == kOptRef; } constexpr bool is_nullable() const { return kind() == kOptRef; }
constexpr bool is_non_nullable() const { return kind() == kRef; }
constexpr bool is_reference_to(uint32_t htype) const { constexpr bool is_reference_to(uint32_t htype) const {
return (kind() == kRef || kind() == kOptRef) && return (kind() == kRef || kind() == kOptRef) &&
...@@ -389,13 +390,16 @@ class ValueType { ...@@ -389,13 +390,16 @@ class ValueType {
return is_packed() ? Primitive(kI32) : *this; return is_packed() ? Primitive(kI32) : *this;
} }
// Returns the version of this type that does not allow null values. Handles // If {this} is (ref null $t), returns (ref $t). Otherwise, returns {this}.
// bottom.
constexpr ValueType AsNonNull() const { constexpr ValueType AsNonNull() const {
DCHECK(is_object_reference() || is_bottom());
return is_nullable() ? Ref(heap_type(), kNonNullable) : *this; return is_nullable() ? Ref(heap_type(), kNonNullable) : *this;
} }
// If {this} is (ref $t), returns (ref null $t). Otherwise, returns {this}.
constexpr ValueType AsNullable() const {
return is_non_nullable() ? Ref(heap_type(), kNullable) : *this;
}
/***************************** Field Accessors ******************************/ /***************************** Field Accessors ******************************/
constexpr ValueKind kind() const { return KindField::decode(bit_field_); } constexpr ValueKind kind() const { return KindField::decode(bit_field_); }
constexpr HeapType::Representation heap_representation() const { constexpr HeapType::Representation heap_representation() const {
......
...@@ -161,6 +161,12 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl( ...@@ -161,6 +161,12 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
HeapType sub_heap = subtype.heap_type(); HeapType sub_heap = subtype.heap_type();
HeapType super_heap = supertype.heap_type(); HeapType super_heap = supertype.heap_type();
return IsHeapSubtypeOfImpl(sub_heap, super_heap, sub_module, super_module);
}
V8_NOINLINE V8_EXPORT_PRIVATE bool IsHeapSubtypeOfImpl(
HeapType sub_heap, HeapType super_heap, const WasmModule* sub_module,
const WasmModule* super_module) {
switch (sub_heap.representation()) { switch (sub_heap.representation()) {
case HeapType::kFunc: case HeapType::kFunc:
// funcref is a subtype of anyref (aka externref) under wasm-gc. // funcref is a subtype of anyref (aka externref) under wasm-gc.
...@@ -255,6 +261,180 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2, ...@@ -255,6 +261,180 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
module2); module2);
} }
namespace {
// Returns the least common ancestor of two type indices, as a type index in
// {module1}.
HeapType::Representation CommonAncestor(uint32_t type_index1,
uint32_t type_index2,
const WasmModule* module1,
const WasmModule* module2) {
TypeDefinition::Kind kind1 = module1->types[type_index1].kind;
TypeDefinition::Kind kind2 = module2->types[type_index2].kind;
{
int depth1 = GetSubtypingDepth(module1, type_index1);
int depth2 = GetSubtypingDepth(module2, type_index2);
while (depth1 > depth2) {
type_index1 = module1->supertype(type_index1);
depth1--;
}
while (depth2 > depth1) {
type_index2 = module2->supertype(type_index2);
depth2--;
}
}
DCHECK_NE(type_index1, kNoSuperType);
DCHECK_NE(type_index2, kNoSuperType);
while (type_index1 != kNoSuperType &&
!(type_index1 == type_index2 && module1 == module2) &&
!EquivalentIndices(type_index1, type_index2, module1, module2)) {
type_index1 = module1->supertype(type_index1);
type_index2 = module2->supertype(type_index2);
}
DCHECK_EQ(type_index1 == kNoSuperType, type_index2 == kNoSuperType);
if (type_index1 != kNoSuperType) {
return static_cast<HeapType::Representation>(type_index1);
}
switch (kind1) {
case TypeDefinition::kFunction:
return kind2 == TypeDefinition::kFunction ? HeapType::kFunc
: HeapType::kAny;
case TypeDefinition::kStruct:
return kind2 == TypeDefinition::kFunction ? HeapType::kAny
: HeapType::kData;
case TypeDefinition::kArray:
switch (kind2) {
case TypeDefinition::kFunction:
return HeapType::kAny;
case TypeDefinition::kStruct:
return HeapType::kData;
case TypeDefinition::kArray:
return HeapType::kArray;
}
}
}
// Returns the least common ancestor of a generic HeapType {heap1}, and
// another HeapType {heap2}.
HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
HeapType heap2,
const WasmModule* module2) {
DCHECK(heap1.is_generic());
switch (heap1.representation()) {
case HeapType::kFunc:
case HeapType::kEq: {
return IsHeapSubtypeOf(heap2, heap1, module2, module2)
? heap1.representation()
: HeapType::kAny;
}
case HeapType::kI31:
switch (heap2.representation()) {
case HeapType::kI31:
return HeapType::kI31;
case HeapType::kEq:
case HeapType::kData:
case HeapType::kArray:
return HeapType::kEq;
case HeapType::kAny:
case HeapType::kFunc:
return HeapType::kAny;
default:
return module2->has_signature(heap2.ref_index()) ? HeapType::kAny
: HeapType::kEq;
}
case HeapType::kData:
switch (heap2.representation()) {
case HeapType::kData:
case HeapType::kArray:
return HeapType::kData;
case HeapType::kI31:
case HeapType::kEq:
return HeapType::kEq;
case HeapType::kAny:
case HeapType::kFunc:
return HeapType::kAny;
default:
return module2->has_signature(heap2.ref_index()) ? HeapType::kAny
: HeapType::kData;
}
case HeapType::kArray:
switch (heap2.representation()) {
case HeapType::kArray:
return HeapType::kArray;
case HeapType::kData:
return HeapType::kData;
case HeapType::kI31:
case HeapType::kEq:
return HeapType::kEq;
case HeapType::kAny:
case HeapType::kFunc:
return HeapType::kAny;
default:
return module2->has_array(heap2.ref_index()) ? HeapType::kArray
: module2->has_struct(heap2.ref_index()) ? HeapType::kData
: HeapType::kAny;
}
case HeapType::kAny:
return HeapType::kAny;
case HeapType::kBottom:
return HeapType::kBottom;
default:
UNREACHABLE();
}
}
} // namespace
V8_NOINLINE V8_EXPORT_PRIVATE TypeInModule Union(ValueType type1,
ValueType type2,
const WasmModule* module1,
const WasmModule* module2) {
if (!type1.is_object_reference() || !type2.is_object_reference()) {
return {
EquivalentTypes(type1, type2, module1, module2) ? type1 : kWasmBottom,
module1};
}
Nullability nullability =
type1.is_nullable() || type2.is_nullable() ? kNullable : kNonNullable;
HeapType heap1 = type1.heap_type();
HeapType heap2 = type2.heap_type();
if (heap1 == heap2 && module1 == module2) {
return {ValueType::Ref(heap1, nullability), module1};
}
if (heap1.is_generic()) {
return {ValueType::Ref(CommonAncestorWithGeneric(heap1, heap2, module2),
nullability),
module1};
} else if (heap2.is_generic()) {
return {ValueType::Ref(CommonAncestorWithGeneric(heap2, heap1, module1),
nullability),
module1};
} else {
return {ValueType::Ref(CommonAncestor(heap1.ref_index(), heap2.ref_index(),
module1, module2),
nullability),
module1};
}
}
TypeInModule Intersection(ValueType type1, ValueType type2,
const WasmModule* module1,
const WasmModule* module2) {
if (!type1.is_object_reference() || !type2.is_object_reference()) {
return {
EquivalentTypes(type1, type2, module1, module2) ? type1 : kWasmBottom,
module1};
}
Nullability nullability =
type1.is_nullable() && type2.is_nullable() ? kNullable : kNonNullable;
return IsHeapSubtypeOf(type1.heap_type(), type2.heap_type(), module1, module2)
? TypeInModule{ValueType::Ref(type1.heap_type(), nullability),
module1}
: IsHeapSubtypeOf(type2.heap_type(), type1.heap_type(), module2,
module1)
? TypeInModule{ValueType::Ref(type2.heap_type(), nullability),
module2}
: TypeInModule{kWasmBottom, module1};
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -20,6 +20,9 @@ struct WasmModule; ...@@ -20,6 +20,9 @@ struct WasmModule;
V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl( V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
ValueType subtype, ValueType supertype, const WasmModule* sub_module, ValueType subtype, ValueType supertype, const WasmModule* sub_module,
const WasmModule* super_module); const WasmModule* super_module);
V8_NOINLINE V8_EXPORT_PRIVATE bool IsHeapSubtypeOfImpl(
HeapType sub_heap, HeapType super_heap, const WasmModule* sub_module,
const WasmModule* super_module);
// Checks if type1, defined in module1, is equivalent with type2, defined in // Checks if type1, defined in module1, is equivalent with type2, defined in
// module2. // module2.
...@@ -45,13 +48,13 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool EquivalentTypes(ValueType type1, ...@@ -45,13 +48,13 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool EquivalentTypes(ValueType type1,
// - rtt1 <: rtt2 iff rtt1 ~ rtt2. // - rtt1 <: rtt2 iff rtt1 ~ rtt2.
// For heap types, the following subtyping rules hold: // For heap types, the following subtyping rules hold:
// - The abstract heap types form the following type hierarchy: // - The abstract heap types form the following type hierarchy:
// any // any (a.k.a. extern)
// / | \ // / \
// eq func extern // eq func
// / \ // / \
// i31 data // i31 data
// | // |
// array // array
// - All functions are subtypes of func. // - All functions are subtypes of func.
// - All structs are subtypes of data. // - All structs are subtypes of data.
// - All arrays are subtypes of array. // - All arrays are subtypes of array.
...@@ -72,13 +75,19 @@ V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype, ...@@ -72,13 +75,19 @@ V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype,
return IsSubtypeOfImpl(subtype, supertype, module, module); return IsSubtypeOfImpl(subtype, supertype, module, module);
} }
// We have this function call IsSubtypeOf instead of the opposite because type V8_INLINE bool IsHeapSubtypeOf(HeapType subtype, HeapType supertype,
// checks are much more common than heap type checks. const WasmModule* sub_module,
V8_INLINE bool IsHeapSubtypeOf(HeapType::Representation subtype, const WasmModule* super_module) {
HeapType::Representation supertype, if (subtype == supertype && sub_module == super_module) return true;
return IsHeapSubtypeOfImpl(subtype, supertype, sub_module, super_module);
}
// Checks if {subtype} is a subtype of {supertype} (both defined in {module}).
V8_INLINE bool IsHeapSubtypeOf(HeapType subtype, HeapType supertype,
const WasmModule* module) { const WasmModule* module) {
return IsSubtypeOf(ValueType::Ref(subtype, kNonNullable), // If the types are trivially identical, exit early.
ValueType::Ref(supertype, kNonNullable), module); if (V8_LIKELY(subtype == supertype)) return true;
return IsHeapSubtypeOfImpl(subtype, supertype, module, module);
} }
// Checks whether {subtype_index} is valid as a declared subtype of // Checks whether {subtype_index} is valid as a declared subtype of
...@@ -99,8 +108,43 @@ V8_EXPORT_PRIVATE bool ValidSubtypeDefinition(uint32_t subtype_index, ...@@ -99,8 +108,43 @@ V8_EXPORT_PRIVATE bool ValidSubtypeDefinition(uint32_t subtype_index,
struct TypeInModule { struct TypeInModule {
ValueType type; ValueType type;
const WasmModule* module; const WasmModule* module;
TypeInModule(ValueType type, const WasmModule* module)
: type(type), module(module) {}
bool operator==(const TypeInModule& other) const {
return type == other.type && module == other.module;
}
bool operator!=(const TypeInModule& other) const {
return type != other.type || module != other.module;
}
}; };
inline std::ostream& operator<<(std::ostream& oss, TypeInModule type) {
return oss << type.type.name() << "@"
<< reinterpret_cast<intptr_t>(type.module);
}
V8_NOINLINE V8_EXPORT_PRIVATE TypeInModule Union(ValueType type1,
ValueType type2,
const WasmModule* module1,
const WasmModule* module2);
V8_INLINE V8_EXPORT_PRIVATE TypeInModule Union(TypeInModule type1,
TypeInModule type2) {
return Union(type1.type, type2.type, type1.module, type2.module);
}
V8_NOINLINE V8_EXPORT_PRIVATE TypeInModule
Intersection(ValueType type1, ValueType type2, const WasmModule* module1,
const WasmModule* module2);
V8_INLINE V8_EXPORT_PRIVATE TypeInModule Intersection(TypeInModule type1,
TypeInModule type2) {
return Intersection(type1.type, type2.type, type1.module, type2.module);
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -124,14 +124,19 @@ TEST_F(WasmSubtypingTest, Subtyping) { ...@@ -124,14 +124,19 @@ TEST_F(WasmSubtypingTest, Subtyping) {
GetTypeCanonicalizer()->AddRecursiveGroup(module, 4); GetTypeCanonicalizer()->AddRecursiveGroup(module, 4);
/* 30 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(18))}, 18); /* 30 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(18))}, 18);
/* 31 */ DefineStruct(module,
{mut(ref(2)), immut(optRef(2)), immut(kWasmS128)}, 1);
} }
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64, constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128}; kWasmS128};
constexpr ValueType ref_types[] = {kWasmFuncRef, kWasmEqRef, kWasmI31Ref, constexpr ValueType ref_types[] = {
kWasmDataRef, kWasmArrayRef, kWasmAnyRef, kWasmFuncRef, kWasmEqRef, kWasmI31Ref, // --
optRef(0), ref(0), optRef(2), kWasmDataRef, kWasmArrayRef, kWasmAnyRef, // --
ref(2), optRef(11), ref(11)}; optRef(0), ref(0), // struct
optRef(2), ref(2), // array
optRef(11), ref(11) // signature
};
// Some macros to help managing types and modules. // Some macros to help managing types and modules.
#define SUBTYPE(type1, type2) \ #define SUBTYPE(type1, type2) \
...@@ -156,6 +161,17 @@ TEST_F(WasmSubtypingTest, Subtyping) { ...@@ -156,6 +161,17 @@ TEST_F(WasmSubtypingTest, Subtyping) {
EXPECT_FALSE(EquivalentTypes(ValueType::Ref(index1, kNullable), \ EXPECT_FALSE(EquivalentTypes(ValueType::Ref(index1, kNullable), \
ValueType::Ref(index2, kNullable), module1, \ ValueType::Ref(index2, kNullable), module1, \
module)); module));
// Union always expresses the result in terms of module1.
#define UNION(type1, type2, type_result) \
EXPECT_EQ(Union(type1, type2, module1, module), \
TypeInModule(type_result, module1))
// Intersection might return either module, so we have a version which checks
// the module and one which deos not.
#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}) { for (WasmModule* module : {module1, module2}) {
// For cross module subtyping, we need to enable type canonicalization. // For cross module subtyping, we need to enable type canonicalization.
...@@ -282,6 +298,127 @@ TEST_F(WasmSubtypingTest, Subtyping) { ...@@ -282,6 +298,127 @@ TEST_F(WasmSubtypingTest, Subtyping) {
// Rtts of identical types are subtype-related. // Rtts of identical types are subtype-related.
SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(17)); SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(17));
} }
// Unions and intersections.
// Distinct numeric types are unrelated.
for (ValueType type1 : numeric_types) {
for (ValueType type2 : numeric_types) {
UNION(type1, type2, (type1 == type2 ? type1 : kWasmBottom));
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, kWasmBottom);
INTERSECTION(type1, type2, kWasmBottom);
}
}
// Reference type vs. itself and anyref.
for (ValueType type : ref_types) {
UNION(type, type, type);
INTERSECTION(type, type, type);
UNION(kWasmAnyRef, type, kWasmAnyRef);
INTERSECTION(kWasmAnyRef, type, type);
UNION(kWasmAnyRef.AsNonNull(), type,
type.is_nullable() ? kWasmAnyRef : kWasmAnyRef.AsNonNull());
INTERSECTION(kWasmAnyRef.AsNonNull(), type, type.AsNonNull());
}
// Abstract types vs abstract types.
UNION(kWasmFuncRef, kWasmEqRef, kWasmAnyRef);
UNION(kWasmFuncRef, kWasmDataRef, kWasmAnyRef);
UNION(kWasmFuncRef, kWasmI31Ref, kWasmAnyRef);
UNION(kWasmFuncRef, kWasmArrayRef, kWasmAnyRef);
UNION(kWasmEqRef, kWasmDataRef, kWasmEqRef);
UNION(kWasmEqRef, kWasmI31Ref, kWasmEqRef);
UNION(kWasmEqRef, kWasmArrayRef, kWasmEqRef);
UNION(kWasmDataRef, kWasmI31Ref, kWasmEqRef.AsNonNull());
UNION(kWasmDataRef, kWasmArrayRef, kWasmDataRef);
UNION(kWasmI31Ref, kWasmArrayRef, kWasmEqRef.AsNonNull());
INTERSECTION(kWasmFuncRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmDataRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmI31Ref, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmEqRef, kWasmDataRef, kWasmDataRef);
INTERSECTION(kWasmEqRef, kWasmI31Ref, kWasmI31Ref);
INTERSECTION(kWasmEqRef, kWasmArrayRef, kWasmArrayRef);
INTERSECTION(kWasmDataRef, kWasmI31Ref, kWasmBottom);
INTERSECTION(kWasmDataRef, kWasmArrayRef, kWasmArrayRef);
INTERSECTION(kWasmI31Ref, kWasmArrayRef, kWasmBottom);
ValueType struct_type = ref(0);
ValueType array_type = ref(2);
ValueType function_type = ref(11);
// Abstract vs indexed types.
UNION(kWasmFuncRef, struct_type, kWasmAnyRef);
UNION(kWasmFuncRef, array_type, kWasmAnyRef);
UNION(kWasmFuncRef, function_type, kWasmFuncRef);
INTERSECTION(kWasmFuncRef, struct_type, kWasmBottom);
INTERSECTION(kWasmFuncRef, array_type, kWasmBottom);
INTERSECTION(kWasmFuncRef, function_type, function_type);
UNION(kWasmEqRef, struct_type, kWasmEqRef);
UNION(kWasmEqRef, array_type, kWasmEqRef);
UNION(kWasmEqRef, function_type, kWasmAnyRef);
INTERSECTION(kWasmEqRef, struct_type, struct_type);
INTERSECTION(kWasmEqRef, array_type, array_type);
INTERSECTION(kWasmEqRef, function_type, kWasmBottom);
UNION(kWasmDataRef, struct_type, kWasmDataRef);
UNION(kWasmDataRef, array_type, kWasmDataRef);
UNION(kWasmDataRef, function_type, kWasmAnyRef.AsNonNull());
INTERSECTION(kWasmDataRef, struct_type, struct_type);
INTERSECTION(kWasmDataRef, array_type, array_type);
INTERSECTION(kWasmDataRef, function_type, kWasmBottom);
UNION(kWasmI31Ref, struct_type, kWasmEqRef.AsNonNull());
UNION(kWasmI31Ref, array_type, kWasmEqRef.AsNonNull());
UNION(kWasmI31Ref, function_type, kWasmAnyRef.AsNonNull());
INTERSECTION(kWasmI31Ref, struct_type, kWasmBottom);
INTERSECTION(kWasmI31Ref, array_type, kWasmBottom);
INTERSECTION(kWasmI31Ref, function_type, kWasmBottom);
UNION(kWasmArrayRef, struct_type, kWasmDataRef);
UNION(kWasmArrayRef, array_type, kWasmArrayRef);
UNION(kWasmArrayRef, function_type, kWasmAnyRef.AsNonNull());
INTERSECTION(kWasmArrayRef, struct_type, kWasmBottom);
INTERSECTION(kWasmArrayRef, array_type, array_type);
INTERSECTION(kWasmArrayRef, function_type, kWasmBottom);
// Indexed types of different kinds.
UNION(struct_type, array_type, kWasmDataRef);
UNION(struct_type, function_type, kWasmAnyRef.AsNonNull());
UNION(array_type, function_type, kWasmAnyRef.AsNonNull());
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(kWasmDataRef, kWasmDataRef.AsNullable(), kWasmDataRef.AsNullable());
INTERSECTION(kWasmDataRef, kWasmDataRef.AsNullable(), kWasmDataRef);
// Concrete types of the same kind.
// Subtyping relation.
UNION(optRef(4), ref(1), optRef(1));
INTERSECTION_M(optRef(4), ref(1), ref(4), module1);
INTERSECTION_M(optRef(1), optRef(4), optRef(4), module);
// Common ancestor.
UNION(ref(4), ref(31), ref(1));
INTERSECTION(ref(4), ref(31), kWasmBottom);
// No common ancestor.
UNION(ref(6), optRef(2), kWasmArrayRef.AsNullable());
INTERSECTION(ref(6), optRef(2), kWasmBottom);
UNION(ref(0), ref(17), kWasmDataRef);
INTERSECTION(ref(0), ref(17), kWasmBottom);
UNION(ref(10), optRef(11), kWasmFuncRef);
INTERSECTION(ref(10), optRef(11), kWasmBottom);
} }
#undef SUBTYPE #undef SUBTYPE
#undef NOT_SUBTYPE #undef NOT_SUBTYPE
...@@ -290,6 +427,9 @@ TEST_F(WasmSubtypingTest, Subtyping) { ...@@ -290,6 +427,9 @@ TEST_F(WasmSubtypingTest, Subtyping) {
#undef NOT_VALID_SUBTYPE #undef NOT_VALID_SUBTYPE
#undef IDENTICAL #undef IDENTICAL
#undef DISTINCT #undef DISTINCT
#undef UNION
#undef INTERSECTION
#undef INTERSECTION_M
} }
} // namespace subtyping_unittest } // namespace subtyping_unittest
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment