From f90d1db1b62bd67df8b92a4f36798eb2f8132a32 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 15 Apr 2026 17:57:44 -0700 Subject: [PATCH] Validate descriptor clause forward references When the descriptor clause on a type definition was a forward reference to a future rec group, we previously failed an assertion that it was not a temporary type. Replace this assertion with a validation check. Fixes #8606. --- src/wasm-type.h | 2 ++ src/wasm/wasm-type.cpp | 11 ++++++++++- test/lit/validation/descriptor-forward-reference.wast | 8 ++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 test/lit/validation/descriptor-forward-reference.wast diff --git a/src/wasm-type.h b/src/wasm-type.h index a0340c2b083..4033c6a36b6 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -917,6 +917,8 @@ struct TypeBuilder { NonStructDescribes, // The described type is an invalid forward reference. ForwardDescribesReference, + // The descriptor type is an invalid forward reference. + ForwardDescriptorReference, // The described type does not have this type as a descriptor. MismatchedDescribes, // A descriptor clause on a non-struct type. diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 8ff0e9c362a..8826985ca19 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -1528,6 +1528,8 @@ std::ostream& operator<<(std::ostream& os, return os << "Describes clause on a non-struct type"; case TypeBuilder::ErrorReasonKind::ForwardDescribesReference: return os << "Describes clause is a forward reference"; + case TypeBuilder::ErrorReasonKind::ForwardDescriptorReference: + return os << "Descriptor clause is a forward reference"; case TypeBuilder::ErrorReasonKind::MismatchedDescribes: return os << "Described type is not a matching descriptor"; case TypeBuilder::ErrorReasonKind::NonStructDescriptor: @@ -2509,7 +2511,6 @@ validateTypeInfo(HeapTypeInfo& info, if (info.kind != HeapTypeKind::Struct) { return TypeBuilder::ErrorReasonKind::NonStructDescribes; } - assert(desc->isTemp && "unexpected canonical described type"); if (!seenTypes.contains(HeapType(uintptr_t(desc)))) { return TypeBuilder::ErrorReasonKind::ForwardDescribesReference; } @@ -2654,6 +2655,14 @@ buildRecGroup(std::unique_ptr&& groupInfo, i, TypeBuilder::ErrorReasonKind::ForwardChildReference}}; } } + if (auto desc = type.getDescriptorType()) { + if (isTemp(*desc) && !seenTypes.contains(*desc)) { + return {TypeBuilder::Error{ + i, TypeBuilder::ErrorReasonKind::ForwardDescriptorReference}}; + } + } + // Describes clauses were already checked as we validated each type in the + // group. } // The rec group is valid, so we can try to move the group into the global rec diff --git a/test/lit/validation/descriptor-forward-reference.wast b/test/lit/validation/descriptor-forward-reference.wast new file mode 100644 index 00000000000..a5f74219cd4 --- /dev/null +++ b/test/lit/validation/descriptor-forward-reference.wast @@ -0,0 +1,8 @@ +;; RUN: not wasm-opt -all %s 2>&1 | filecheck %s + +(module + ;; These types should be in the same rec group! + ;; CHECK: invalid type: Descriptor clause is a forward reference + (type $Default (descriptor $Default.desc) (struct (field i32))) + (type $Default.desc (describes $Default) (struct (field (ref extern)))) +)