Skip to content

Commit 653e424

Browse files
authored
Fix Unsubtyping on extern.convert_any (WebAssembly#8062)
We were not previously recording that the input to extern.convert_any has to be a subtype of anyref. This meant that it was possible for Unsubtyping to optimize too aggressively and break casts.
1 parent ad13362 commit 653e424

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

src/ir/subtype-exprs.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,19 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
445445
self()->noteSubtype(curr->replacement, type);
446446
}
447447
void visitRefAs(RefAs* curr) {
448-
if (curr->op == RefAsNonNull) {
449-
self()->noteCast(curr->value, curr);
448+
switch (curr->op) {
449+
case RefAsNonNull:
450+
self()->noteCast(curr->value, curr);
451+
return;
452+
case AnyConvertExtern:
453+
return;
454+
case ExternConvertAny:
455+
if (curr->type != Type::unreachable) {
456+
auto any =
457+
HeapTypes::any.getBasic(curr->type.getHeapType().getShared());
458+
self()->noteSubtype(curr->value, Type(any, Nullable));
459+
}
460+
return;
450461
}
451462
}
452463
void visitStringNew(StringNew* curr) {}

test/lit/passes/unsubtyping.wast

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,3 +1920,85 @@
19201920
)
19211921
)
19221922
)
1923+
1924+
;; extern.convert_any requires its input to remain a subtype of any.
1925+
(module
1926+
;; CHECK: (rec
1927+
;; CHECK-NEXT: (type $A (sub (struct)))
1928+
(type $A (sub (struct)))
1929+
;; CHECK: (type $B (sub $A (struct)))
1930+
(type $B (sub $A (struct)))
1931+
1932+
;; CHECK: (type $2 (func (result externref)))
1933+
1934+
;; CHECK: (type $3 (func (param anyref)))
1935+
1936+
;; CHECK: (func $test (type $2) (result externref)
1937+
;; CHECK-NEXT: (extern.convert_any
1938+
;; CHECK-NEXT: (struct.new_default $B)
1939+
;; CHECK-NEXT: )
1940+
;; CHECK-NEXT: )
1941+
(func $test (result externref)
1942+
(extern.convert_any
1943+
(struct.new $B)
1944+
)
1945+
)
1946+
1947+
;; CHECK: (func $cast (type $3) (param $0 anyref)
1948+
;; CHECK-NEXT: (drop
1949+
;; CHECK-NEXT: (ref.cast (ref null $A)
1950+
;; CHECK-NEXT: (local.get $0)
1951+
;; CHECK-NEXT: )
1952+
;; CHECK-NEXT: )
1953+
;; CHECK-NEXT: )
1954+
(func $cast (param anyref)
1955+
(drop
1956+
;; Since $B flows into any, it must remain a subtype of $A to continue
1957+
;; passing this cast.
1958+
(ref.cast (ref null $A)
1959+
(local.get 0)
1960+
)
1961+
)
1962+
)
1963+
)
1964+
1965+
;; Same as above with shared types.
1966+
(module
1967+
;; CHECK: (rec
1968+
;; CHECK-NEXT: (type $A (sub (shared (struct))))
1969+
(type $A (sub (shared (struct))))
1970+
;; CHECK: (type $B (sub $A (shared (struct))))
1971+
(type $B (sub $A (shared (struct))))
1972+
1973+
;; CHECK: (type $2 (func (result (ref null (shared extern)))))
1974+
1975+
;; CHECK: (type $3 (func (param (ref null (shared any)))))
1976+
1977+
;; CHECK: (func $test (type $2) (result (ref null (shared extern)))
1978+
;; CHECK-NEXT: (extern.convert_any
1979+
;; CHECK-NEXT: (struct.new_default $B)
1980+
;; CHECK-NEXT: )
1981+
;; CHECK-NEXT: )
1982+
(func $test (result (ref null (shared extern)))
1983+
(extern.convert_any
1984+
(struct.new $B)
1985+
)
1986+
)
1987+
1988+
;; CHECK: (func $cast (type $3) (param $0 (ref null (shared any)))
1989+
;; CHECK-NEXT: (drop
1990+
;; CHECK-NEXT: (ref.cast (ref null $A)
1991+
;; CHECK-NEXT: (local.get $0)
1992+
;; CHECK-NEXT: )
1993+
;; CHECK-NEXT: )
1994+
;; CHECK-NEXT: )
1995+
(func $cast (param (ref null (shared any)))
1996+
(drop
1997+
;; Since $B flows into any, it must remain a subtype of $A to continue
1998+
;; passing this cast.
1999+
(ref.cast (ref null $A)
2000+
(local.get 0)
2001+
)
2002+
)
2003+
)
2004+
)

0 commit comments

Comments
 (0)