Commit 18105c72 authored by Matthias Liedtke's avatar Matthias Liedtke Committed by V8 LUCI CQ

[wasm-gc] Introduce function null type nofunc

nofunc is the abstract null type, the equivalent of none but for the
function type hierarchy.
none and nofunc (and later on noextern) all can only represent a null
value, however their nulls are distinct (as there isn't any subtype
relationship between them).

Bug: v8:7748
Change-Id: Ic5ae502cc21a581ca2e0f5abc46139435d950af9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3805884Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82274}
parent e5524920
......@@ -6025,12 +6025,6 @@ class LiftoffCompiler {
WASM_ARRAY_TYPE, frozen);
}
void FuncCheck(TypeCheck& check, const FreezeCacheState& frozen) {
LoadInstanceType(check, frozen);
__ emit_i32_cond_jumpi(kUnequal, check.no_match, check.instance_type(),
WASM_INTERNAL_FUNCTION_TYPE, frozen);
}
void I31Check(TypeCheck& check, const FreezeCacheState& frozen) {
__ emit_smi_check(check.obj_reg, check.no_match,
LiftoffAssembler::kJumpOnNotSmi, frozen);
......
......@@ -239,6 +239,7 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc,
case kArrayRefCode:
case kAnyRefCode:
case kNoneCode:
case kNoFuncCode:
if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>(
decoder, pc,
......@@ -316,6 +317,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
case kArrayRefCode:
case kAnyRefCode:
case kNoneCode:
case kNoFuncCode:
if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>(
decoder, pc,
......
......@@ -71,6 +71,7 @@ class HeapType {
kStringViewWtf16, // shorthand: y.
kStringViewIter, // shorthand: z.
kNone, //
kNoFunc, //
// This value is used to represent failures in the parsing of heap types and
// does not correspond to a wasm heap type. It has to be last in this list.
kBottom
......@@ -102,6 +103,8 @@ class HeapType {
return HeapType(kStringViewIter);
case ValueTypeCode::kNoneCode:
return HeapType(kNone);
case ValueTypeCode::kNoFuncCode:
return HeapType(kNoFunc);
default:
return HeapType(kBottom);
}
......@@ -168,6 +171,8 @@ class HeapType {
return std::string("stringview_iter");
case kNone:
return std::string("none");
case kNoFunc:
return std::string("nofunc");
default:
return std::to_string(representation_);
}
......@@ -203,6 +208,8 @@ class HeapType {
return mask | kStringViewIterCode;
case kNone:
return mask | kNoneCode;
case kNoFunc:
return mask | kNoFuncCode;
default:
return static_cast<int32_t>(representation_);
}
......@@ -422,7 +429,8 @@ class ValueType {
// If {this} is (ref null $t), returns (ref $t). Otherwise, returns {this}.
constexpr ValueType AsNonNull() const {
if (is_reference_to(HeapType::kNone)) {
if (is_reference_to(HeapType::kNone) ||
is_reference_to(HeapType::kNoFunc)) {
// Non-null none type is not a valid type.
return ValueType::Primitive(kBottom);
}
......@@ -548,6 +556,8 @@ class ValueType {
return kStringViewIterCode;
case HeapType::kNone:
return kNoneCode;
case HeapType::kNoFunc:
return kNoFuncCode;
default:
return kRefNullCode;
}
......@@ -585,6 +595,10 @@ class ValueType {
break;
case kRefNull:
if (heap_type().is_generic()) {
// TODO(mliedtke): Adapt short names:
// noneref -> nullref
// nofuncref -> nullfuncref
// noexternref -> nullexternref
buf << heap_type().name() << "ref";
} else {
buf << "(ref null " << heap_type().name() << ")";
......@@ -687,6 +701,7 @@ constexpr ValueType kWasmStringViewWtf16 =
constexpr ValueType kWasmStringViewIter =
ValueType::RefNull(HeapType::kStringViewIter);
constexpr ValueType kWasmNullRef = ValueType::RefNull(HeapType::kNone);
constexpr ValueType kWasmNullFuncRef = ValueType::RefNull(HeapType::kNoFunc);
// Constants used by the generic js-to-wasm wrapper.
constexpr int kWasmValueKindBitsMask = (1u << ValueType::kKindBits) - 1;
......
......@@ -37,6 +37,9 @@ enum ValueTypeCode : uint8_t {
kI16Code = 0x79,
// Current reference types
kFuncRefCode = 0x70,
// TODO(7784): Switch to official opcodes once they are aligned with the
// stringref proposal for nofunc and noextern.
kNoFuncCode = 0x68,
kExternRefCode = 0x6f,
// typed-funcref and GC proposal types
kAnyRefCode = 0x6e,
......
......@@ -99,6 +99,41 @@ bool ValidFunctionSubtypeDefinition(uint32_t subtype_index,
return true;
}
HeapType::Representation NullSentinel(HeapType type, const WasmModule* module) {
switch (type.representation()) {
case HeapType::kI31:
case HeapType::kNone:
case HeapType::kEq:
case HeapType::kData:
case HeapType::kArray:
case HeapType::kAny:
case HeapType::kExtern: // TODO(mliedtke): Add noextern.
case HeapType::kString:
case HeapType::kStringViewWtf8:
case HeapType::kStringViewWtf16:
case HeapType::kStringViewIter:
return HeapType::kNone;
case HeapType::kFunc:
case HeapType::kNoFunc:
return HeapType::kNoFunc;
default:
// TODO(mliedtke): Add noextern.
return module->has_signature(type.ref_index()) ? HeapType::kNoFunc
: HeapType::kNone;
}
}
bool IsNullSentinel(HeapType type) {
switch (type.representation()) {
case HeapType::kNone:
case HeapType::kNoFunc:
// TODO(mliedtke): Add noextern.
return true;
default:
return false;
}
}
} // namespace
bool ValidSubtypeDefinition(uint32_t subtype_index, uint32_t supertype_index,
......@@ -191,8 +226,19 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsHeapSubtypeOfImpl(
case HeapType::kBottom:
UNREACHABLE();
case HeapType::kNone:
// none is a subtype of every compatible reference type under wasm-gc.
return true;
// none is a subtype of every non-func, non-extern reference type under
// wasm-gc.
// TODO(mliedtke): Break-up extern and nullref subtyping.
if (super_heap.is_index()) {
return !super_module->has_signature(super_heap.ref_index());
}
return super_heap != HeapType::kFunc && super_heap != HeapType::kNoFunc;
case HeapType::kNoFunc:
// nofunc is a subtype of every funcref type under wasm-gc.
if (super_heap.is_index()) {
return super_module->has_signature(super_heap.ref_index());
}
return super_heap == HeapType::kNoFunc || super_heap == HeapType::kFunc;
default:
break;
}
......@@ -206,13 +252,12 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsHeapSubtypeOfImpl(
return sub_module->has_signature(sub_index);
case HeapType::kEq:
case HeapType::kData:
case HeapType::kAny:
return !sub_module->has_signature(sub_index);
case HeapType::kArray:
return sub_module->has_array(sub_index);
case HeapType::kI31:
return false;
case HeapType::kAny:
return true;
case HeapType::kExtern:
return false;
case HeapType::kString:
......@@ -223,7 +268,8 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsHeapSubtypeOfImpl(
case HeapType::kBottom:
UNREACHABLE();
case HeapType::kNone:
// None is not a supertype for any index type.
case HeapType::kNoFunc:
// Abstract null types are not supertypes for any index type.
return false;
default:
break;
......@@ -345,9 +391,10 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
return HeapType::kAny;
case HeapType::kFunc:
case HeapType::kExtern:
case HeapType::kNoFunc:
UNREACHABLE();
default:
return module2->has_signature(heap2.ref_index()) ? HeapType::kAny
return module2->has_signature(heap2.ref_index()) ? HeapType::kBottom
: HeapType::kEq;
}
case HeapType::kData:
......@@ -363,9 +410,10 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
return HeapType::kAny;
case HeapType::kFunc:
case HeapType::kExtern:
case HeapType::kNoFunc:
UNREACHABLE();
default:
return module2->has_signature(heap2.ref_index()) ? HeapType::kAny
return module2->has_signature(heap2.ref_index()) ? HeapType::kBottom
: HeapType::kData;
}
case HeapType::kArray:
......@@ -382,11 +430,12 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
return HeapType::kAny;
case HeapType::kFunc:
case HeapType::kExtern:
case HeapType::kNoFunc:
UNREACHABLE();
default:
return module2->has_array(heap2.ref_index()) ? HeapType::kArray
: module2->has_struct(heap2.ref_index()) ? HeapType::kData
: HeapType::kAny;
: HeapType::kBottom;
}
case HeapType::kAny:
return HeapType::kAny;
......@@ -394,6 +443,25 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
return HeapType::kBottom;
case HeapType::kNone:
return heap2.representation();
case HeapType::kNoFunc:
switch (heap2.representation()) {
case HeapType::kArray:
case HeapType::kNone:
case HeapType::kData:
case HeapType::kI31:
case HeapType::kEq:
case HeapType::kAny:
case HeapType::kExtern:
UNREACHABLE();
case HeapType::kNoFunc:
return HeapType::kNoFunc;
case HeapType::kFunc:
return HeapType::kFunc;
default:
return module2->has_signature(heap2.ref_index())
? heap2.representation()
: HeapType::kBottom;
}
default:
UNREACHABLE();
}
......@@ -442,9 +510,9 @@ TypeInModule Intersection(ValueType type1, ValueType type2,
}
Nullability nullability =
type1.is_nullable() && type2.is_nullable() ? kNullable : kNonNullable;
// non-nullable none is not a valid type.
if (nullability == kNonNullable &&
(type1 == kWasmNullRef || type2 == kWasmNullRef)) {
// non-nullable null type is not a valid type.
if (nullability == kNonNullable && (IsNullSentinel(type1.heap_type()) ||
IsNullSentinel(type2.heap_type()))) {
return {kWasmBottom, module1};
}
if (IsHeapSubtypeOf(type1.heap_type(), type2.heap_type(), module1, module2)) {
......@@ -455,8 +523,16 @@ TypeInModule Intersection(ValueType type1, ValueType type2,
return TypeInModule{ValueType::RefMaybeNull(type2.heap_type(), nullability),
module2};
}
ValueType type = nullability == kNullable ? kWasmNullRef : kWasmBottom;
return TypeInModule{type, module1};
if (nullability == kNonNullable) {
return {kWasmBottom, module1};
}
// Check for common null representation.
HeapType::Representation null_type1 =
NullSentinel(type1.heap_type(), module1);
if (null_type1 == NullSentinel(type2.heap_type(), module2)) {
return {ValueType::RefNull(HeapType(null_type1)), module1};
}
return {kWasmBottom, module1};
}
} // namespace wasm
......
......@@ -1533,22 +1533,27 @@ WASM_COMPILED_EXEC_TEST(CallRef) {
tester.CheckResult(caller, 47, 5);
}
// Test that calling a function expecting any ref accepts nullref argument.
WASM_COMPILED_EXEC_TEST(CallNullRefImplicitConversion) {
const ValueType null_ref_types[] = {
kWasmFuncRef,
kWasmEqRef,
kWasmI31Ref.AsNullable(),
kWasmDataRef.AsNullable(),
kWasmArrayRef.AsNullable(),
kWasmAnyRef,
refNull(0), // struct
refNull(1), // array
refNull(2), // signature
// Test that calling a function expecting any ref accepts the abstract null
// type argument (nullref, nullfuncref, nullexternref).
WASM_COMPILED_EXEC_TEST(CallAbstractNullTypeImplicitConversion) {
const struct {
ValueType super_type;
ValueTypeCode sub_type_code;
} null_ref_types[] = {
{kWasmFuncRef, kNoFuncCode},
{kWasmEqRef, kNoneCode},
{kWasmI31Ref.AsNullable(), kNoneCode},
{kWasmDataRef.AsNullable(), kNoneCode},
{kWasmArrayRef.AsNullable(), kNoneCode},
{kWasmAnyRef, kNoneCode},
{kWasmExternRef, kNoneCode},
{refNull(0), kNoneCode}, // struct
{refNull(1), kNoneCode}, // array
{refNull(2), kNoFuncCode}, // signature
};
for (ValueType ref_type : null_ref_types) {
CHECK(ref_type.is_nullable());
for (auto [super_type, sub_type_code] : null_ref_types) {
CHECK(super_type.is_nullable());
WasmGCTester tester(execution_tier);
byte struct_idx = tester.DefineStruct({F(wasm::kWasmI32, true)});
CHECK_EQ(struct_idx, 0);
......@@ -1558,13 +1563,13 @@ WASM_COMPILED_EXEC_TEST(CallNullRefImplicitConversion) {
byte signature_idx = tester.DefineSignature(&dummySig);
CHECK_EQ(signature_idx, 2);
ValueType ref_sig_types[] = {kWasmI32, ref_type};
ValueType ref_sig_types[] = {kWasmI32, super_type};
FunctionSig sig_ref(1, 1, ref_sig_types);
byte callee = tester.DefineFunction(
&sig_ref, {}, {WASM_REF_IS_NULL(WASM_LOCAL_GET(0)), kExprEnd});
byte caller = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_CALL_FUNCTION(callee, WASM_REF_NULL(kNoneCode)), kExprEnd});
{WASM_CALL_FUNCTION(callee, WASM_REF_NULL(sub_type_code)), kExprEnd});
tester.CompileModule();
tester.CheckResult(caller, 1);
......@@ -1723,7 +1728,7 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
byte kBrOnDataTaken =
BR_ON(DATA, Data, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10)));
byte kBrOnDataNotTaken = BR_ON(DATA, Data, WASM_REF_FUNC(function_index));
byte kBrOnDataNotTaken = BR_ON(DATA, Data, WASM_REF_NULL(kNoneCode));
byte kBrOnArrayTaken =
BR_ON(ARRAY, Array, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10)));
byte kBrOnArrayNotTaken = BR_ON(ARRAY, Array, WASM_I31_NEW(WASM_I32V(42)));
......@@ -1745,7 +1750,7 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
byte kBrOnNonDataNotTaken =
BR_ON_NON(DATA, Data, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10)));
byte kBrOnNonDataTaken = BR_ON_NON(DATA, Data, WASM_REF_FUNC(function_index));
byte kBrOnNonDataTaken = BR_ON_NON(DATA, Data, WASM_REF_NULL(kNoneCode));
byte kBrOnNonArrayNotTaken = BR_ON_NON(
ARRAY, Array, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10)));
byte kBrOnNonArrayTaken =
......
......@@ -9,7 +9,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
builder.addStruct([]);
builder.addType(makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]));
builder.addType(makeSig([kWasmAnyRef, kWasmFuncRef, kWasmExternRef],
builder.addType(makeSig([kWasmFuncRef, kWasmFuncRef, kWasmExternRef],
[wasmRefType(0)]));
builder.addType(
makeSig([kWasmI64, kWasmF32, kWasmS128, kWasmI32],
......@@ -23,7 +23,7 @@ builder.addType(
wasmRefNullType(2)]));
builder.addType(makeSig([], []));
builder.addType(
makeSig([wasmRefType(kWasmAnyRef)],
makeSig([wasmRefType(kWasmFuncRef)],
[kWasmI32, kWasmI32, wasmRefType(1), wasmRefType(kWasmAnyRef),
kWasmI32, wasmRefType(1), kWasmI64, wasmRefNullType(4), kWasmI32,
wasmRefType(kWasmAnyRef), wasmRefNullType(4), kWasmI64, kWasmI64,
......
......@@ -2131,12 +2131,28 @@ TEST_F(FunctionBodyDecoderTest, Float64Globals) {
}
TEST_F(FunctionBodyDecoderTest, NullRefGlobals) {
WASM_FEATURE_SCOPE(gc);
ValueType nullRefs[] = {kWasmNullRef, kWasmNullRef, kWasmNullRef};
FunctionSig sig(1, 2, nullRefs);
builder.AddGlobal(kWasmNullRef);
ExpectValidates(&sig, {WASM_GLOBAL_GET(0)});
ExpectValidates(&sig,
{WASM_GLOBAL_SET(0, WASM_LOCAL_GET(0)), WASM_LOCAL_GET(0)});
ExpectValidates(
&sig, {WASM_GLOBAL_SET(0, WASM_REF_NULL(kNoneCode)), WASM_LOCAL_GET(0)});
}
TEST_F(FunctionBodyDecoderTest, NullFuncRefGlobals) {
WASM_FEATURE_SCOPE(gc);
ValueType nullFuncRefs[] = {kWasmNullFuncRef, kWasmNullFuncRef,
kWasmNullFuncRef};
FunctionSig sig(1, 2, nullFuncRefs);
builder.AddGlobal(kWasmNullFuncRef);
ExpectValidates(&sig, {WASM_GLOBAL_GET(0)});
ExpectValidates(&sig,
{WASM_GLOBAL_SET(0, WASM_LOCAL_GET(0)), WASM_LOCAL_GET(0)});
ExpectValidates(&sig, {WASM_GLOBAL_SET(0, WASM_REF_NULL(kNoFuncCode)),
WASM_LOCAL_GET(0)});
}
TEST_F(FunctionBodyDecoderTest, AllGetGlobalCombinations) {
......
......@@ -187,6 +187,7 @@ struct ValueTypePair {
{kF32Code, kWasmF32}, // --
{kF64Code, kWasmF64}, // --
{kFuncRefCode, kWasmFuncRef}, // --
{kNoFuncCode, kWasmNullFuncRef}, // --
{kExternRefCode, kWasmExternRef}, // --
{kAnyRefCode, kWasmAnyRef}, // --
{kEqRefCode, kWasmEqRef}, // --
......
......@@ -129,12 +129,13 @@ TEST_F(WasmSubtypingTest, Subtyping) {
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128};
constexpr ValueType ref_types[] = {
kWasmFuncRef, kWasmEqRef, kWasmI31Ref, // --
kWasmDataRef, kWasmArrayRef, kWasmAnyRef, // --
kWasmNullRef, // --
refNull(0), ref(0), // struct
refNull(2), ref(2), // array
refNull(11), ref(11) // signature
// TODO(mliedtke): Add externref
kWasmFuncRef, kWasmEqRef, kWasmI31Ref, // --
kWasmDataRef, kWasmArrayRef, kWasmAnyRef, // --
kWasmNullRef, kWasmNullFuncRef, // --
refNull(0), ref(0), // struct
refNull(2), ref(2), // array
refNull(11), ref(11) // signature
};
// Some macros to help managing types and modules.
......@@ -191,11 +192,14 @@ TEST_F(WasmSubtypingTest, Subtyping) {
}
for (ValueType ref_type : ref_types) {
const bool is_any_func = ref_type == kWasmFuncRef ||
ref_type == kWasmNullFuncRef ||
ref_type == refNull(11) || ref_type == ref(11);
SCOPED_TRACE("ref_type: " + ref_type.name());
// Concrete reference types, i31ref and dataref are subtypes of eqref,
// externref/funcref/anyref/functions are not.
SUBTYPE_IFF(ref_type, kWasmEqRef,
ref_type != kWasmFuncRef && ref_type != kWasmAnyRef &&
ref_type != refNull(11) && ref_type != ref(11));
ref_type != kWasmAnyRef && !is_any_func);
// Struct/array types are subtypes of dataref.
SUBTYPE_IFF(ref_type, kWasmDataRef,
ref_type == kWasmDataRef || ref_type == kWasmArrayRef ||
......@@ -207,22 +211,27 @@ TEST_F(WasmSubtypingTest, Subtyping) {
ref_type == kWasmArrayRef || ref_type == ref(2) ||
ref_type == kWasmNullRef || ref_type == refNull(2));
// Functions are subtypes of funcref.
SUBTYPE_IFF(ref_type, kWasmFuncRef,
ref_type == kWasmFuncRef || ref_type == refNull(11) ||
ref_type == ref(11) || ref_type == kWasmNullRef);
SUBTYPE_IFF(ref_type, kWasmFuncRef, is_any_func);
// Each reference type is a subtype of itself.
SUBTYPE(ref_type, ref_type);
// Each reference type excluding funcref is a subtype of anyref.
SUBTYPE_IFF(ref_type, kWasmAnyRef, ref_type != kWasmFuncRef);
// Each non-func, non-extern reference type is a subtype of anyref.
SUBTYPE_IFF(ref_type, kWasmAnyRef, !is_any_func);
// Only anyref is a subtype of anyref.
SUBTYPE_IFF(kWasmAnyRef, ref_type, ref_type == kWasmAnyRef);
// TODO(mliedtke): Improve test coverage for externref.
// Only externref is a subtype of externref.
NOT_SUBTYPE(kWasmExternRef, ref_type);
// Each nullable reference type is a supertype of nullref.
SUBTYPE_IFF(kWasmNullRef, ref_type, ref_type.is_nullable());
// 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);
// 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) {
......@@ -338,8 +347,11 @@ TEST_F(WasmSubtypingTest, Subtyping) {
for (ValueType type : ref_types) {
UNION(type, type, type);
INTERSECTION(type, type, type);
if (type == kWasmFuncRef) {
continue; // funcref and anyref don't share the same type hierarchy.
if (type == kWasmFuncRef || type == kWasmNullFuncRef || type == ref(11) ||
type == refNull(11)) {
// functypes don't share the same type hierarchy as anyref.
INTERSECTION(type, kWasmAnyRef, kWasmBottom);
continue;
}
UNION(kWasmAnyRef, type, kWasmAnyRef);
INTERSECTION(kWasmAnyRef, type, type);
......@@ -350,7 +362,6 @@ TEST_F(WasmSubtypingTest, Subtyping) {
}
// Abstract types vs abstract types.
UNION(kWasmFuncRef, kWasmNullRef, kWasmFuncRef);
UNION(kWasmEqRef, kWasmDataRef, kWasmEqRef);
UNION(kWasmEqRef, kWasmI31Ref, kWasmEqRef);
UNION(kWasmEqRef, kWasmArrayRef, kWasmEqRef);
......@@ -367,16 +378,23 @@ TEST_F(WasmSubtypingTest, Subtyping) {
UNION(kWasmDataRef, kWasmArrayRef, kWasmDataRef);
UNION(kWasmI31Ref.AsNonNull(), kWasmArrayRef, kWasmEqRef);
INTERSECTION(kWasmFuncRef, kWasmEqRef, kWasmNullRef);
INTERSECTION(kWasmFuncRef, kWasmDataRef, kWasmNullRef);
INTERSECTION(kWasmFuncRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmDataRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmI31Ref.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmArrayRef, kWasmNullRef);
INTERSECTION(kWasmFuncRef, kWasmNullRef, kWasmNullRef);
INTERSECTION(kWasmFuncRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmDataRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmI31Ref, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmFuncRef, kWasmNullFuncRef);
INTERSECTION(kWasmNullFuncRef, kWasmFuncRef.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmEqRef, kWasmDataRef, kWasmDataRef);
INTERSECTION(kWasmEqRef, kWasmI31Ref, kWasmI31Ref);
INTERSECTION(kWasmEqRef, kWasmArrayRef, kWasmArrayRef);
INTERSECTION(kWasmEqRef, kWasmNullRef, kWasmNullRef);
INTERSECTION(kWasmEqRef, kWasmFuncRef, kWasmNullRef);
INTERSECTION(kWasmEqRef, kWasmFuncRef, kWasmBottom);
INTERSECTION(kWasmDataRef, kWasmI31Ref, kWasmNullRef);
INTERSECTION(kWasmDataRef, kWasmArrayRef, kWasmArrayRef);
INTERSECTION(kWasmDataRef, kWasmNullRef, kWasmNullRef);
......@@ -394,6 +412,15 @@ TEST_F(WasmSubtypingTest, Subtyping) {
INTERSECTION(kWasmFuncRef, array_type, kWasmBottom);
INTERSECTION(kWasmFuncRef, function_type, function_type);
UNION(kWasmNullFuncRef, function_type, function_type.AsNullable());
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);
......
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