Skip to content

Commit 96362eb

Browse files
authored
Fix SubtypingDiscoverer with shared i31 (#7774)
SubtypingDiscoverer previously noted that the child of I31Get expressions were subtypes of i31, but they can alternatively be subtypes of (shared i31) and this was not handled. This should properly be modeled as a new kind of type constraint in SubtypingDiscoverer, but the more robust solution would be to refactor SubtypingDiscoverer to use principal types so the sharedness can be a type variable. For now, just look at the child to figure out what shareness to use. This bug was found because it caused an infinite loop in Unsubtyping, but add assertions that would have caught it directly and add a simpler test that would have triggered the assertions had they existed before. As drive-bys, add a new `insert` overload to InsertOrderedSet to make the compiler happy with UNSUBTYPING_DEBUG set and make the whitespace in another unsubtyping test consistent with the whitespace in the rest of the file. Fixes #7773.
1 parent adabf63 commit 96362eb

File tree

4 files changed

+53
-18
lines changed

4 files changed

+53
-18
lines changed

src/ir/subtype-exprs.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,15 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
273273
void visitI31Get(I31Get* curr) {
274274
// This could be |noteNonFlowSubtype| but as there are no subtypes of i31
275275
// it does not matter.
276-
self()->noteSubtype(curr->i31, Type(HeapType::i31, Nullable));
276+
// TODO: This should be i31 with a sharedness type variable, but we will
277+
// have to refactor this to use principal types to represent that
278+
// accurately. For now look at the operand to guess the proper sharedness.
279+
auto share = Unshared;
280+
if (curr->i31->type.isRef() && curr->i31->type.getHeapType().isShared()) {
281+
share = Shared;
282+
}
283+
self()->noteSubtype(curr->i31,
284+
Type(HeapTypes::i31.getBasic(share), Nullable));
277285
}
278286
void visitCallRef(CallRef* curr) {
279287
// Even if we are unreachable, the target must be valid, and in particular

src/passes/Unsubtyping.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,6 @@ struct Unsubtyping : Pass {
323323
if (sub == super || sub.isBottom()) {
324324
return;
325325
}
326-
327326
work.push_back({sub, super});
328327
}
329328

@@ -377,6 +376,7 @@ struct Unsubtyping : Pass {
377376
noteSubtype(sub.getHeapType(), super.getHeapType());
378377
}
379378
void noteSubtype(HeapType sub, HeapType super) {
379+
assert(HeapType::isSubType(sub, super));
380380
if (sub == super || sub.isBottom()) {
381381
return;
382382
}
@@ -481,6 +481,7 @@ struct Unsubtyping : Pass {
481481
}
482482

483483
void process(HeapType sub, HeapType super) {
484+
assert(HeapType::isSubType(sub, super));
484485
auto oldSuper = types.getSupertype(sub);
485486
if (oldSuper) {
486487
// We already had a recorded supertype. The new supertype might be

src/support/insert_ordered.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ template<typename T> struct InsertOrderedSet {
5959
return inserted;
6060
}
6161

62+
template<typename It> void insert(It begin, It end) {
63+
for (; begin != end; ++begin) {
64+
insert(*begin);
65+
}
66+
}
67+
6268
size_t size() const { return Map.size(); }
6369
bool empty() const { return Map.empty(); }
6470

test/lit/passes/unsubtyping.wast

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,21 +1854,41 @@
18541854
;; Regression test for assertion failure on incorrect updating of type tree
18551855
;; state.
18561856
(module
1857-
(rec
1858-
;; CHECK: (rec
1859-
;; CHECK-NEXT: (type $0 (sub (struct)))
1860-
(type $0 (sub (struct)))
1861-
;; CHECK: (type $1 (sub $0 (struct (field (ref null $0)))))
1862-
(type $1 (sub $0 (struct (field (ref null $0)))))
1863-
;; CHECK: (type $2 (sub $1 (struct (field (ref null $3)))))
1864-
(type $2 (sub $1 (struct (field (ref null $3)))))
1865-
;; CHECK: (type $3 (sub $0 (struct)))
1866-
(type $3 (sub $0 (struct)))
1857+
(rec
1858+
;; CHECK: (rec
1859+
;; CHECK-NEXT: (type $0 (sub (struct)))
1860+
(type $0 (sub (struct)))
1861+
;; CHECK: (type $1 (sub $0 (struct (field (ref null $0)))))
1862+
(type $1 (sub $0 (struct (field (ref null $0)))))
1863+
;; CHECK: (type $2 (sub $1 (struct (field (ref null $3)))))
1864+
(type $2 (sub $1 (struct (field (ref null $3)))))
1865+
;; CHECK: (type $3 (sub $0 (struct)))
1866+
(type $3 (sub $0 (struct)))
1867+
)
1868+
;; CHECK: (global $g (ref struct) (struct.new_default $2))
1869+
(global $g (ref struct) (struct.new_default $2))
1870+
;; CHECK: (global $g2 (ref null $1) (ref.null none))
1871+
(global $g2 (ref null $1) (ref.null none))
1872+
;; CHECK: (export "" (global $g2))
1873+
(export "" (global $g2))
1874+
)
1875+
1876+
;; Regression for a mishandling of shared i31 with i31.get.
1877+
(module
1878+
;; CHECK: (type $0 (func (result i32)))
1879+
1880+
;; CHECK: (func $i31-get (type $0) (result i32)
1881+
;; CHECK-NEXT: (i31.get_s
1882+
;; CHECK-NEXT: (ref.i31_shared
1883+
;; CHECK-NEXT: (i32.const 0)
1884+
;; CHECK-NEXT: )
1885+
;; CHECK-NEXT: )
1886+
;; CHECK-NEXT: )
1887+
(func $i31-get (result i32)
1888+
(i31.get_s
1889+
(ref.i31_shared
1890+
(i32.const 0)
18671891
)
1868-
;; CHECK: (global $g (ref struct) (struct.new_default $2))
1869-
(global $g (ref struct) (struct.new_default $2))
1870-
;; CHECK: (global $g2 (ref null $1) (ref.null none))
1871-
(global $g2 (ref null $1) (ref.null none))
1872-
;; CHECK: (export "" (global $g2))
1873-
(export "" (global $g2))
1892+
)
1893+
)
18741894
)

0 commit comments

Comments
 (0)