summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/wasm-type.h4
-rw-r--r--src/wasm/wasm-type.cpp145
2 files changed, 106 insertions, 43 deletions
diff --git a/src/wasm-type.h b/src/wasm-type.h
index 4c050ed35..7b63adf9d 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -594,6 +594,10 @@ struct TypeBuilder {
SelfSupertype,
// The declared supertype of a type is invalid.
InvalidSupertype,
+ // The declared supertype is an invalid forward reference.
+ ForwardSupertypeReference,
+ // A child of the type is an invalid forward reference.
+ ForwardChildReference,
};
struct Error {
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index b69977426..475e2e714 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -1323,6 +1323,10 @@ std::ostream& operator<<(std::ostream& os, TypeBuilder::ErrorReason reason) {
return os << "Heap type is a supertype of itself";
case TypeBuilder::ErrorReason::InvalidSupertype:
return os << "Heap type has an invalid supertype";
+ case TypeBuilder::ErrorReason::ForwardSupertypeReference:
+ return os << "Heap type has an undeclared supertype";
+ case TypeBuilder::ErrorReason::ForwardChildReference:
+ return os << "Heap type has an undeclared child";
}
WASM_UNREACHABLE("Unexpected error reason");
}
@@ -3113,6 +3117,50 @@ void canonicalizeEquirecursive(CanonicalizationState& state) {
}
std::optional<TypeBuilder::Error>
+validateStructuralSubtyping(const std::vector<HeapType>& types) {
+ for (size_t i = 0; i < types.size(); ++i) {
+ HeapType type = types[i];
+ if (type.isBasic()) {
+ continue;
+ }
+ auto* sub = getHeapTypeInfo(type);
+ auto* super = sub->supertype;
+ if (super == nullptr) {
+ continue;
+ }
+
+ auto fail = [&]() {
+ return TypeBuilder::Error{i, TypeBuilder::ErrorReason::InvalidSupertype};
+ };
+
+ if (sub->kind != super->kind) {
+ return fail();
+ }
+ SubTyper typer;
+ switch (sub->kind) {
+ case HeapTypeInfo::BasicKind:
+ WASM_UNREACHABLE("unexpected kind");
+ case HeapTypeInfo::SignatureKind:
+ if (!typer.isSubType(sub->signature, super->signature)) {
+ return fail();
+ }
+ break;
+ case HeapTypeInfo::StructKind:
+ if (!typer.isSubType(sub->struct_, super->struct_)) {
+ return fail();
+ }
+ break;
+ case HeapTypeInfo::ArrayKind:
+ if (!typer.isSubType(sub->array, super->array)) {
+ return fail();
+ }
+ break;
+ }
+ }
+ return {};
+}
+
+std::optional<TypeBuilder::Error>
canonicalizeNominal(CanonicalizationState& state) {
// TODO: clear recursion groups once we are no longer piggybacking the
// isorecursive system on the nominal system.
@@ -3150,45 +3198,9 @@ canonicalizeNominal(CanonicalizationState& state) {
checked.insert(path.begin(), path.end());
}
- // Ensure that all the subtype relations are valid.
- for (size_t i = 0; i < state.results.size(); ++i) {
- HeapType type = state.results[i];
- if (type.isBasic()) {
- continue;
- }
- auto* sub = getHeapTypeInfo(type);
- auto* super = sub->supertype;
- if (super == nullptr) {
- continue;
- }
-
- auto fail = [&]() {
- return TypeBuilder::Error{i, TypeBuilder::ErrorReason::InvalidSupertype};
- };
-
- if (sub->kind != super->kind) {
- return fail();
- }
- SubTyper typer;
- switch (sub->kind) {
- case HeapTypeInfo::BasicKind:
- WASM_UNREACHABLE("unexpected kind");
- case HeapTypeInfo::SignatureKind:
- if (!typer.isSubType(sub->signature, super->signature)) {
- return fail();
- }
- break;
- case HeapTypeInfo::StructKind:
- if (!typer.isSubType(sub->struct_, super->struct_)) {
- return fail();
- }
- break;
- case HeapTypeInfo::ArrayKind:
- if (!typer.isSubType(sub->array, super->array)) {
- return fail();
- }
- break;
- }
+ // Check that the declared supertypes are valid.
+ if (auto error = validateStructuralSubtyping(state.results)) {
+ return {*error};
}
#if TIME_CANONICALIZATION
@@ -3212,10 +3224,57 @@ std::optional<TypeBuilder::Error> canonicalizeIsorecursive(
}
}
- // TODO: proper isorecursive validation and canonicalization. For now just
- // piggyback on the nominal system.
- if (auto err = canonicalizeNominal(state)) {
- return err;
+ // Check that supertypes precede their subtypes and that other child types
+ // either precede their parents or appear later in the same recursion group.
+ // `indexOfType` both maps types to their indices and keeps track of which
+ // types we have seen so far.
+ std::unordered_map<HeapType, size_t> indexOfType;
+ std::optional<RecGroup> currGroup;
+ size_t groupStart = 0;
+
+ // Validate the children of all types in a recursion group after all the types
+ // have been registered in `indexOfType`.
+ auto finishGroup = [&](size_t groupEnd) -> std::optional<TypeBuilder::Error> {
+ for (size_t index = groupStart; index < groupEnd; ++index) {
+ HeapType type = state.results[index];
+ for (HeapType child : type.getHeapTypeChildren()) {
+ // Only basic children, globally canonical children, and children
+ // defined in this or previous recursion groups are allowed.
+ if (isTemp(child) && !indexOfType.count(child)) {
+ return {{index, TypeBuilder::ErrorReason::ForwardChildReference}};
+ }
+ }
+ }
+ groupStart = groupEnd;
+ return {};
+ };
+
+ for (size_t index = 0; index < state.results.size(); ++index) {
+ HeapType type = state.results[index];
+ // Validate the supertype. Supertypes must precede their subtypes.
+ if (auto super = type.getSuperType()) {
+ if (!indexOfType.count(*super)) {
+ return {{index, TypeBuilder::ErrorReason::ForwardSupertypeReference}};
+ }
+ }
+ // Check whether we have finished a rec group.
+ auto newGroup = type.getRecGroup();
+ if (currGroup && *currGroup != newGroup) {
+ if (auto error = finishGroup(index)) {
+ return *error;
+ }
+ }
+ currGroup = newGroup;
+ // Register this type as seen.
+ indexOfType.insert({type, index});
+ }
+ if (auto error = finishGroup(state.results.size())) {
+ return *error;
+ }
+
+ // Check that the declared supertypes are structurally valid.
+ if (auto error = validateStructuralSubtyping(state.results)) {
+ return {*error};
}
// Move the recursion groups into the global store. TODO: after proper