From 688d42e0e33187a42aac7f0eff895f0a2a07ba65 Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Tue, 11 Aug 2026 22:08:33 +0000 Subject: [PATCH 1/7] Allow i64 and ref eq control words in struct.wait --- src/ir/child-typer.h | 11 +++- src/wasm-interpreter.h | 6 +- src/wasm/wasm-ir-builder.cpp | 1 + src/wasm/wasm-validator.cpp | 48 +++++++++++--- test/lit/validation/waitqueue.wast | 10 ++- test/spec/waitqueue.wast | 101 +++++++++++++++++++++++++++-- 6 files changed, 160 insertions(+), 17 deletions(-) diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 2ecabfbf65e..74dfedd8c13 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1038,10 +1038,19 @@ template struct ChildTyper : OverriddenVisitor { } ht = curr->ref->type.getHeapType(); } + const auto& fields = ht->getStruct().fields; + if (curr->index >= fields.size()) { + self().noteUnknown(); + return; + } note(&curr->ref, Type(*ht, Nullable)); note(&curr->waitqueue, Type(HeapTypes::sharedWaitqueue, Nullable)); - note(&curr->expected, Type(Type::BasicType::i32)); + auto expectedType = fields[curr->index].type; + if (expectedType.isRef()) { + expectedType = Type(HeapTypes::eq.getBasic(Shared), Nullable); + } + note(&curr->expected, expectedType); note(&curr->timeout, Type(Type::BasicType::i64)); } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 6e046152252..3523e7d118a 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2324,6 +2324,7 @@ class ExpressionRunner : public OverriddenVisitor { Flow visitStructWait(StructWait* curr) { VISIT(ref, curr->ref) + VISIT(waitqueue, curr->waitqueue) VISIT(expected, curr->expected) VISIT(timeout, curr->timeout) @@ -2335,8 +2336,11 @@ class ExpressionRunner : public OverriddenVisitor { if (!data) { trap("null ref"); } + if (!waitqueue.getSingleValue().getGCData()) { + trap("null ref"); + } auto& field = data->values[curr->index]; - if (field.geti32() != expected.getSingleValue().geti32()) { + if (field != expected.getSingleValue()) { return Literal(int32_t{1}); // not equal } // TODO: Add threads support. For now, report a host limit here, as there diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index e4c753fb220..585a1c98f2a 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -2426,6 +2426,7 @@ Result<> IRBuilder::makeStructWait(HeapType type, Index index) { } StructWait curr(wasm.allocator); + curr.index = index; CHECK_ERR(ChildPopper{*this}.visitStructWait(&curr, type)); CHECK_ERR(validateTypeAnnotation(type, curr.ref)); push(builder.makeStructWait( diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 56237a3d732..ba849bc507e 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -600,6 +600,8 @@ struct FunctionValidator : public WalkerPass> { bool shouldBeTrue(bool result, T curr, const char* text) { return info.shouldBeTrue(result, curr, text, getFunction()); } + + // Returns true if the assertion was met, i.e. returns !result. template bool shouldBeFalse(bool result, T curr, const char* text) { return info.shouldBeFalse(result, curr, text, getFunction()); @@ -3679,6 +3681,10 @@ void FunctionValidator::visitStructCmpxchg(StructCmpxchg* curr) { } void FunctionValidator::visitStructWait(StructWait* curr) { + // In IRBuilder, we check that the struct ref matches the type immediate. + // We can't check this here because we've already discarded the type immediate + // at this point. All other validations are here. + shouldBeTrue( !getModule() || getModule()->features.hasSharedEverything(), curr, @@ -3688,20 +3694,44 @@ void FunctionValidator::visitStructWait(StructWait* curr) { Type(HeapTypes::sharedWaitqueue, Nullable), curr, "struct.wait waitqueue must be a shared waitqueue reference"); - shouldBeEqual(curr->expected->type, - Type(Type::BasicType::i32), - curr, - "struct.wait expected must be an i32"); shouldBeEqual(curr->timeout->type, Type(Type::BasicType::i64), curr, "struct.wait timeout must be an i64"); - // Checks to the ref argument's type are done in IRBuilder where we have the - // type annotation immediate available. We check that - // * The reference arg is a subtype of the type immediate - // * The index immediate is a valid field index of the type immediate (and - // thus valid for the reference's type too) + if (curr->ref->type == Type::unreachable || curr->ref->type.isNull()) { + return; + } + if (!shouldBeTrue(curr->ref->type.isStruct(), + curr->ref, + "struct.wait ref must be a struct")) { + return; + } + const auto& fields = curr->ref->type.getHeapType().getStruct().fields; + if (!shouldBeTrue( + curr->index < fields.size(), curr, "out of bounds struct.wait field")) { + return; + } + auto& field = fields[curr->index]; + if (!shouldBeFalse( + field.isPacked(), curr, "struct.wait field must not be packed")) { + return; + } + + if ( + !shouldBeTrue( + field.type == Type::i32 || field.type == Type::i64 || + Type::isSubType(field.type, + Type(HeapTypes::eq.getBasic(Shared), Nullable)), + curr, + R"(struct.wait control word field must be i32, i64 or a subtype of (ref null (shared eq)))")) { + return; + } + + shouldBeSubType(curr->expected->type, + field.type, + curr, + "struct.wait expected value must match the field immediate"); } void FunctionValidator::visitWaitqueueNew(WaitqueueNew* curr) { diff --git a/test/lit/validation/waitqueue.wast b/test/lit/validation/waitqueue.wast index 0ff4d4a0379..bdfb3245116 100644 --- a/test/lit/validation/waitqueue.wast +++ b/test/lit/validation/waitqueue.wast @@ -1,9 +1,15 @@ ;; RUN: not wasm-opt --enable-reference-types --enable-gc %s 2>&1 | filecheck %s + +;; Tests feature-related validations. +;; Other validations are in the spec test spec/waitqueue.wast. (module (type $struct (struct (field i32))) ;; CHECK: waitqueue.new requires shared-everything [--enable-shared-everything] - (func + (func $new (drop (waitqueue.new)) ) + ;; CHECK: struct.wait requires shared-everything [--enable-shared-everything] + (func $wait-no-feature (param $ref (ref $struct)) (param $wq (ref null (shared waitqueue))) + (drop (struct.wait $struct 0 (local.get $ref) (local.get $wq) (i32.const 0) (i64.const 0))) + ) ) - diff --git a/test/spec/waitqueue.wast b/test/spec/waitqueue.wast index 6317a39d970..da153579b01 100644 --- a/test/spec/waitqueue.wast +++ b/test/spec/waitqueue.wast @@ -14,7 +14,7 @@ (func (param $expected i32) (param $timeout i64) (result i32) (struct.wait $t 2 (ref.null $t) (global.get $wq) (local.get $expected) (local.get $timeout)) ) - ) "struct index out of bounds" + ) "out of bounds struct.wait field" ) (assert_invalid @@ -25,7 +25,29 @@ (func (param $expected i32) (param $timeout i64) (result i32) (struct.wait $t 0 (global.get $g) (global.get $wq) (i64.const 0) (local.get $timeout)) ) - ) "struct.wait expected must be an i32" + ) "struct.wait expected value must match the field immediate" +) + +(assert_invalid + (module + (type $t (shared (struct (field f32)))) + (global $g (ref $t) (struct.new $t (f32.const 0))) + (global $wq (ref (shared waitqueue)) (waitqueue.new)) + (func (param $expected f32) (param $timeout i64) (result i32) + (struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (local.get $timeout)) + ) + ) "struct.wait control word field must be i32, i64 or a subtype of (ref null (shared eq))" +) + +(assert_invalid + (module + (type $t (shared (struct (field i8)))) + (global $g (ref $t) (struct.new $t (i32.const 0))) + (global $wq (ref (shared waitqueue)) (waitqueue.new)) + (func (param $expected i32) (param $timeout i64) (result i32) + (struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (local.get $timeout)) + ) + ) "struct.wait field must not be packed" ) (assert_invalid @@ -41,8 +63,6 @@ (assert_invalid (module - (type $t (shared (struct (field i32)))) - (global $wq (ref (shared waitqueue)) (waitqueue.new)) (func (param $count i32) (result i32) (waitqueue.notify (ref.null waitqueue) (local.get $count)) ) @@ -74,6 +94,7 @@ ) ) +;; i32 control word (module (type $t (shared (struct (field (mut i32))))) @@ -119,6 +140,78 @@ (assert_trap (invoke "struct.wait" (i32.const 0) (i64.const 0)) "null ref") (assert_trap (invoke "waitqueue.notify" (i32.const 0)) "null ref") +;; i64 control word +(module + (type $t (shared (struct (field (mut i64))))) + + (global $g (mut (ref null $t)) (struct.new $t (i64.const 0))) + (global $wq (mut (ref (shared waitqueue))) (waitqueue.new)) + + (func (export "struct.wait") (param $expected i64) (param $timeout i64) (result i32) + (struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (local.get $timeout)) + ) + + (func (export "struct.set") (param $val i64) + (struct.set $t 0 (global.get $g) (local.get $val)) + ) + + (func (export "struct.get") (result i64) + (struct.get $t 0 (global.get $g)) + ) +) + +(invoke "struct.set" (i64.const 42)) +(assert_return (invoke "struct.get") (i64.const 42)) +(assert_return (invoke "struct.wait" (i64.const 0) (i64.const 100)) (i32.const 1)) +(assert_return (invoke "struct.wait" (i64.const 42) (i64.const 0)) (i32.const 2)) + +;; (ref null (shared eq)) control word +(module + (type $control (shared (struct))) + + (type $t (shared (struct + (field (mut (ref null (shared eq)))) + ))) + + (global $control1 (ref $control) (struct.new $control)) + (global $control2 (ref $control) (struct.new $control)) + + (global $g (mut (ref null $t)) (struct.new $t + (global.get $control1) + )) + + (global $wq (mut (ref null (shared waitqueue))) (waitqueue.new)) + + (func (export "wait_control1") (result i32) + (struct.wait $t 0 (global.get $g) (global.get $wq) (global.get $control1) (i64.const 0)) + ) + + (func (export "wait_control2") (result i32) + (struct.wait $t 0 (global.get $g) (global.get $wq) (global.get $control2) (i64.const 0)) + ) + + (func (export "wait_null") (result i32) + (struct.wait $t 0 (global.get $g) (global.get $wq) (ref.null (shared eq)) (i64.const 0)) + ) + + (func (export "set_control_to_null") + (struct.set $t 0 (global.get $g) (ref.null (shared eq))) + ) +) + +;; $control1 is the control word, wait 0ns and return 2. +(assert_return (invoke "wait_control1") (i32.const 2)) +;; $control2 is not the control work, don't wait and return 1. +(assert_return (invoke "wait_control2") (i32.const 1)) +;; ditto for null. +(assert_return (invoke "wait_null") (i32.const 1)) + +(invoke "set_control_to_null") + +;; null is now the control word. +(assert_return (invoke "wait_null") (i32.const 2)) +(assert_return (invoke "wait_control1") (i32.const 1)) + ;; Binary format test for waitqueue and nowaitqueue. (module binary "\00asm\01\00\00\00" ;; Wasm header From c8208083e3eff5ea908708460a64fc5748472e72 Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Mon, 17 Aug 2026 21:50:09 +0000 Subject: [PATCH 2/7] Fix validator --- src/wasm/wasm-validator.cpp | 16 ++++++++-------- test/spec/waitqueue.wast | 12 +++++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index ba849bc507e..9595987ef0a 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3694,10 +3694,10 @@ void FunctionValidator::visitStructWait(StructWait* curr) { Type(HeapTypes::sharedWaitqueue, Nullable), curr, "struct.wait waitqueue must be a shared waitqueue reference"); - shouldBeEqual(curr->timeout->type, - Type(Type::BasicType::i64), - curr, - "struct.wait timeout must be an i64"); + shouldBeEqualOrFirstIsUnreachable(curr->timeout->type, + Type(Type::BasicType::i64), + curr, + "struct.wait timeout must be an i64"); if (curr->ref->type == Type::unreachable || curr->ref->type.isNull()) { return; @@ -3752,10 +3752,10 @@ void FunctionValidator::visitWaitqueueNotify(WaitqueueNotify* curr) { Type(HeapTypes::sharedWaitqueue, Nullable), curr, "waitqueue.notify waitqueue must be a shared waitqueue reference"); - shouldBeEqual(curr->count->type, - Type(Type::BasicType::i32), - curr, - "waitqueue.notify count must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->count->type, + Type(Type::BasicType::i32), + curr, + "waitqueue.notify count must be an i32"); } void FunctionValidator::visitArrayNew(ArrayNew* curr) { diff --git a/test/spec/waitqueue.wast b/test/spec/waitqueue.wast index da153579b01..d89d4dae01e 100644 --- a/test/spec/waitqueue.wast +++ b/test/spec/waitqueue.wast @@ -82,16 +82,26 @@ ;; unreachable is allowed (module (type $t (shared (struct (field i32)))) + (global $g (ref $t) (struct.new $t (i32.const 0))) (global $wq (ref (shared waitqueue)) (waitqueue.new)) (func (param $expected i32) (param $timeout i64) (result i32) (struct.wait $t 0 (unreachable) (global.get $wq) (local.get $expected) (local.get $timeout)) ) (func (param $expected i32) (param $timeout i64) (result i32) - (struct.wait $t 0 (ref.null $t) (unreachable) (local.get $expected) (local.get $timeout)) + (struct.wait $t 0 (global.get $g) (unreachable) (local.get $expected) (local.get $timeout)) + ) + (func (param $timeout i64) (result i32) + (struct.wait $t 0 (global.get $g) (global.get $wq) (unreachable) (local.get $timeout)) + ) + (func (param $expected i32) (result i32) + (struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (unreachable)) ) (func (param $count i32) (result i32) (waitqueue.notify (unreachable) (local.get $count)) ) + (func (result i32) + (waitqueue.notify (global.get $wq) (unreachable)) + ) ) ;; i32 control word From 853b2cc989ba54786afacfcc837abcdb7fdc326b Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Mon, 17 Aug 2026 23:05:59 +0000 Subject: [PATCH 3/7] Typo --- test/spec/waitqueue.wast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/waitqueue.wast b/test/spec/waitqueue.wast index d89d4dae01e..b0da049567e 100644 --- a/test/spec/waitqueue.wast +++ b/test/spec/waitqueue.wast @@ -211,7 +211,7 @@ ;; $control1 is the control word, wait 0ns and return 2. (assert_return (invoke "wait_control1") (i32.const 2)) -;; $control2 is not the control work, don't wait and return 1. +;; $control2 is not the control word, don't wait and return 1. (assert_return (invoke "wait_control2") (i32.const 1)) ;; ditto for null. (assert_return (invoke "wait_null") (i32.const 1)) From b69b60910b7273573eb47952f9cc76eb21dba38b Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Mon, 17 Aug 2026 21:50:09 +0000 Subject: [PATCH 4/7] Update subtype-exprs --- src/ir/subtype-exprs.h | 17 +++++++++-- test/lit/fuzz-types.test | 62 ++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index 4ad1cdd1bed..29b3b377730 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -383,9 +383,22 @@ struct SubtypingDiscoverer : public OverriddenVisitor { self()->noteSubtype(curr->expected, expectedType); self()->noteSubtype(curr->replacement, type); } - void visitStructWait(StructWait* curr) {} + void visitStructWait(StructWait* curr) { + self()->noteSubtype(curr->waitqueue, + Type(HeapTypes::sharedWaitqueue, Nullable)); + if (!curr->ref->type.isStruct()) { + return; + } + const auto& fields = curr->ref->type.getHeapType().getStruct().fields; + if (curr->index < fields.size()) { + self()->noteSubtype(curr->expected, fields[curr->index].type); + } + } void visitWaitqueueNew(WaitqueueNew* curr) {} - void visitWaitqueueNotify(WaitqueueNotify* curr) {} + void visitWaitqueueNotify(WaitqueueNotify* curr) { + self()->noteSubtype(curr->waitqueue, + Type(HeapTypes::sharedWaitqueue, Nullable)); + } void visitArrayNew(ArrayNew* curr) { if (!curr->type.isArray() || curr->isWithDefault()) { return; diff --git a/test/lit/fuzz-types.test b/test/lit/fuzz-types.test index c164e32aaac..809641d35ba 100644 --- a/test/lit/fuzz-types.test +++ b/test/lit/fuzz-types.test @@ -10,23 +10,23 @@ ;; CHECK-NEXT: (type $4 (shared (describes $2) (struct))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $5 (sub (shared (descriptor $6) (struct (field (ref null $1)) (field (ref null (shared func))) (field (mut f32)) (field i8) (field (mut i8)))))) -;; CHECK-NEXT: (type $6 (shared (describes $5) (descriptor $7) (struct (field (mut (ref null $2))) (field (mut i8)) (field f32) (field (mut (ref $11))) (field f32)))) -;; CHECK-NEXT: (type $7 (shared (describes $6) (descriptor $9) (struct (field i32) (field v128) (field (ref null $11)) (field (mut (ref $6))) (field (mut f64)) (field (ref (shared extern)))))) -;; CHECK-NEXT: (type $8 (sub (struct (field (ref null $7)) (field i8) (field (mut (ref $6))) (field (mut externref))))) -;; CHECK-NEXT: (type $9 (shared (describes $7) (descriptor $11) (struct))) -;; CHECK-NEXT: (type $10 (sub (func (param f64 i32) (result f32)))) -;; CHECK-NEXT: (type $11 (sub (shared (describes $9) (struct (field (mut i8)) (field (mut (ref $6))) (field f32) (field (ref null $2)))))) -;; CHECK-NEXT: (type $12 (sub (descriptor $13) (struct (field i32) (field (ref null $9)) (field (mut f32)) (field (mut i16)) (field f64)))) -;; CHECK-NEXT: (type $13 (describes $12) (struct (field externref) (field (ref $2)) (field (mut (ref $6))) (field (mut f32)))) -;; CHECK-NEXT: (type $14 (sub final $8 (struct (field (ref $7)) (field i8) (field (mut (ref $6))) (field (mut externref)) (field (mut (ref $1)))))) +;; CHECK-NEXT: (type $5 (sub (shared (descriptor $6) (struct (field (ref null $1)) (field (ref (shared any))) (field i16) (field (mut (ref null $7))) (field (ref null $11)))))) +;; CHECK-NEXT: (type $6 (sub (shared (describes $5) (descriptor $7) (struct)))) +;; CHECK-NEXT: (type $7 (sub (shared (describes $6) (descriptor $9) (struct)))) +;; CHECK-NEXT: (type $8 (sub (struct (field v128) (field (ref $6)) (field f64) (field v128)))) +;; CHECK-NEXT: (type $9 (sub (shared (describes $7) (descriptor $11) (struct (field (ref null $1)) (field (mut (ref $7))) (field (mut i16)))))) +;; CHECK-NEXT: (type $10 (sub (func (result eqref)))) +;; CHECK-NEXT: (type $11 (shared (describes $9) (struct (field (mut (ref null $9))) (field (mut (ref (shared i31)))) (field (mut (ref $5))) (field (mut f64))))) +;; CHECK-NEXT: (type $12 (sub (descriptor $13) (struct (field (ref $11)) (field (mut f64)) (field v128) (field (mut (ref any))) (field (mut v128))))) +;; CHECK-NEXT: (type $13 (sub (describes $12) (struct (field i64) (field (mut (ref null $0))) (field i16) (field f64)))) +;; CHECK-NEXT: (type $14 (sub final $8 (struct (field v128) (field (ref $6)) (field f64) (field v128)))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $15 (sub final $8 (struct (field (ref $7)) (field i8) (field (mut (ref $6))) (field (mut externref))))) -;; CHECK-NEXT: (type $16 (descriptor $18) (struct (field (ref null $2)) (field i16) (field i64) (field (ref $9)) (field (mut (ref $3))))) -;; CHECK-NEXT: (type $17 (sub (func (param f64) (result (ref null $19))))) -;; CHECK-NEXT: (type $18 (describes $16) (struct)) -;; CHECK-NEXT: (type $19 (cont $17)) +;; CHECK-NEXT: (type $15 (sub final $8 (struct (field v128) (field (ref (shared none))) (field f64) (field v128) (field (mut (ref $11))) (field v128)))) +;; CHECK-NEXT: (type $16 (descriptor $18) (struct (field (mut v128)) (field (mut i64)) (field (mut (ref null $18))))) +;; CHECK-NEXT: (type $17 (func (param (ref $18) (ref null $13)) (result (ref null (shared i31))))) +;; CHECK-NEXT: (type $18 (describes $16) (struct (field (ref extern)) (field i8))) +;; CHECK-NEXT: (type $19 (cont $10)) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ;; CHECK-NEXT: Inhabitable types: @@ -40,21 +40,21 @@ ;; CHECK-NEXT: (type $4 (shared (describes $2) (struct))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $5 (sub (shared (descriptor $6) (struct (field (ref null $1)) (field (ref null (shared func))) (field (mut f32)) (field i8) (field (mut i8)))))) -;; CHECK-NEXT: (type $6 (shared (describes $5) (descriptor $7) (struct (field (mut (ref null $2))) (field (mut i8)) (field f32) (field (mut (ref $11))) (field f32)))) -;; CHECK-NEXT: (type $7 (shared (describes $6) (descriptor $9) (struct (field i32) (field v128) (field (ref null $11)) (field (mut (ref null $6))) (field (mut f64)) (field (ref null (shared extern)))))) -;; CHECK-NEXT: (type $8 (sub (struct (field (ref null $7)) (field i8) (field (mut (ref $6))) (field (mut externref))))) -;; CHECK-NEXT: (type $9 (shared (describes $7) (descriptor $11) (struct))) -;; CHECK-NEXT: (type $10 (sub (func (param f64 i32) (result f32)))) -;; CHECK-NEXT: (type $11 (sub (shared (describes $9) (struct (field (mut i8)) (field (mut (ref null $6))) (field f32) (field (ref null $2)))))) -;; CHECK-NEXT: (type $12 (sub (descriptor $13) (struct (field i32) (field (ref null $9)) (field (mut f32)) (field (mut i16)) (field f64)))) -;; CHECK-NEXT: (type $13 (describes $12) (struct (field externref) (field (ref $2)) (field (mut (ref $6))) (field (mut f32)))) -;; CHECK-NEXT: (type $14 (sub final $8 (struct (field (ref $7)) (field i8) (field (mut (ref $6))) (field (mut externref)) (field (mut (ref $1)))))) +;; CHECK-NEXT: (type $5 (sub (shared (descriptor $6) (struct (field (ref null $1)) (field (ref (shared any))) (field i16) (field (mut (ref null $7))) (field (ref null $11)))))) +;; CHECK-NEXT: (type $6 (sub (shared (describes $5) (descriptor $7) (struct)))) +;; CHECK-NEXT: (type $7 (sub (shared (describes $6) (descriptor $9) (struct)))) +;; CHECK-NEXT: (type $8 (sub (struct (field v128) (field (ref null $6)) (field f64) (field v128)))) +;; CHECK-NEXT: (type $9 (sub (shared (describes $7) (descriptor $11) (struct (field (ref null $1)) (field (mut (ref null $7))) (field (mut i16)))))) +;; CHECK-NEXT: (type $10 (sub (func (result eqref)))) +;; CHECK-NEXT: (type $11 (shared (describes $9) (struct (field (mut (ref null $9))) (field (mut (ref (shared i31)))) (field (mut (ref null $5))) (field (mut f64))))) +;; CHECK-NEXT: (type $12 (sub (descriptor $13) (struct (field (ref $11)) (field (mut f64)) (field v128) (field (mut (ref any))) (field (mut v128))))) +;; CHECK-NEXT: (type $13 (sub (describes $12) (struct (field i64) (field (mut (ref null $0))) (field i16) (field f64)))) +;; CHECK-NEXT: (type $14 (sub final $8 (struct (field v128) (field (ref $6)) (field f64) (field v128)))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $15 (sub final $8 (struct (field (ref $7)) (field i8) (field (mut (ref $6))) (field (mut externref))))) -;; CHECK-NEXT: (type $16 (descriptor $18) (struct (field (ref null $2)) (field i16) (field i64) (field (ref $9)) (field (mut (ref $3))))) -;; CHECK-NEXT: (type $17 (sub (func (param f64) (result (ref null $19))))) -;; CHECK-NEXT: (type $18 (describes $16) (struct)) -;; CHECK-NEXT: (type $19 (cont $17)) -;; CHECK-NEXT: ) +;; CHECK-NEXT: (type $15 (sub final $8 (struct (field v128) (field (ref null (shared none))) (field f64) (field v128) (field (mut (ref $11))) (field v128)))) +;; CHECK-NEXT: (type $16 (descriptor $18) (struct (field (mut v128)) (field (mut i64)) (field (mut (ref null $18))))) +;; CHECK-NEXT: (type $17 (func (param (ref $18) (ref null $13)) (result (ref null (shared i31))))) +;; CHECK-NEXT: (type $18 (describes $16) (struct (field externref) (field i8))) +;; CHECK-NEXT: (type $19 (cont $10)) +;; CHECK-NEXT: ) \ No newline at end of file From 71c76c5b535b731aea67c1dbea88d53768b445f3 Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Thu, 20 Aug 2026 22:05:26 +0000 Subject: [PATCH 5/7] Remove check for fields length --- src/ir/child-typer.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 74dfedd8c13..27499b785fb 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1038,15 +1038,10 @@ template struct ChildTyper : OverriddenVisitor { } ht = curr->ref->type.getHeapType(); } - const auto& fields = ht->getStruct().fields; - if (curr->index >= fields.size()) { - self().noteUnknown(); - return; - } note(&curr->ref, Type(*ht, Nullable)); note(&curr->waitqueue, Type(HeapTypes::sharedWaitqueue, Nullable)); - auto expectedType = fields[curr->index].type; + auto expectedType = ht->getStruct().fields[curr->index].type; if (expectedType.isRef()) { expectedType = Type(HeapTypes::eq.getBasic(Shared), Nullable); } From 2ca884f49339911202670ecdbf098e52ffe1d066 Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Thu, 20 Aug 2026 22:13:18 +0000 Subject: [PATCH 6/7] Remove other check for length --- src/ir/subtype-exprs.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index 29b3b377730..2394fdd114a 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -390,9 +390,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor { return; } const auto& fields = curr->ref->type.getHeapType().getStruct().fields; - if (curr->index < fields.size()) { - self()->noteSubtype(curr->expected, fields[curr->index].type); - } + self()->noteSubtype(curr->expected, fields[curr->index].type); } void visitWaitqueueNew(WaitqueueNew* curr) {} void visitWaitqueueNotify(WaitqueueNotify* curr) { From af936dbdd1feb55033caaf966797416555a1fd70 Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Thu, 20 Aug 2026 22:13:18 +0000 Subject: [PATCH 7/7] Fix fuzz-types lit test --- test/lit/fuzz-types.test | 62 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/test/lit/fuzz-types.test b/test/lit/fuzz-types.test index 809641d35ba..c164e32aaac 100644 --- a/test/lit/fuzz-types.test +++ b/test/lit/fuzz-types.test @@ -10,23 +10,23 @@ ;; CHECK-NEXT: (type $4 (shared (describes $2) (struct))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $5 (sub (shared (descriptor $6) (struct (field (ref null $1)) (field (ref (shared any))) (field i16) (field (mut (ref null $7))) (field (ref null $11)))))) -;; CHECK-NEXT: (type $6 (sub (shared (describes $5) (descriptor $7) (struct)))) -;; CHECK-NEXT: (type $7 (sub (shared (describes $6) (descriptor $9) (struct)))) -;; CHECK-NEXT: (type $8 (sub (struct (field v128) (field (ref $6)) (field f64) (field v128)))) -;; CHECK-NEXT: (type $9 (sub (shared (describes $7) (descriptor $11) (struct (field (ref null $1)) (field (mut (ref $7))) (field (mut i16)))))) -;; CHECK-NEXT: (type $10 (sub (func (result eqref)))) -;; CHECK-NEXT: (type $11 (shared (describes $9) (struct (field (mut (ref null $9))) (field (mut (ref (shared i31)))) (field (mut (ref $5))) (field (mut f64))))) -;; CHECK-NEXT: (type $12 (sub (descriptor $13) (struct (field (ref $11)) (field (mut f64)) (field v128) (field (mut (ref any))) (field (mut v128))))) -;; CHECK-NEXT: (type $13 (sub (describes $12) (struct (field i64) (field (mut (ref null $0))) (field i16) (field f64)))) -;; CHECK-NEXT: (type $14 (sub final $8 (struct (field v128) (field (ref $6)) (field f64) (field v128)))) +;; CHECK-NEXT: (type $5 (sub (shared (descriptor $6) (struct (field (ref null $1)) (field (ref null (shared func))) (field (mut f32)) (field i8) (field (mut i8)))))) +;; CHECK-NEXT: (type $6 (shared (describes $5) (descriptor $7) (struct (field (mut (ref null $2))) (field (mut i8)) (field f32) (field (mut (ref $11))) (field f32)))) +;; CHECK-NEXT: (type $7 (shared (describes $6) (descriptor $9) (struct (field i32) (field v128) (field (ref null $11)) (field (mut (ref $6))) (field (mut f64)) (field (ref (shared extern)))))) +;; CHECK-NEXT: (type $8 (sub (struct (field (ref null $7)) (field i8) (field (mut (ref $6))) (field (mut externref))))) +;; CHECK-NEXT: (type $9 (shared (describes $7) (descriptor $11) (struct))) +;; CHECK-NEXT: (type $10 (sub (func (param f64 i32) (result f32)))) +;; CHECK-NEXT: (type $11 (sub (shared (describes $9) (struct (field (mut i8)) (field (mut (ref $6))) (field f32) (field (ref null $2)))))) +;; CHECK-NEXT: (type $12 (sub (descriptor $13) (struct (field i32) (field (ref null $9)) (field (mut f32)) (field (mut i16)) (field f64)))) +;; CHECK-NEXT: (type $13 (describes $12) (struct (field externref) (field (ref $2)) (field (mut (ref $6))) (field (mut f32)))) +;; CHECK-NEXT: (type $14 (sub final $8 (struct (field (ref $7)) (field i8) (field (mut (ref $6))) (field (mut externref)) (field (mut (ref $1)))))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $15 (sub final $8 (struct (field v128) (field (ref (shared none))) (field f64) (field v128) (field (mut (ref $11))) (field v128)))) -;; CHECK-NEXT: (type $16 (descriptor $18) (struct (field (mut v128)) (field (mut i64)) (field (mut (ref null $18))))) -;; CHECK-NEXT: (type $17 (func (param (ref $18) (ref null $13)) (result (ref null (shared i31))))) -;; CHECK-NEXT: (type $18 (describes $16) (struct (field (ref extern)) (field i8))) -;; CHECK-NEXT: (type $19 (cont $10)) +;; CHECK-NEXT: (type $15 (sub final $8 (struct (field (ref $7)) (field i8) (field (mut (ref $6))) (field (mut externref))))) +;; CHECK-NEXT: (type $16 (descriptor $18) (struct (field (ref null $2)) (field i16) (field i64) (field (ref $9)) (field (mut (ref $3))))) +;; CHECK-NEXT: (type $17 (sub (func (param f64) (result (ref null $19))))) +;; CHECK-NEXT: (type $18 (describes $16) (struct)) +;; CHECK-NEXT: (type $19 (cont $17)) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ;; CHECK-NEXT: Inhabitable types: @@ -40,21 +40,21 @@ ;; CHECK-NEXT: (type $4 (shared (describes $2) (struct))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $5 (sub (shared (descriptor $6) (struct (field (ref null $1)) (field (ref (shared any))) (field i16) (field (mut (ref null $7))) (field (ref null $11)))))) -;; CHECK-NEXT: (type $6 (sub (shared (describes $5) (descriptor $7) (struct)))) -;; CHECK-NEXT: (type $7 (sub (shared (describes $6) (descriptor $9) (struct)))) -;; CHECK-NEXT: (type $8 (sub (struct (field v128) (field (ref null $6)) (field f64) (field v128)))) -;; CHECK-NEXT: (type $9 (sub (shared (describes $7) (descriptor $11) (struct (field (ref null $1)) (field (mut (ref null $7))) (field (mut i16)))))) -;; CHECK-NEXT: (type $10 (sub (func (result eqref)))) -;; CHECK-NEXT: (type $11 (shared (describes $9) (struct (field (mut (ref null $9))) (field (mut (ref (shared i31)))) (field (mut (ref null $5))) (field (mut f64))))) -;; CHECK-NEXT: (type $12 (sub (descriptor $13) (struct (field (ref $11)) (field (mut f64)) (field v128) (field (mut (ref any))) (field (mut v128))))) -;; CHECK-NEXT: (type $13 (sub (describes $12) (struct (field i64) (field (mut (ref null $0))) (field i16) (field f64)))) -;; CHECK-NEXT: (type $14 (sub final $8 (struct (field v128) (field (ref $6)) (field f64) (field v128)))) +;; CHECK-NEXT: (type $5 (sub (shared (descriptor $6) (struct (field (ref null $1)) (field (ref null (shared func))) (field (mut f32)) (field i8) (field (mut i8)))))) +;; CHECK-NEXT: (type $6 (shared (describes $5) (descriptor $7) (struct (field (mut (ref null $2))) (field (mut i8)) (field f32) (field (mut (ref $11))) (field f32)))) +;; CHECK-NEXT: (type $7 (shared (describes $6) (descriptor $9) (struct (field i32) (field v128) (field (ref null $11)) (field (mut (ref null $6))) (field (mut f64)) (field (ref null (shared extern)))))) +;; CHECK-NEXT: (type $8 (sub (struct (field (ref null $7)) (field i8) (field (mut (ref $6))) (field (mut externref))))) +;; CHECK-NEXT: (type $9 (shared (describes $7) (descriptor $11) (struct))) +;; CHECK-NEXT: (type $10 (sub (func (param f64 i32) (result f32)))) +;; CHECK-NEXT: (type $11 (sub (shared (describes $9) (struct (field (mut i8)) (field (mut (ref null $6))) (field f32) (field (ref null $2)))))) +;; CHECK-NEXT: (type $12 (sub (descriptor $13) (struct (field i32) (field (ref null $9)) (field (mut f32)) (field (mut i16)) (field f64)))) +;; CHECK-NEXT: (type $13 (describes $12) (struct (field externref) (field (ref $2)) (field (mut (ref $6))) (field (mut f32)))) +;; CHECK-NEXT: (type $14 (sub final $8 (struct (field (ref $7)) (field i8) (field (mut (ref $6))) (field (mut externref)) (field (mut (ref $1)))))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $15 (sub final $8 (struct (field v128) (field (ref null (shared none))) (field f64) (field v128) (field (mut (ref $11))) (field v128)))) -;; CHECK-NEXT: (type $16 (descriptor $18) (struct (field (mut v128)) (field (mut i64)) (field (mut (ref null $18))))) -;; CHECK-NEXT: (type $17 (func (param (ref $18) (ref null $13)) (result (ref null (shared i31))))) -;; CHECK-NEXT: (type $18 (describes $16) (struct (field externref) (field i8))) -;; CHECK-NEXT: (type $19 (cont $10)) -;; CHECK-NEXT: ) \ No newline at end of file +;; CHECK-NEXT: (type $15 (sub final $8 (struct (field (ref $7)) (field i8) (field (mut (ref $6))) (field (mut externref))))) +;; CHECK-NEXT: (type $16 (descriptor $18) (struct (field (ref null $2)) (field i16) (field i64) (field (ref $9)) (field (mut (ref $3))))) +;; CHECK-NEXT: (type $17 (sub (func (param f64) (result (ref null $19))))) +;; CHECK-NEXT: (type $18 (describes $16) (struct)) +;; CHECK-NEXT: (type $19 (cont $17)) +;; CHECK-NEXT: )