summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2021-03-02 23:41:16 +0000
committerGitHub <noreply@github.com>2021-03-02 15:41:16 -0800
commit5f88277c4bddc097d2f073e41488889f02b08103 (patch)
treed669c7c77f9945f696930c3b29c7d67a21a5d274
parente7c6356c7529c29ef06f4541bc3164659e8917f6 (diff)
downloadbinaryen-5f88277c4bddc097d2f073e41488889f02b08103.tar.gz
binaryen-5f88277c4bddc097d2f073e41488889f02b08103.tar.bz2
binaryen-5f88277c4bddc097d2f073e41488889f02b08103.zip
[Wasm GC] Allow subtyping in arguments to struct.get etc. Fixes #3636 (#3644)
Note that Binaryen "canonicalizes" the type, so in the test output here we end up with $grandchild twice. This is a consequence of us not storing the heap type as an extra field. I can't think of a downside to this canonicalization, aside from losing perfect roundtripping, but I think that's a worthwhile tradeoff for efficiency as we've been thinking so far. Fixes #3636
-rw-r--r--src/wasm/wasm-binary.cpp2
-rw-r--r--src/wasm/wasm-s-parser.cpp2
-rw-r--r--test/example/c-api-kitchen-sink.c28
-rw-r--r--test/heap-types.wast4
-rw-r--r--test/heap-types.wast.from-wast7
-rw-r--r--test/heap-types.wast.fromBinary7
-rw-r--r--test/heap-types.wast.fromBinary.noDebugInfo7
7 files changed, 45 insertions, 12 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 1f54a0038..d5579eedf 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -6417,7 +6417,7 @@ void WasmBinaryBuilder::validateHeapTypeUsingChild(Expression* child,
return;
}
if ((!child->type.isRef() && !child->type.isRtt()) ||
- child->type.getHeapType() != heapType) {
+ !HeapType::isSubType(child->type.getHeapType(), heapType)) {
throwError("bad heap type: expected " + heapType.toString() +
" but found " + child->type.toString());
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 1271ff603..11121ced6 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -3451,7 +3451,7 @@ void SExpressionWasmBuilder::validateHeapTypeUsingChild(Expression* child,
return;
}
if ((!child->type.isRef() && !child->type.isRtt()) ||
- child->type.getHeapType() != heapType) {
+ !HeapType::isSubType(child->type.getHeapType(), heapType)) {
throw ParseException("bad heap type: expected " + heapType.toString() +
" but found " + child->type.toString(),
s.line,
diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c
index 4faf7aa5e..04f552069 100644
--- a/test/example/c-api-kitchen-sink.c
+++ b/test/example/c-api-kitchen-sink.c
@@ -731,13 +731,27 @@ void test_core() {
BinaryenRefEq(module,
BinaryenRefNull(module, BinaryenTypeEqref()),
BinaryenRefNull(module, BinaryenTypeEqref())),
- BinaryenRefIs(module, BinaryenRefIsFunc(), BinaryenRefNull(module, BinaryenTypeAnyref())),
- BinaryenRefIs(module, BinaryenRefIsData(), BinaryenRefNull(module, BinaryenTypeAnyref())),
- BinaryenRefIs(module, BinaryenRefIsI31(), BinaryenRefNull(module, BinaryenTypeAnyref())),
- BinaryenRefAs(module, BinaryenRefAsNonNull(), BinaryenRefNull(module, BinaryenTypeAnyref())),
- BinaryenRefAs(module, BinaryenRefAsFunc(), BinaryenRefNull(module, BinaryenTypeAnyref())),
- BinaryenRefAs(module, BinaryenRefAsData(), BinaryenRefNull(module, BinaryenTypeAnyref())),
- BinaryenRefAs(module, BinaryenRefAsI31(), BinaryenRefNull(module, BinaryenTypeAnyref())),
+ BinaryenRefIs(module,
+ BinaryenRefIsFunc(),
+ BinaryenRefNull(module, BinaryenTypeAnyref())),
+ BinaryenRefIs(module,
+ BinaryenRefIsData(),
+ BinaryenRefNull(module, BinaryenTypeAnyref())),
+ BinaryenRefIs(module,
+ BinaryenRefIsI31(),
+ BinaryenRefNull(module, BinaryenTypeAnyref())),
+ BinaryenRefAs(module,
+ BinaryenRefAsNonNull(),
+ BinaryenRefNull(module, BinaryenTypeAnyref())),
+ BinaryenRefAs(module,
+ BinaryenRefAsFunc(),
+ BinaryenRefNull(module, BinaryenTypeAnyref())),
+ BinaryenRefAs(module,
+ BinaryenRefAsData(),
+ BinaryenRefNull(module, BinaryenTypeAnyref())),
+ BinaryenRefAs(module,
+ BinaryenRefAsI31(),
+ BinaryenRefNull(module, BinaryenTypeAnyref())),
// Exception handling
BinaryenTry(module, NULL, tryBody, catchEvents, 1, catchBodies, 2, NULL),
// (try $try_outer
diff --git a/test/heap-types.wast b/test/heap-types.wast
index e645ee74d..f340e9dee 100644
--- a/test/heap-types.wast
+++ b/test/heap-types.wast
@@ -58,6 +58,10 @@
(struct.get_s $struct.B 0 (local.get $tB))
)
(drop
+ ;; immutable fields allow subtyping.
+ (struct.get $child 0 (ref.null $grandchild))
+ )
+ (drop
(ref.null $struct.A)
)
(drop
diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast
index b4228fd20..6c07baf9a 100644
--- a/test/heap-types.wast.from-wast
+++ b/test/heap-types.wast.from-wast
@@ -5,6 +5,7 @@
(type $vector (array (mut f64)))
(type $anyref_=>_none (func (param anyref)))
(type $parent (struct ))
+ (type $grandchild (struct (field i32) (field i64)))
(type $struct.C (struct (field $named-mut (mut f32))))
(type $none_=>_none (func))
(type $rtt_1_$parent_=>_none (func (param (rtt 1 $parent))))
@@ -12,7 +13,6 @@
(type $ref?|$struct.A|_=>_ref?|$struct.B| (func (param (ref null $struct.A)) (result (ref null $struct.B))))
(type $ref?|$vector|_=>_ref?|$matrix| (func (param (ref null $vector)) (result (ref null $matrix))))
(type $child (struct (field i32)))
- (type $grandchild (struct (field i32) (field i64)))
(type $words (array (mut i32)))
(type $bytes (array (mut i8)))
(global $rttparent (rtt 0 $parent) (rtt.canon $parent))
@@ -62,6 +62,11 @@
)
)
(drop
+ (struct.get $grandchild 0
+ (ref.null $grandchild)
+ )
+ )
+ (drop
(ref.null $struct.A)
)
(drop
diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary
index 2fe6adbe1..795c789ca 100644
--- a/test/heap-types.wast.fromBinary
+++ b/test/heap-types.wast.fromBinary
@@ -5,6 +5,7 @@
(type $vector (array (mut f64)))
(type $anyref_=>_none (func (param anyref)))
(type $parent (struct ))
+ (type $grandchild (struct (field i32) (field i64)))
(type $struct.C (struct (field $named-mut (mut f32))))
(type $none_=>_none (func))
(type $rtt_1_$parent_=>_none (func (param (rtt 1 $parent))))
@@ -12,7 +13,6 @@
(type $ref?|$struct.A|_=>_ref?|$struct.B| (func (param (ref null $struct.A)) (result (ref null $struct.B))))
(type $ref?|$vector|_=>_ref?|$matrix| (func (param (ref null $vector)) (result (ref null $matrix))))
(type $child (struct (field i32)))
- (type $grandchild (struct (field i32) (field i64)))
(type $words (array (mut i32)))
(type $bytes (array (mut i8)))
(global $rttparent (rtt 0 $parent) (rtt.canon $parent))
@@ -62,6 +62,11 @@
)
)
(drop
+ (struct.get $grandchild 0
+ (ref.null $grandchild)
+ )
+ )
+ (drop
(ref.null $struct.A)
)
(drop
diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo
index dbfcb9cdd..6b1641407 100644
--- a/test/heap-types.wast.fromBinary.noDebugInfo
+++ b/test/heap-types.wast.fromBinary.noDebugInfo
@@ -5,6 +5,7 @@
(type $[mut:f64] (array (mut f64)))
(type $anyref_=>_none (func (param anyref)))
(type ${} (struct ))
+ (type ${i32_i64} (struct (field i32) (field i64)))
(type ${mut:f32} (struct (field (mut f32))))
(type $none_=>_none (func))
(type $rtt_1_{}_=>_none (func (param (rtt 1 ${}))))
@@ -12,7 +13,6 @@
(type $ref?|{i32_f32_f64}|_=>_ref?|{i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}| (func (param (ref null ${i32_f32_f64})) (result (ref null ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))))
(type $ref?|[mut:f64]|_=>_ref?|[ref?|[mut:f64]|]| (func (param (ref null $[mut:f64])) (result (ref null $[ref?|[mut:f64]|]))))
(type ${i32} (struct (field i32)))
- (type ${i32_i64} (struct (field i32) (field i64)))
(type $[mut:i32] (array (mut i32)))
(type $[mut:i8] (array (mut i8)))
(global $global$0 (rtt 0 ${}) (rtt.canon ${}))
@@ -62,6 +62,11 @@
)
)
(drop
+ (struct.get ${i32_i64} 0
+ (ref.null ${i32_i64})
+ )
+ )
+ (drop
(ref.null ${i32_f32_f64})
)
(drop