diff --git a/issues.txt b/issues.txt index a6951256..f730bf08 100644 --- a/issues.txt +++ b/issues.txt @@ -18,7 +18,6 @@ Prio Component Issue interpreter compress ctlxfer entries (4 byte cases) eh implement catch in SPC - gc Test for final types gc tests for binary iso-rec canonicalization gc tests for verifying supertype decls gc implement array.new_data, array.new_elem in init expressions @@ -254,6 +253,7 @@ Inline WasmObject.values array Done -------------------------------- + gc Test for final types monitor Track runtime function argument consts to detect specialization/inlining opportunities ext extended constants fast-int non-final subtyping check for call_indirect diff --git a/src/engine/Type.v3 b/src/engine/Type.v3 index a7447358..557d67fd 100644 --- a/src/engine/Type.v3 +++ b/src/engine/Type.v3 @@ -457,6 +457,7 @@ component HeapTypeDecls { for (s in t.supertypes) { if (!HeapType.Struct.?(s)) return error.abs(addr).IllegalSupertype(x, s); var y = HeapType.Struct.!(s).sdecl; + if (y.final) return error.abs(addr).FinalSupertype(x, s); if (x.field_types.length < y.field_types.length) return error.abs(addr).IllegalSupertype(x, s); for (i < y.field_types.length) { if (!checkStorageType(x.field_types[i], y.field_types[i])) return error.abs(addr).IllegalSupertype(x, s); @@ -467,6 +468,7 @@ component HeapTypeDecls { for (s in t.supertypes) { if (!HeapType.Array.?(s)) return error.abs(addr).IllegalSupertype(x, s); var y = HeapType.Array.!(s).array; + if (y.final) return error.abs(addr).FinalSupertype(x, s); if (!Arrays.allTrue(x.elem_types, y.elem_types, checkStorageType)) return error.abs(addr).IllegalSupertype(x, s); } } @@ -474,6 +476,7 @@ component HeapTypeDecls { for (s in t.supertypes) { if (!HeapType.Func.?(s)) return error.abs(addr).IllegalSupertype(x, s); var y = HeapType.Func.!(s).sig; + if (y.final) return error.abs(addr).FinalSupertype(x, s); if (!Arrays.allTrue(x.results, y.results, ValueTypes.isAssignable)) return error.abs(addr).IllegalSupertype(x, s); if (!Arrays.allTrue(y.params, x.params, ValueTypes.isAssignable)) return error.abs(addr).IllegalSupertype(x, s); } @@ -482,6 +485,7 @@ component HeapTypeDecls { for (s in t.supertypes) { if (!HeapType.Cont.?(s)) return error.abs(addr).IllegalSupertype(x, s); var y = HeapType.Cont.!(s).cont.sig; + if (y.final) return error.abs(addr).FinalSupertype(x, s); if (!Arrays.allTrue(x.sig.results, y.results, ValueTypes.isAssignable)) return error.abs(addr).IllegalSupertype(x, s); if (!Arrays.allTrue(y.params, x.sig.params, ValueTypes.isAssignable)) return error.abs(addr).IllegalSupertype(x, s); } diff --git a/src/util/ErrorGen.v3 b/src/util/ErrorGen.v3 index b34fc447..a0d8b558 100644 --- a/src/util/ErrorGen.v3 +++ b/src/util/ErrorGen.v3 @@ -332,6 +332,16 @@ class ErrorGen(filename: string) { setc(WasmError.ILLEGAL_SUPERTYPE, Strings.format2("%s type definition cannot have a %q as supertype", kind, ValueTypes.renderHeapType(_, null, false, sup))); } + def FinalSupertype(decl: HeapTypeDecl, sup: HeapType) { + var kind = "decl"; + match (decl) { + x: StructDecl => kind = "struct"; + x: ArrayDecl => kind = "array"; + x: SigDecl => kind = "func"; + } + setc(WasmError.ILLEGAL_SUPERTYPE, + Strings.format2("%s type definition cannot have final type %q as supertype", kind, ValueTypes.renderHeapType(_, null, false, sup))); + } def InvalidTagAttribute(b: byte) { setc(WasmError.INVALID_ATTRIBUTE, Strings.format1("invalid tag attribute, expected 0, got %x", b)); diff --git a/test/regress/ext:gc/type-subtyping3.bin.wast b/test/regress/ext:gc/type-subtyping3.bin.wast new file mode 100644 index 00000000..23462d68 --- /dev/null +++ b/test/regress/ext:gc/type-subtyping3.bin.wast @@ -0,0 +1,7 @@ +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\8a\80\80\80\00\02\60" + "\00\00\50\01\00\60\00\00" + ) + "sub type" +) diff --git a/test/regress/ext:gc/type-subtyping3.wast b/test/regress/ext:gc/type-subtyping3.wast new file mode 100644 index 00000000..b6b5ae1f --- /dev/null +++ b/test/regress/ext:gc/type-subtyping3.wast @@ -0,0 +1,7 @@ +(assert_invalid + (module + (type $t (func)) + (type $s (sub $t (func))) + ) + "sub type" +) diff --git a/test/regress/ext:gc/type-subtyping4.bin.wast b/test/regress/ext:gc/type-subtyping4.bin.wast new file mode 100644 index 00000000..dc088542 --- /dev/null +++ b/test/regress/ext:gc/type-subtyping4.bin.wast @@ -0,0 +1,10 @@ +(module binary + "\00\61\73\6d\01\00\00\00\01\b9\80\80\80\00\03\4e" + "\02\50\00\60\00\00\50\00\5f\01\64\00\00\4e\02\50" + "\00\60\00\00\50\00\5f\01\64\02\00\4e\02\50\01\02" + "\60\00\00\50\01\03\5f\05\64\00\00\64\02\00\64\00" + "\00\64\02\00\64\04\00\03\82\80\80\80\00\01\04\07" + "\85\80\80\80\00\01\01\67\00\00\0a\88\80\80\80\00" + "\01\82\80\80\80\00\00\0b" +) +(register "M4") diff --git a/test/regress/ext:gc/type-subtyping4.wast b/test/regress/ext:gc/type-subtyping4.wast new file mode 100644 index 00000000..a0313224 --- /dev/null +++ b/test/regress/ext:gc/type-subtyping4.wast @@ -0,0 +1,10 @@ +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g2 (sub $f2 (func))) + (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) + ) + (func (export "g") (type $g2)) +) +(register "M4") diff --git a/test/unittest/BinParserTest.v3 b/test/unittest/BinParserTest.v3 index ca365e06..6a814c0e 100644 --- a/test/unittest/BinParserTest.v3 +++ b/test/unittest/BinParserTest.v3 @@ -87,6 +87,7 @@ def Z = void( T("bad_sub0", test_bad_sub0), T("bad_sub1", test_bad_sub1), T("bad_sub2", test_bad_sub2), + T("bad_sub3", test_bad_sub3), // TODO: tests for proper decoding of struct and array types () ); @@ -1528,6 +1529,7 @@ def test_sub2(t: BinParserTester) { var s = BinSectionTester.new(t, BpSection.Type); var sect1: Array = [ 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.I32.code, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.I32.code @@ -1543,6 +1545,7 @@ def test_bad_sub0(t: BinParserTester) { var s = BinSectionTester.new(t, BpSection.Type); s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // func <: func 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.I32.code, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.I64.code @@ -1550,6 +1553,7 @@ def test_bad_sub0(t: BinParserTester) { s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // func <: struct 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Struct.code, 0, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.F64.code @@ -1557,6 +1561,7 @@ def test_bad_sub0(t: BinParserTester) { s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // func <: array 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Array.code, BpTypeCode.I8.code, 0, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.F32.code @@ -1569,13 +1574,15 @@ def test_bad_sub1(t: BinParserTester) { var s = BinSectionTester.new(t, BpSection.Type); s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // struct <: struct 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Struct.code, 1, BpTypeCode.I8.code, 0, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Struct.code, 0 ]); - s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // struct <: funct + s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // struct <: func 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.F64.code, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Struct.code, 0 @@ -1583,6 +1590,7 @@ def test_bad_sub1(t: BinParserTester) { s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // struct <: array 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.F32.code, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Struct.code, 0 @@ -1595,6 +1603,7 @@ def test_bad_sub2(t: BinParserTester) { var s = BinSectionTester.new(t, BpSection.Type); s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // array <: array 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Array.code, BpTypeCode.I8.code, 0, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Array.code, BpTypeCode.I16.code, 0 @@ -1602,6 +1611,7 @@ def test_bad_sub2(t: BinParserTester) { s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // array <: struct 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Struct.code, 0, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Array.code, BpTypeCode.I8.code, 0 @@ -1609,8 +1619,35 @@ def test_bad_sub2(t: BinParserTester) { s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // array <: func 2, + BpDefTypeCode.SUB.code, 0, BpDefTypeCode.Function.code, 0, 1, BpTypeCode.F32.code, BpDefTypeCode.SUB.code, 1, 0, BpDefTypeCode.Array.code, BpTypeCode.I8.code, 0 ]); } + +def test_bad_sub3(t: BinParserTester) { + t.extensions |= Extension.GC; + + var s = BinSectionTester.new(t, BpSection.Type); + s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // struct <: final struct + 2, + BpDefTypeCode.Struct.code, 0, + BpDefTypeCode.SUB.code, 1, 0, + BpDefTypeCode.Struct.code, 0 + ]); + + s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // array <: final array + 2, + BpDefTypeCode.Array.code, BpTypeCode.I8.code, 0, + BpDefTypeCode.SUB.code, 1, 0, + BpDefTypeCode.Array.code, BpTypeCode.I8.code, 0 + ]); + + s.invalid(WasmError.ILLEGAL_SUPERTYPE, [ // func <: final func + 2, + BpDefTypeCode.Function.code, 0, 1, BpTypeCode.I32.code, + BpDefTypeCode.SUB.code, 1, 0, + BpDefTypeCode.Function.code, 0, 1, BpTypeCode.I32.code + ]); +} diff --git a/test/unittest/CodeValidatorTest.v3 b/test/unittest/CodeValidatorTest.v3 index bcc895bc..ba1ee9b5 100644 --- a/test/unittest/CodeValidatorTest.v3 +++ b/test/unittest/CodeValidatorTest.v3 @@ -1450,7 +1450,7 @@ def test_call_ref0(t: CodeValidatorTester) { var sig_v_v = t.addSig(SigCache.v_v); var ref_i_i = ValueTypes.RefFunc(false, sig_i_i); var ref_v_v = ValueTypes.RefFunc(false, sig_v_v); - + var code1_i_i: Array = [Opcode.CALL_REF.code, heapIndexByte(sig_i_i)]; var code1_v_v: Array = [Opcode.CALL_REF.code, heapIndexByte(sig_v_v)]; var code2_i_i: Array = [Opcode.LOCAL_GET.code, 0, Opcode.CALL_REF.code, heapIndexByte(sig_i_i)]; @@ -1517,7 +1517,7 @@ def test_retcall_ref0(t: CodeValidatorTester) { if (sig == sig_v_v) t.valid(code3); else if (sig.params.length == 1 && sig.params[0] == p && sig.results.length == 0) t.valid(code3); else t.TypeError(code3, 5); - + t.sig0([p, ref], [p]); if (sig.results.length == 1 && sig.results[0] == p && sig.params.length == 1 && sig.params[0] == p) t.valid(code3); diff --git a/test/unittest/HeapTypeTest.v3 b/test/unittest/HeapTypeTest.v3 index 76e8a6d8..f526b7e0 100644 --- a/test/unittest/HeapTypeTest.v3 +++ b/test/unittest/HeapTypeTest.v3 @@ -18,7 +18,9 @@ def X_ = void( T("canon_mixed0", test_canon_mixed0), T("canon_mixed1", test_canon_mixed1), T("valid_suptype0", test_valid_suptype0), + T("valid_suptype0b", test_valid_suptype0b), T("valid_suptype1", test_valid_suptype1), + T("valid_suptype1b", test_valid_suptype1b), T("invalid_suptype0", test_invalid_suptype0), T("invalid_suptype1", test_invalid_suptype1), T("invalid_suptype2", test_invalid_suptype2), @@ -33,6 +35,9 @@ def newArray = ArrayDecl.new(true, NO_SUPERS, _); def finalSig = SigDecl.new(true, _, _, _); def finalStruct = StructDecl.new(true, _, _); def finalArray = ArrayDecl.new(true, _, _); +def nonfinalSig = SigDecl.new(false, _, _, _); +def nonfinalStruct = StructDecl.new(false, _, _); +def nonfinalArray = ArrayDecl.new(false, _, _); class HeapTypeTester(t: Tester) { def test_id_cached(a: Array) { @@ -276,13 +281,28 @@ def test_valid_suptype0(t: HeapTypeTester) { var mut_int_field = StorageType(ValueType.I32, Packedness.UNPACKED, true); var s0 = newStruct(no_fields); + t.test_supertype_checking(false, finalStruct([HeapType.Struct(s0)], no_fields)); + t.test_supertype_checking(false, finalStruct([HeapType.Struct(s0)], [mut_int_field])); + + var a0 = finalArray(NO_SUPERS, [mut_int_field]); + t.test_supertype_checking(false, finalArray([HeapType.Array(a0)], [mut_int_field])); + + var f0 = SigCache.v_v; + t.test_supertype_checking(false, finalSig([HeapType.Func(f0)], SigCache.arr_v, SigCache.arr_v)); +} + +def test_valid_suptype0b(t: HeapTypeTester) { + var no_fields = Array.new(0); + var mut_int_field = StorageType(ValueType.I32, Packedness.UNPACKED, true); + + var s0 = nonfinalStruct(NO_SUPERS, no_fields); t.test_supertype_checking(true, finalStruct([HeapType.Struct(s0)], no_fields)); t.test_supertype_checking(true, finalStruct([HeapType.Struct(s0)], [mut_int_field])); - var a0 = finalArray(NO_SUPERS, [mut_int_field]); + var a0 = nonfinalArray(NO_SUPERS, [mut_int_field]); t.test_supertype_checking(true, finalArray([HeapType.Array(a0)], [mut_int_field])); - var f0 = SigCache.v_v; + var f0 = nonfinalSig(NO_SUPERS, [], []); t.test_supertype_checking(true, finalSig([HeapType.Func(f0)], SigCache.arr_v, SigCache.arr_v)); } @@ -293,19 +313,44 @@ def test_valid_suptype1(t: HeapTypeTester) { var imm_nullref_field = StorageType(ValueTypes.NULLREF, Packedness.UNPACKED, false); var s0 = newStruct([imm_anyref_field]); + t.test_supertype_checking(false, finalStruct([HeapType.Struct(s0)], [imm_anyref_field])); + t.test_supertype_checking(false, finalStruct([HeapType.Struct(s0)], [imm_nullref_field])); + t.test_supertype_checking(false, finalStruct([HeapType.Struct(s0)], [imm_funcref_field])); + + var a0 = finalArray(NO_SUPERS, [imm_anyref_field]); + t.test_supertype_checking(false, finalArray([HeapType.Array(a0)], [imm_anyref_field])); + + var f0 = finalSig(NO_SUPERS, SigCache.arr_v, [ValueTypes.ANYREF]); + t.test_supertype_checking(false, finalSig([HeapType.Func(f0)], SigCache.arr_v, [ValueTypes.ANYREF])); // covariant ret + t.test_supertype_checking(false, finalSig([HeapType.Func(f0)], SigCache.arr_v, [ValueTypes.NULLREF])); // covariant ret + t.test_supertype_checking(false, finalSig([HeapType.Func(f0)], SigCache.arr_v, [ValueTypes.FUNCREF])); // covariant ret + + var f1 = finalSig(NO_SUPERS, [ValueTypes.NULLREF], SigCache.arr_v); + t.test_supertype_checking(false, finalSig([HeapType.Func(f1)], [ValueTypes.NULLREF], SigCache.arr_v)); // contra-variant param + t.test_supertype_checking(false, finalSig([HeapType.Func(f1)], [ValueTypes.ANYREF], SigCache.arr_v)); // contra-variant param + t.test_supertype_checking(false, finalSig([HeapType.Func(f1)], [ValueTypes.FUNCREF], SigCache.arr_v)); // contra-variant param +} + +def test_valid_suptype1b(t: HeapTypeTester) { + var no_fields = Array.new(0); + var imm_anyref_field = StorageType(ValueTypes.ANYREF, Packedness.UNPACKED, false); + var imm_funcref_field = StorageType(ValueTypes.FUNCREF, Packedness.UNPACKED, false); + var imm_nullref_field = StorageType(ValueTypes.NULLREF, Packedness.UNPACKED, false); + + var s0 = nonfinalStruct(NO_SUPERS, [imm_anyref_field]); t.test_supertype_checking(true, finalStruct([HeapType.Struct(s0)], [imm_anyref_field])); t.test_supertype_checking(true, finalStruct([HeapType.Struct(s0)], [imm_nullref_field])); t.test_supertype_checking(false, finalStruct([HeapType.Struct(s0)], [imm_funcref_field])); - var a0 = finalArray(NO_SUPERS, [imm_anyref_field]); + var a0 = nonfinalArray(NO_SUPERS, [imm_anyref_field]); t.test_supertype_checking(true, finalArray([HeapType.Array(a0)], [imm_anyref_field])); - var f0 = finalSig(NO_SUPERS, SigCache.arr_v, [ValueTypes.ANYREF]); + var f0 = nonfinalSig(NO_SUPERS, SigCache.arr_v, [ValueTypes.ANYREF]); t.test_supertype_checking(true, finalSig([HeapType.Func(f0)], SigCache.arr_v, [ValueTypes.ANYREF])); // covariant ret t.test_supertype_checking(true, finalSig([HeapType.Func(f0)], SigCache.arr_v, [ValueTypes.NULLREF])); // covariant ret t.test_supertype_checking(false, finalSig([HeapType.Func(f0)], SigCache.arr_v, [ValueTypes.FUNCREF])); // covariant ret - var f1 = finalSig(NO_SUPERS, [ValueTypes.NULLREF], SigCache.arr_v); + var f1 = nonfinalSig(NO_SUPERS, [ValueTypes.NULLREF], SigCache.arr_v); t.test_supertype_checking(true, finalSig([HeapType.Func(f1)], [ValueTypes.NULLREF], SigCache.arr_v)); // contra-variant param t.test_supertype_checking(true, finalSig([HeapType.Func(f1)], [ValueTypes.ANYREF], SigCache.arr_v)); // contra-variant param t.test_supertype_checking(false, finalSig([HeapType.Func(f1)], [ValueTypes.FUNCREF], SigCache.arr_v)); // contra-variant param @@ -352,17 +397,17 @@ def test_invalid_suptype2(t: HeapTypeTester) { var mut_anyref_field = StorageType(ValueTypes.ANYREF, Packedness.UNPACKED, true); var mut_funcref_field = StorageType(ValueTypes.FUNCREF, Packedness.UNPACKED, true); - var s0 = newStruct([mut_anyref_field]); + var s0 = nonfinalStruct(NO_SUPERS, [mut_anyref_field]); t.test_supertype_checking(true, finalStruct([HeapType.Struct(s0)], [mut_anyref_field])); // ok t.test_supertype_checking(false, finalStruct([HeapType.Struct(s0)], [mut_int_field])); // field type mismatch t.test_supertype_checking(false, finalStruct([HeapType.Struct(s0)], [mut_funcref_field])); // field type mismatch - var a0 = finalArray(NO_SUPERS, [mut_anyref_field]); + var a0 = nonfinalArray(NO_SUPERS, [mut_anyref_field]); t.test_supertype_checking(true, finalArray([HeapType.Array(a0)], [mut_anyref_field])); // ok t.test_supertype_checking(false, finalArray([HeapType.Array(a0)], [mut_int_field])); // elem type mismatch t.test_supertype_checking(false, finalArray([HeapType.Array(a0)], [mut_funcref_field])); // elem type mismatch - var f0 = finalSig(NO_SUPERS, [ValueTypes.ANYREF], [ValueTypes.FUNCREF]); + var f0 = nonfinalSig(NO_SUPERS, [ValueTypes.ANYREF], [ValueTypes.FUNCREF]); t.test_supertype_checking(true, finalSig([HeapType.Func(f0)], [ValueTypes.ANYREF], [ValueTypes.FUNCREF])); // ok t.test_supertype_checking(false, finalSig([HeapType.Func(f0)], [ValueType.I32], [ValueTypes.FUNCREF])); // param type t.test_supertype_checking(false, finalSig([HeapType.Func(f0)], [ValueTypes.FUNCREF], [ValueTypes.FUNCREF])); // param type