diff options
-rw-r--r-- | CHANGELOG.md | 6 | ||||
-rw-r--r-- | src/binaryen-c.cpp | 15 | ||||
-rw-r--r-- | src/binaryen-c.h | 9 | ||||
-rw-r--r-- | src/ir/module-utils.cpp | 28 | ||||
-rw-r--r-- | src/tools/fuzzing/heap-types.cpp | 39 | ||||
-rw-r--r-- | src/tools/tool-options.h | 4 | ||||
-rw-r--r-- | src/tools/wasm-fuzz-types.cpp | 56 | ||||
-rw-r--r-- | src/wasm-type.h | 11 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 32 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 161 | ||||
-rw-r--r-- | test/example/c-api-kitchen-sink.c | 19 | ||||
-rw-r--r-- | test/example/c-api-kitchen-sink.txt | 2 | ||||
-rw-r--r-- | test/example/type-builder-nominal.cpp | 428 | ||||
-rw-r--r-- | test/example/type-builder-nominal.txt | 102 | ||||
-rw-r--r-- | test/gtest/possible-contents.cpp | 6 | ||||
-rw-r--r-- | test/gtest/type-builder.cpp | 100 | ||||
-rw-r--r-- | test/gtest/type-test.h | 17 |
17 files changed, 94 insertions, 941 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f2d76ec92..6bad6aa3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,9 @@ Current Trunk - Some C and JS API functions now refer to data and element segments by name instead of index. -- The --nominal and --hybrid command line options have been removed. The only - supported type system is now the standard isorecursive (i.e. hybrid) type - system. +- The --nominal and --hybrid command line options and related API functions have + been removed. The only supported type system is now the standard isorecursive + (i.e. hybrid) type system. v112 ---- diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 01f14f924..b23d53ad6 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -406,21 +406,6 @@ BinaryenType BinaryenTypeFromHeapType(BinaryenHeapType heapType, .getID(); } -// TypeSystem - -BinaryenTypeSystem BinaryenTypeSystemNominal() { - return static_cast<BinaryenTypeSystem>(TypeSystem::Nominal); -} -BinaryenTypeSystem BinaryenTypeSystemIsorecursive() { - return static_cast<BinaryenTypeSystem>(TypeSystem::Isorecursive); -} -BinaryenTypeSystem BinaryenGetTypeSystem() { - return BinaryenTypeSystem(getTypeSystem()); -} -void BinaryenSetTypeSystem(BinaryenTypeSystem typeSystem) { - setTypeSystem(TypeSystem(typeSystem)); -} - // Expression ids BinaryenExpressionId BinaryenInvalidId(void) { diff --git a/src/binaryen-c.h b/src/binaryen-c.h index afbc13782..d05bdf33b 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -188,15 +188,6 @@ BINARYEN_API bool BinaryenTypeIsNullable(BinaryenType type); BINARYEN_API BinaryenType BinaryenTypeFromHeapType(BinaryenHeapType heapType, bool nullable); -// TypeSystem - -typedef uint32_t BinaryenTypeSystem; - -BINARYEN_API BinaryenTypeSystem BinaryenTypeSystemNominal(void); -BINARYEN_API BinaryenTypeSystem BinaryenTypeSystemIsorecursive(void); -BINARYEN_API BinaryenTypeSystem BinaryenGetTypeSystem(void); -BINARYEN_API void BinaryenSetTypeSystem(BinaryenTypeSystem typeSystem); - // Expression ids (call to get the value of each; you can cache them) typedef uint32_t BinaryenExpressionId; diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index efab9e20b..ce6fadda7 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -345,7 +345,6 @@ std::vector<HeapType> getPrivateHeapTypes(Module& wasm) { } IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) { - TypeSystem system = getTypeSystem(); Counts counts = getHeapTypeCounts(wasm); // Types have to be arranged into topologically ordered recursion groups. @@ -385,32 +384,21 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) { // Update the reference count. info.useCount += counts.at(type); // Collect predecessor groups. - switch (system) { - case TypeSystem::Isorecursive: - for (auto child : type.getReferencedHeapTypes()) { - if (!child.isBasic()) { - RecGroup otherGroup = child.getRecGroup(); - if (otherGroup != group) { - info.preds.insert(otherGroup); - } - } - } - break; - case TypeSystem::Nominal: - if (auto super = type.getSuperType()) { - info.preds.insert(super->getRecGroup()); + for (auto child : type.getReferencedHeapTypes()) { + if (!child.isBasic()) { + RecGroup otherGroup = child.getRecGroup(); + if (otherGroup != group) { + info.preds.insert(otherGroup); } - break; + } } } // Fix up the use counts to be averages to ensure groups are used comensurate // with the amount of index space they occupy. Skip this for nominal types // since their internal group size is always 1. - if (system != TypeSystem::Nominal) { - for (auto& [group, info] : groupInfos) { - info.useCount /= group.size(); - } + for (auto& [group, info] : groupInfos) { + info.useCount /= group.size(); } // Sort the predecessors so the most used will be visited first. diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 24054572d..e58c7f51d 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -65,8 +65,7 @@ struct HeapTypeGeneratorImpl { // Set up the subtype relationships. Start with some number of root types, // then after that start creating subtypes of existing types. Determine the // top-level kind of each type in advance so that we can appropriately use - // types we haven't constructed yet. For simplicity, always choose a - // supertype to bea previous type, which is valid in all type systems. + // types we haven't constructed yet. typeKinds.reserve(builder.size()); supertypeIndices.reserve(builder.size()); Index numRoots = 1 + rand.upTo(builder.size()); @@ -90,31 +89,23 @@ struct HeapTypeGeneratorImpl { // Initialize the recursion groups. recGroupEnds.reserve(builder.size()); - if (getTypeSystem() != TypeSystem::Isorecursive) { - // Recursion groups don't matter and we can choose children as though we - // had a single large recursion group. - for (Index i = 0; i < builder.size(); ++i) { - recGroupEnds.push_back(builder.size()); - } - } else { - // We are using isorecursive types, so create groups. Choose an expected - // group size uniformly at random, then create groups with random sizes on - // a geometric distribution based on that expected size. - size_t expectedSize = 1 + rand.upTo(builder.size()); - Index groupStart = 0; - for (Index i = 0; i < builder.size(); ++i) { - if (i == builder.size() - 1 || rand.oneIn(expectedSize)) { - // End the old group and create a new group. - Index newGroupStart = i + 1; - builder.createRecGroup(groupStart, newGroupStart - groupStart); - for (Index j = groupStart; j < newGroupStart; ++j) { - recGroupEnds.push_back(newGroupStart); - } - groupStart = newGroupStart; + // Create isorecursive recursion groups. Choose an expected group size + // uniformly at random, then create groups with random sizes on a geometric + // distribution based on that expected size. + size_t expectedSize = 1 + rand.upTo(builder.size()); + Index groupStart = 0; + for (Index i = 0; i < builder.size(); ++i) { + if (i == builder.size() - 1 || rand.oneIn(expectedSize)) { + // End the old group and create a new group. + Index newGroupStart = i + 1; + builder.createRecGroup(groupStart, newGroupStart - groupStart); + for (Index j = groupStart; j < newGroupStart; ++j) { + recGroupEnds.push_back(newGroupStart); } + groupStart = newGroupStart; } - assert(recGroupEnds.size() == builder.size()); } + assert(recGroupEnds.size() == builder.size()); // Create the heap types. for (; index < builder.size(); ++index) { diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h index 4e9d85d00..a6e57a76e 100644 --- a/src/tools/tool-options.h +++ b/src/tools/tool-options.h @@ -182,10 +182,6 @@ struct ToolOptions : public Options { void applyFeatures(Module& module) const { module.features.enable(enabledFeatures); module.features.disable(disabledFeatures); - // Non-default type systems only make sense with GC enabled. - if (!module.features.hasGC() && getTypeSystem() == TypeSystem::Nominal) { - Fatal() << "Nominal typing is only allowed when GC is enabled"; - } } private: diff --git a/src/tools/wasm-fuzz-types.cpp b/src/tools/wasm-fuzz-types.cpp index ce393d133..0dc9f9d17 100644 --- a/src/tools/wasm-fuzz-types.cpp +++ b/src/tools/wasm-fuzz-types.cpp @@ -228,11 +228,6 @@ void Fuzzer::checkCanonicalization() { // Check that structural canonicalization is working correctly by building the // types again, choosing randomly between equivalent possible children for // each definition from both the new and old sets of built types. - if (getTypeSystem() == TypeSystem::Nominal) { - // No canonicalization to check. - return; - } - TypeBuilder builder(types.size()); // Helper for creating new definitions of existing types, randomly choosing @@ -274,36 +269,29 @@ void Fuzzer::checkCanonicalization() { // Set up recursion groups and record group ends to ensure we only select // valid children. recGroupEnds.reserve(builder.size()); - if (getTypeSystem() != TypeSystem::Isorecursive) { - // No rec groups. - for (size_t i = 0; i < builder.size(); ++i) { - recGroupEnds.push_back(builder.size()); + // Set up recursion groups + std::optional<RecGroup> currGroup; + size_t currGroupStart = 0; + auto finishGroup = [&](Index end) { + builder.createRecGroup(currGroupStart, end - currGroupStart); + for (Index i = currGroupStart; i < end; ++i) { + recGroupEnds.push_back(end); } - } else { - // Set up recursion groups - std::optional<RecGroup> currGroup; - size_t currGroupStart = 0; - auto finishGroup = [&](Index end) { - builder.createRecGroup(currGroupStart, end - currGroupStart); - for (Index i = currGroupStart; i < end; ++i) { - recGroupEnds.push_back(end); - } - currGroupStart = end; - }; - for (Index i = 0; i < types.size(); ++i) { - auto type = types[i]; - if (type.isBasic()) { - continue; - } - auto newGroup = type.getRecGroup(); - if (!currGroup || newGroup != currGroup || - type == types[currGroupStart]) { - finishGroup(i); - currGroup = newGroup; - } + currGroupStart = end; + }; + for (Index i = 0; i < types.size(); ++i) { + auto type = types[i]; + if (type.isBasic()) { + continue; + } + auto newGroup = type.getRecGroup(); + if (!currGroup || newGroup != currGroup || + type == types[currGroupStart]) { + finishGroup(i); + currGroup = newGroup; } - finishGroup(builder.size()); } + finishGroup(builder.size()); // Copy the original types for (; index < types.size(); ++index) { @@ -349,7 +337,7 @@ void Fuzzer::checkCanonicalization() { assert(old.isBasic()); return {OldHeapType{old}}; } - if (!old.isBasic() && getTypeSystem() == TypeSystem::Isorecursive) { + if (!old.isBasic()) { // Check whether this child heap type is supposed to be a self-reference // into the recursion group we are defining. If it is, we must use the // corresponding type in the new recursion group, since anything else @@ -514,7 +502,7 @@ void Fuzzer::checkInhabitable() { } // TODO: We could also check that the transformed types are the same as the // original types up to nullability. - } else if (getTypeSystem() == TypeSystem::Isorecursive) { + } else { // Verify the produced inhabitable types are the same as the original types // (which also implies that they are indeed inhabitable). if (types.size() != inhabitable.size()) { diff --git a/src/wasm-type.h b/src/wasm-type.h index d72f5477d..ad0606d59 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -40,17 +40,6 @@ namespace wasm { -enum class TypeSystem { - Isorecursive, - Nominal, -}; - -// This should only ever be called before any Types or HeapTypes have been -// created. The default system is equirecursive. -void setTypeSystem(TypeSystem system); - -TypeSystem getTypeSystem(); - // Dangerous! Frees all types and heap types that have ever been created and // resets the type system's internal state. This is only really meant to be used // for tests. diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index e85740614..967356210 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -229,40 +229,22 @@ void WasmBinaryWriter::writeTypes() { // the type section. With nominal typing there is always one group and with // equirecursive typing there is one group per type. size_t numGroups = 0; - // MVP types are structural and do not use recursion groups. - TypeSystem typeSystem = getTypeSystem(); - if (!wasm->features.hasGC()) { - typeSystem = TypeSystem::Isorecursive; - } - switch (typeSystem) { - case TypeSystem::Nominal: - numGroups = 1; - break; - case TypeSystem::Isorecursive: { - std::optional<RecGroup> lastGroup; - for (auto type : indexedTypes.types) { - auto currGroup = type.getRecGroup(); - numGroups += lastGroup != currGroup; - lastGroup = currGroup; - } + { + std::optional<RecGroup> lastGroup; + for (auto type : indexedTypes.types) { + auto currGroup = type.getRecGroup(); + numGroups += lastGroup != currGroup; + lastGroup = currGroup; } } BYN_TRACE("== writeTypes\n"); auto start = startSection(BinaryConsts::Section::Type); o << U32LEB(numGroups); - if (typeSystem == TypeSystem::Nominal) { - // The nominal recursion group contains every type. - o << S32LEB(BinaryConsts::EncodedType::Rec) - << U32LEB(indexedTypes.types.size()); - } std::optional<RecGroup> lastGroup = std::nullopt; for (Index i = 0; i < indexedTypes.types.size(); ++i) { auto type = indexedTypes.types[i]; // Check whether we need to start a new recursion group. Recursion groups of - // size 1 are implicit, so only emit a group header for larger groups. This - // gracefully handles non-isorecursive type systems, which only have groups - // of size 1 internally (even though nominal types are emitted as a single - // large group). + // size 1 are implicit, so only emit a group header for larger groups. auto currGroup = type.getRecGroup(); if (lastGroup != currGroup && currGroup.size() > 1) { o << S32LEB(BinaryConsts::EncodedType::Rec) << U32LEB(currGroup.size()); diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 8b1dfb0af..c9c35ac49 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -44,12 +44,6 @@ namespace wasm { -static TypeSystem typeSystem = TypeSystem::Isorecursive; - -void setTypeSystem(TypeSystem system) { typeSystem = system; } - -TypeSystem getTypeSystem() { return typeSystem; } - namespace { struct TypeInfo { @@ -104,11 +98,10 @@ struct HeapTypeInfo { // Otherwise, the type definition tree is still being constructed via the // TypeBuilder interface, so hashing and equality use pointer identity. bool isFinalized = true; - // In nominal or isorecursive mode, the supertype of this HeapType, if it - // exists. + // The supertype of this HeapType, if it exists. HeapTypeInfo* supertype = nullptr; - // In isorecursive mode, the recursion group of this type or null if the - // recursion group is trivial (i.e. contains only this type). + // The recursion group of this type or null if the recursion group is trivial + // (i.e. contains only this type). RecGroupInfo* recGroup = nullptr; size_t recGroupIndex = 0; enum Kind { @@ -714,12 +707,6 @@ private: return *canonical; } std::lock_guard<std::recursive_mutex> lock(mutex); - // Nominal HeapTypes are always unique, so don't bother deduplicating them. - if constexpr (std::is_same_v<Info, HeapTypeInfo>) { - if (typeSystem == TypeSystem::Nominal) { - return insertNew(); - } - } // Check whether we already have a type for this structural Info. auto indexIt = typeIDs.find(std::cref(info)); if (indexIt != typeIDs.end()) { @@ -759,34 +746,6 @@ template<typename Info> bool Store<Info>::isGlobalStore() { } #endif -// Cache canonical nominal signature types. See comment in -// `HeapType::HeapType(Signature)`. -struct SignatureTypeCache { - std::unordered_map<Signature, HeapType> cache; - std::mutex mutex; - - HeapType getType(Signature sig) { - std::lock_guard<std::mutex> lock(mutex); - // Try inserting a placeholder type, then replace it with a real type if we - // don't already have a canonical type for this signature. - auto [entry, inserted] = cache.insert({sig, {}}); - auto& [_, type] = *entry; - if (inserted) { - type = globalHeapTypeStore.insert(sig); - } - return type; - } - - void insertType(HeapType type) { - std::lock_guard<std::mutex> lock(mutex); - cache.insert({type.getSignature(), type}); - } - - void clear() { cache.clear(); } -}; - -static SignatureTypeCache nominalSignatureCache; - // Keep track of the constructed recursion groups. struct RecGroupStore { std::mutex mutex; @@ -841,7 +800,6 @@ static RecGroupStore globalRecGroupStore; void destroyAllTypesForTestingPurposesOnly() { globalTypeStore.clear(); globalHeapTypeStore.clear(); - nominalSignatureCache.clear(); globalRecGroupStore.clear(); } @@ -1222,24 +1180,8 @@ const Type& Type::Iterator::operator*() const { HeapType::HeapType(Signature sig) { assert(!isTemp(sig.params) && "Leaking temporary type!"); assert(!isTemp(sig.results) && "Leaking temporary type!"); - switch (getTypeSystem()) { - case TypeSystem::Nominal: - // Special case the creation of signature types in nominal mode to return - // a "canonical" type for the signature, which happens to be the first one - // created. We depend on being able to create new function signatures in - // many places, and historically they have always been structural, so - // creating a copy of an existing signature did not result in any code - // bloat or semantic changes. To avoid regressions or significant changes - // of behavior in nominal mode, we cache the canonical heap types for each - // signature to emulate structural behavior. - new (this) HeapType(nominalSignatureCache.getType(sig)); - return; - case TypeSystem::Isorecursive: - new (this) HeapType( - globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(sig))); - return; - } - WASM_UNREACHABLE("unexpected type system"); + new (this) + HeapType(globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(sig))); } HeapType::HeapType(const Struct& struct_) { @@ -1248,16 +1190,8 @@ HeapType::HeapType(const Struct& struct_) { assert(!isTemp(field.type) && "Leaking temporary type!"); } #endif - switch (getTypeSystem()) { - case TypeSystem::Nominal: - new (this) HeapType(globalHeapTypeStore.insert(struct_)); - return; - case TypeSystem::Isorecursive: - new (this) HeapType( - globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(struct_))); - return; - } - WASM_UNREACHABLE("unexpected type system"); + new (this) HeapType( + globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(struct_))); } HeapType::HeapType(Struct&& struct_) { @@ -1266,30 +1200,14 @@ HeapType::HeapType(Struct&& struct_) { assert(!isTemp(field.type) && "Leaking temporary type!"); } #endif - switch (getTypeSystem()) { - case TypeSystem::Nominal: - new (this) HeapType(globalHeapTypeStore.insert(std::move(struct_))); - return; - case TypeSystem::Isorecursive: - new (this) HeapType(globalRecGroupStore.insert( - std::make_unique<HeapTypeInfo>(std::move(struct_)))); - return; - } - WASM_UNREACHABLE("unexpected type system"); + new (this) HeapType(globalRecGroupStore.insert( + std::make_unique<HeapTypeInfo>(std::move(struct_)))); } HeapType::HeapType(Array array) { assert(!isTemp(array.element.type) && "Leaking temporary type!"); - switch (getTypeSystem()) { - case TypeSystem::Nominal: - new (this) HeapType(globalHeapTypeStore.insert(array)); - return; - case TypeSystem::Isorecursive: - new (this) HeapType( - globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(array))); - return; - } - WASM_UNREACHABLE("unexpected type system"); + new (this) + HeapType(globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(array))); } bool HeapType::isFunction() const { @@ -2774,39 +2692,6 @@ validateSubtyping(const std::vector<HeapType>& types) { return {}; } -std::optional<TypeBuilder::Error> -canonicalizeNominal(CanonicalizationState& state) { - for (auto& info : state.newInfos) { - info->recGroup = nullptr; - } - - // Nominal types do not require separate canonicalization, so just validate - // that their subtyping is correct. - - // Ensure there are no cycles in the subtype graph. This is the classic DFA - // algorithm for detecting cycles, but in the form of a simple loop because - // each node (type) has at most one child (supertype). - std::unordered_set<HeapTypeInfo*> checked; - for (size_t i = 0; i < state.results.size(); ++i) { - HeapType type = state.results[i]; - if (type.isBasic()) { - continue; - } - std::unordered_set<HeapTypeInfo*> path; - for (auto* curr = getHeapTypeInfo(type); - curr != nullptr && !checked.count(curr); - curr = curr->supertype) { - if (!path.insert(curr).second) { - return TypeBuilder::Error{i, TypeBuilder::ErrorReason::SelfSupertype}; - } - } - // None of the types in `path` reach themselves. - checked.insert(path.begin(), path.end()); - } - - return {}; -} - std::optional<TypeBuilder::Error> canonicalizeIsorecursive( CanonicalizationState& state, std::vector<std::unique_ptr<RecGroupInfo>>& recGroupInfos) { @@ -2992,17 +2877,8 @@ TypeBuilder::BuildResult TypeBuilder::build() { auto start = std::chrono::steady_clock::now(); #endif - switch (typeSystem) { - case TypeSystem::Nominal: - if (auto error = canonicalizeNominal(state)) { - return {*error}; - } - break; - case TypeSystem::Isorecursive: - if (auto error = canonicalizeIsorecursive(state, impl->recGroups)) { - return {*error}; - } - break; + if (auto error = canonicalizeIsorecursive(state, impl->recGroups)) { + return {*error}; } #if TIME_CANONICALIZATION @@ -3026,17 +2902,6 @@ TypeBuilder::BuildResult TypeBuilder::build() { return {*error}; } - // Note built signature types. See comment in `HeapType::HeapType(Signature)`. - for (auto type : state.results) { - // Do not cache types with explicit supertypes (that is, whose supertype is - // not HeapType::func). We don't want to reuse such types because then we'd - // be adding subtyping relationships that are not in the input. - if (type.isSignature() && (getTypeSystem() == TypeSystem::Nominal) && - !type.getSuperType()) { - nominalSignatureCache.insertType(type); - } - } - return {state.results}; } diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 01cc6a9c0..760f4be1d 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -2122,23 +2122,7 @@ void test_func_opt() { BinaryenModuleDispose(module); } -void test_typesystem() { - BinaryenTypeSystem defaultTypeSystem = BinaryenGetTypeSystem(); - assert(defaultTypeSystem == BinaryenTypeSystemIsorecursive()); - BinaryenSetTypeSystem(BinaryenTypeSystemNominal()); - assert(BinaryenGetTypeSystem() == BinaryenTypeSystemNominal()); - printf("BinaryenTypeSystemNominal: %d\n", BinaryenTypeSystemNominal()); - BinaryenSetTypeSystem(BinaryenTypeSystemIsorecursive()); - assert(BinaryenGetTypeSystem() == BinaryenTypeSystemIsorecursive()); - printf("BinaryenTypeSystemIsorecursive: %d\n", - BinaryenTypeSystemIsorecursive()); - BinaryenSetTypeSystem(defaultTypeSystem); -} - void test_typebuilder() { - BinaryenTypeSystem defaultTypeSystem = BinaryenGetTypeSystem(); - BinaryenSetTypeSystem(BinaryenTypeSystemIsorecursive()); - printf("TypeBuilderErrorReasonSelfSupertype: %d\n", TypeBuilderErrorReasonSelfSupertype()); printf("TypeBuilderErrorReasonInvalidSupertype: %d\n", @@ -2344,8 +2328,6 @@ void test_typebuilder() { printf("module with recursive GC types:\n"); BinaryenModulePrint(module); BinaryenModuleDispose(module); - - BinaryenSetTypeSystem(defaultTypeSystem); } int main() { @@ -2360,7 +2342,6 @@ int main() { test_color_status(); test_for_each(); test_func_opt(); - test_typesystem(); test_typebuilder(); return 0; diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index dcfd6de8c..1c1a3b956 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -3147,8 +3147,6 @@ optimized: (i32.const 4) ) ) -BinaryenTypeSystemNominal: 1 -BinaryenTypeSystemIsorecursive: 0 TypeBuilderErrorReasonSelfSupertype: 0 TypeBuilderErrorReasonInvalidSupertype: 1 TypeBuilderErrorReasonForwardSupertypeReference: 2 diff --git a/test/example/type-builder-nominal.cpp b/test/example/type-builder-nominal.cpp deleted file mode 100644 index a3e8695ac..000000000 --- a/test/example/type-builder-nominal.cpp +++ /dev/null @@ -1,428 +0,0 @@ -#include <cassert> -#include <iostream> - -#include "wasm-builder.h" -#include "wasm-type-printing.h" -#include "wasm-type.h" - -using namespace wasm; - -// Construct Signature, Struct, and Array heap types using undefined types. -void test_builder() { - std::cout << ";; Test TypeBuilder\n"; - - // (type $sig (func (param (ref $struct)) (result (ref $array) i32))) - // (type $struct (struct (field (ref null $array)))) - // (type $array (array (mut anyref))) - - TypeBuilder builder; - assert(builder.size() == 0); - builder.grow(3); - assert(builder.size() == 3); - - Type refSig = builder.getTempRefType(builder[0], NonNullable); - Type refStruct = builder.getTempRefType(builder[1], NonNullable); - Type refArray = builder.getTempRefType(builder[2], NonNullable); - Type refNullArray = builder.getTempRefType(builder[2], Nullable); - Type refNullAny(HeapType::any, Nullable); - - Signature sig(refStruct, builder.getTempTupleType({refArray, Type::i32})); - Struct struct_({Field(refNullArray, Immutable)}); - Array array(Field(refNullAny, Mutable)); - - { - IndexedTypeNameGenerator print(builder); - std::cout << "Before setting heap types:\n"; - std::cout << "$sig => " << print(builder[0]) << "\n"; - std::cout << "$struct => " << print(builder[1]) << "\n"; - std::cout << "$array => " << print(builder[2]) << "\n"; - std::cout << "(ref $sig) => " << print(refSig) << "\n"; - std::cout << "(ref $struct) => " << print(refStruct) << "\n"; - std::cout << "(ref $array) => " << print(refArray) << "\n"; - std::cout << "(ref null $array) => " << print(refNullArray) << "\n"; - } - - builder[0] = sig; - builder[1] = struct_; - builder[2] = array; - - { - IndexedTypeNameGenerator print(builder); - std::cout << "After setting heap types:\n"; - std::cout << "$sig => " << print(builder[0]) << "\n"; - std::cout << "$struct => " << print(builder[1]) << "\n"; - std::cout << "$array => " << print(builder[2]) << "\n"; - std::cout << "(ref $sig) => " << print(refSig) << "\n"; - std::cout << "(ref $struct) => " << print(refStruct) << "\n"; - std::cout << "(ref $array) => " << print(refArray) << "\n"; - std::cout << "(ref null $array) => " << print(refNullArray) << "\n"; - } - - std::vector<HeapType> built = *builder.build(); - - Type newRefSig = Type(built[0], NonNullable); - Type newRefStruct = Type(built[1], NonNullable); - Type newRefArray = Type(built[2], NonNullable); - Type newRefNullArray = Type(built[2], Nullable); - - { - IndexedTypeNameGenerator print(built); - std::cout << "After building types:\n"; - std::cout << "$sig => " << print(built[0]) << "\n"; - std::cout << "$struct => " << print(built[1]) << "\n"; - std::cout << "$array => " << print(built[2]) << "\n"; - std::cout << "(ref $sig) => " << print(newRefSig) << "\n"; - std::cout << "(ref $struct) => " << print(newRefStruct) << "\n"; - std::cout << "(ref $array) => " << print(newRefArray) << "\n"; - std::cout << "(ref null $array) => " << print(newRefNullArray) << "\n"; - } -} - -// Check that the builder works when there are duplicate definitions -void test_canonicalization() { - std::cout << ";; Test canonicalization\n"; - - // (type $struct (struct (field (ref null $sig) (ref null $sig)))) - // (type $sig (func)) - HeapType sig = Signature(Type::none, Type::none); - HeapType struct_ = Struct({Field(Type(sig, Nullable), Immutable), - Field(Type(sig, Nullable), Immutable)}); - - TypeBuilder builder(4); - - Type tempSigRef1 = builder.getTempRefType(builder[2], Nullable); - Type tempSigRef2 = builder.getTempRefType(builder[3], Nullable); - - assert(tempSigRef1 != tempSigRef2); - assert(tempSigRef1 != Type(sig, Nullable)); - assert(tempSigRef2 != Type(sig, Nullable)); - - builder[0] = - Struct({Field(tempSigRef1, Immutable), Field(tempSigRef1, Immutable)}); - builder[1] = - Struct({Field(tempSigRef2, Immutable), Field(tempSigRef2, Immutable)}); - builder[2] = Signature(Type::none, Type::none); - builder[3] = Signature(Type::none, Type::none); - - std::vector<HeapType> built = *builder.build(); - - assert(built[0] != struct_); - assert(built[1] != struct_); - assert(built[0] != built[1]); - assert(built[2] != sig); - assert(built[3] != sig); - assert(built[2] != built[3]); -} - -// Check that defined basic HeapTypes are handled correctly. -void test_basic() { - std::cout << ";; Test basic\n"; - - Type canonAnyref = Type(HeapType::any, Nullable); - Type canonI31ref = Type(HeapType::i31, NonNullable); - - TypeBuilder builder(6); - - Type anyref = builder.getTempRefType(builder[4], Nullable); - Type i31ref = builder.getTempRefType(builder[5], NonNullable); - - builder[0] = Signature(canonAnyref, canonI31ref); - builder[1] = Signature(anyref, canonI31ref); - builder[2] = Signature(canonAnyref, i31ref); - builder[3] = Signature(anyref, i31ref); - builder[4] = HeapType::any; - builder[5] = HeapType::i31; - - std::vector<HeapType> built = *builder.build(); - - assert(built[0].getSignature() == Signature(canonAnyref, canonI31ref)); - assert(built[1].getSignature() == built[0].getSignature()); - assert(built[2].getSignature() == built[1].getSignature()); - assert(built[3].getSignature() == built[2].getSignature()); - assert(built[4] == HeapType::any); - assert(built[5] == HeapType::i31); -} - -// Check that signatures created with TypeBuilders are properly recorded as -// canonical. -void test_signatures(bool warm) { - std::cout << ";; Test canonical signatures\n"; - - Type canonAnyref = Type(HeapType::any, Nullable); - Type canonI31ref = Type(HeapType::i31, NonNullable); - - TypeBuilder builder(2); - Type tempRef = builder.getTempRefType(builder[0], Nullable); - builder[0] = Signature(canonI31ref, canonAnyref); - builder[1] = Signature(tempRef, tempRef); - std::vector<HeapType> built = *builder.build(); - - HeapType small = Signature(canonI31ref, canonAnyref); - HeapType big = Signature(Type(Signature(canonI31ref, canonAnyref), Nullable), - Type(Signature(canonI31ref, canonAnyref), Nullable)); - if (warm) { - assert(built[0] != small); - assert(built[1] != big); - } else { - assert(built[0] == small); - assert(built[1] == big); - } -} - -void test_recursive() { - std::cout << ";; Test recursive types\n"; - - { - // Trivial recursion - std::vector<HeapType> built; - { - TypeBuilder builder(1); - Type temp = builder.getTempRefType(builder[0], Nullable); - builder[0] = Signature(Type::none, temp); - built = *builder.build(); - } - IndexedTypeNameGenerator print(built); - std::cout << print(built[0]) << "\n\n"; - assert(built[0] == built[0].getSignature().results.getHeapType()); - assert(Type(built[0], Nullable) == built[0].getSignature().results); - } - - { - // Mutual recursion - std::vector<HeapType> built; - { - TypeBuilder builder(2); - Type temp0 = builder.getTempRefType(builder[0], Nullable); - Type temp1 = builder.getTempRefType(builder[1], Nullable); - builder[0] = Signature(Type::none, temp1); - builder[1] = Signature(Type::none, temp0); - built = *builder.build(); - } - IndexedTypeNameGenerator print(built); - std::cout << print(built[0]) << "\n"; - std::cout << print(built[1]) << "\n\n"; - assert(built[0].getSignature().results.getHeapType() == built[1]); - assert(built[1].getSignature().results.getHeapType() == built[0]); - assert(built[0] != built[1]); - } - - { - // A longer chain of recursion - std::vector<HeapType> built; - { - TypeBuilder builder(5); - Type temp0 = builder.getTempRefType(builder[0], Nullable); - Type temp1 = builder.getTempRefType(builder[1], Nullable); - Type temp2 = builder.getTempRefType(builder[2], Nullable); - Type temp3 = builder.getTempRefType(builder[3], Nullable); - Type temp4 = builder.getTempRefType(builder[4], Nullable); - builder[0] = Signature(Type::none, temp1); - builder[1] = Signature(Type::none, temp2); - builder[2] = Signature(Type::none, temp3); - builder[3] = Signature(Type::none, temp4); - builder[4] = Signature(Type::none, temp0); - built = *builder.build(); - } - IndexedTypeNameGenerator print(built); - std::cout << print(built[0]) << "\n"; - std::cout << print(built[1]) << "\n"; - std::cout << print(built[2]) << "\n"; - std::cout << print(built[3]) << "\n"; - std::cout << print(built[4]) << "\n\n"; - assert(built[0].getSignature().results.getHeapType() == built[1]); - assert(built[1].getSignature().results.getHeapType() == built[2]); - assert(built[2].getSignature().results.getHeapType() == built[3]); - assert(built[3].getSignature().results.getHeapType() == built[4]); - assert(built[4].getSignature().results.getHeapType() == built[0]); - assert(built[0] != built[1]); - assert(built[0] != built[2]); - assert(built[0] != built[3]); - assert(built[0] != built[4]); - assert(built[1] != built[2]); - assert(built[1] != built[3]); - assert(built[1] != built[4]); - assert(built[2] != built[3]); - assert(built[2] != built[4]); - assert(built[3] != built[4]); - } - - { - // Check canonicalization for non-recursive parents and children of - // recursive HeapTypes. - std::vector<HeapType> built; - { - TypeBuilder builder(6); - Type temp0 = builder.getTempRefType(builder[0], Nullable); - Type temp1 = builder.getTempRefType(builder[1], Nullable); - Type temp2 = builder.getTempRefType(builder[2], Nullable); - Type temp3 = builder.getTempRefType(builder[3], Nullable); - Type tuple0_2 = builder.getTempTupleType({temp0, temp2}); - Type tuple1_3 = builder.getTempTupleType({temp1, temp3}); - builder[0] = Signature(Type::none, tuple0_2); - builder[1] = Signature(Type::none, tuple1_3); - builder[2] = Signature(); - builder[3] = Signature(); - builder[4] = Signature(Type::none, temp0); - builder[5] = Signature(Type::none, temp1); - built = *builder.build(); - } - IndexedTypeNameGenerator print(built); - std::cout << print(built[0]) << "\n"; - std::cout << print(built[1]) << "\n"; - std::cout << print(built[2]) << "\n"; - std::cout << print(built[3]) << "\n"; - std::cout << print(built[4]) << "\n"; - std::cout << print(built[5]) << "\n\n"; - assert(built[0] != built[1]); - assert(built[2] != built[3]); - assert(built[4] != built[5]); - assert(built[4].getSignature().results.getHeapType() == built[0]); - assert(built[5].getSignature().results.getHeapType() == built[1]); - assert(built[0].getSignature().results == - Type({Type(built[0], Nullable), Type(built[2], Nullable)})); - assert(built[1].getSignature().results == - Type({Type(built[1], Nullable), Type(built[3], Nullable)})); - } - - { - // Folded and unfolded - std::vector<HeapType> built; - { - TypeBuilder builder(2); - Type temp0 = builder.getTempRefType(builder[0], Nullable); - builder[0] = Signature(Type::none, temp0); - builder[1] = Signature(Type::none, temp0); - built = *builder.build(); - } - IndexedTypeNameGenerator print(built); - std::cout << print(built[0]) << "\n"; - std::cout << print(built[1]) << "\n\n"; - assert(built[0].getSignature().results.getHeapType() == built[0]); - assert(built[1].getSignature().results.getHeapType() == built[0]); - assert(built[0] != built[1]); - } -} - -void test_subtypes() { - std::cout << ";; Test subtyping\n"; - - Type anyref = Type(HeapType::any, Nullable); - Type eqref = Type(HeapType::eq, Nullable); - - auto LUB = [&](HeapType a, HeapType b) { - Type refA = Type(a, Nullable); - Type refB = Type(b, Nullable); - Type lubAB = Type::getLeastUpperBound(refA, refB); - Type lubBA = Type::getLeastUpperBound(refB, refA); - assert(lubAB == lubBA); - assert(lubAB != Type::none); - HeapType lub = lubAB.getHeapType(); - assert(Type::hasLeastUpperBound(refA, refB)); - assert(Type::hasLeastUpperBound(refB, refA)); - assert(Type::isSubType(refA, lubAB)); - assert(Type::isSubType(refB, lubAB)); - assert(HeapType::isSubType(a, lub)); - assert(HeapType::isSubType(b, lub)); - assert(lub == a || !HeapType::isSubType(lub, a)); - assert(lub == b || !HeapType::isSubType(lub, b)); - return lub; - }; - - { - // Subtype declarations, but still no subtypes - std::vector<HeapType> built; - { - TypeBuilder builder(3); - builder[0].subTypeOf(builder[1]); - builder[0] = Struct{}; - builder[1] = Struct{}; - builder[2] = Struct{}; - built = *builder.build(); - } - assert(LUB(built[0], built[2]) == HeapType::struct_); - } - - { - // Subtyping of identical types - std::vector<HeapType> built; - { - TypeBuilder builder(6); - builder[0].subTypeOf(builder[1]); - builder[2].subTypeOf(builder[3]); - builder[4].subTypeOf(builder[5]); - builder[0] = Struct({Field(Type::i32, Mutable), Field(anyref, Mutable)}); - builder[1] = Struct({Field(Type::i32, Mutable), Field(anyref, Mutable)}); - builder[2] = Signature(Type::i32, anyref); - builder[3] = Signature(Type::i32, anyref); - builder[4] = Array(Field(Type::i32, Mutable)); - builder[5] = Array(Field(Type::i32, Mutable)); - built = *builder.build(); - } - assert(LUB(built[0], built[1]) == built[1]); - assert(LUB(built[2], built[3]) == built[3]); - assert(LUB(built[4], built[5]) == built[5]); - } - - { - // Width subtyping - std::vector<HeapType> built; - { - TypeBuilder builder(2); - builder[0] = Struct({Field(Type::i32, Immutable)}); - builder[1] = - Struct({Field(Type::i32, Immutable), Field(Type::i32, Immutable)}); - builder[1].subTypeOf(builder[0]); - built = *builder.build(); - } - assert(LUB(built[1], built[0]) == built[0]); - } - - { - // Depth subtyping - std::vector<HeapType> built; - { - TypeBuilder builder(2); - builder[0] = Struct({Field(anyref, Immutable)}); - builder[1] = Struct({Field(eqref, Immutable)}); - builder[1].subTypeOf(builder[0]); - built = *builder.build(); - } - assert(LUB(built[1], built[0]) == built[0]); - } - - { - // Mutually recursive subtyping - std::vector<HeapType> built; - { - TypeBuilder builder(4); - Type a = builder.getTempRefType(builder[0], Nullable); - Type b = builder.getTempRefType(builder[1], Nullable); - Type c = builder.getTempRefType(builder[2], Nullable); - Type d = builder.getTempRefType(builder[3], Nullable); - builder[1].subTypeOf(builder[0]); - builder[3].subTypeOf(builder[2]); - builder[0] = Struct({Field(c, Immutable)}); - builder[1] = Struct({Field(d, Immutable)}); - builder[2] = Struct({Field(a, Immutable)}); - builder[3] = Struct({Field(b, Immutable)}); - built = *builder.build(); - } - assert(LUB(built[0], built[1]) == built[0]); - assert(LUB(built[2], built[3]) == built[2]); - } -} - -int main() { - wasm::setTypeSystem(TypeSystem::Nominal); - - // Run the tests twice to ensure things still work when the global stores are - // already populated. - for (size_t i = 0; i < 2; ++i) { - test_builder(); - test_canonicalization(); - test_basic(); - test_signatures(i == 1); - test_recursive(); - test_subtypes(); - } -} diff --git a/test/example/type-builder-nominal.txt b/test/example/type-builder-nominal.txt deleted file mode 100644 index 4da8857f2..000000000 --- a/test/example/type-builder-nominal.txt +++ /dev/null @@ -1,102 +0,0 @@ -;; Test TypeBuilder -Before setting heap types: -$sig => (type $0 (; temp ;) (func)) -$struct => (type $1 (; temp ;) (func)) -$array => (type $2 (; temp ;) (func)) -(ref $sig) => (; temp ;) (ref $0) -(ref $struct) => (; temp ;) (ref $1) -(ref $array) => (; temp ;) (ref $2) -(ref null $array) => (; temp ;) (ref null $2) -After setting heap types: -$sig => (type $0 (; temp ;) (func (param (; temp ;) (ref $1)) (result (; temp ;) (ref $2) i32))) -$struct => (type $1 (; temp ;) (struct (field (; temp ;) (ref null $2)))) -$array => (type $2 (; temp ;) (array (mut anyref))) -(ref $sig) => (; temp ;) (ref $0) -(ref $struct) => (; temp ;) (ref $1) -(ref $array) => (; temp ;) (ref $2) -(ref null $array) => (; temp ;) (ref null $2) -After building types: -$sig => (type $0 (func (param (ref $1)) (result (ref $2) i32))) -$struct => (type $1 (struct (field (ref null $2)))) -$array => (type $2 (array (mut anyref))) -(ref $sig) => (ref $0) -(ref $struct) => (ref $1) -(ref $array) => (ref $2) -(ref null $array) => (ref null $2) -;; Test canonicalization -;; Test basic -;; Test canonical signatures -;; Test recursive types -(type $0 (func (result (ref null $0)))) - -(type $0 (func (result (ref null $1)))) -(type $1 (func (result (ref null $0)))) - -(type $0 (func (result (ref null $1)))) -(type $1 (func (result (ref null $2)))) -(type $2 (func (result (ref null $3)))) -(type $3 (func (result (ref null $4)))) -(type $4 (func (result (ref null $0)))) - -(type $0 (func (result (ref null $0) (ref null $2)))) -(type $1 (func (result (ref null $1) (ref null $3)))) -(type $2 (func)) -(type $3 (func)) -(type $4 (func (result (ref null $0)))) -(type $5 (func (result (ref null $1)))) - -(type $0 (func (result (ref null $0)))) -(type $1 (func (result (ref null $0)))) - -;; Test subtyping -;; Test TypeBuilder -Before setting heap types: -$sig => (type $0 (; temp ;) (func)) -$struct => (type $1 (; temp ;) (func)) -$array => (type $2 (; temp ;) (func)) -(ref $sig) => (; temp ;) (ref $0) -(ref $struct) => (; temp ;) (ref $1) -(ref $array) => (; temp ;) (ref $2) -(ref null $array) => (; temp ;) (ref null $2) -After setting heap types: -$sig => (type $0 (; temp ;) (func (param (; temp ;) (ref $1)) (result (; temp ;) (ref $2) i32))) -$struct => (type $1 (; temp ;) (struct (field (; temp ;) (ref null $2)))) -$array => (type $2 (; temp ;) (array (mut anyref))) -(ref $sig) => (; temp ;) (ref $0) -(ref $struct) => (; temp ;) (ref $1) -(ref $array) => (; temp ;) (ref $2) -(ref null $array) => (; temp ;) (ref null $2) -After building types: -$sig => (type $0 (func (param (ref $1)) (result (ref $2) i32))) -$struct => (type $1 (struct (field (ref null $2)))) -$array => (type $2 (array (mut anyref))) -(ref $sig) => (ref $0) -(ref $struct) => (ref $1) -(ref $array) => (ref $2) -(ref null $array) => (ref null $2) -;; Test canonicalization -;; Test basic -;; Test canonical signatures -;; Test recursive types -(type $0 (func (result (ref null $0)))) - -(type $0 (func (result (ref null $1)))) -(type $1 (func (result (ref null $0)))) - -(type $0 (func (result (ref null $1)))) -(type $1 (func (result (ref null $2)))) -(type $2 (func (result (ref null $3)))) -(type $3 (func (result (ref null $4)))) -(type $4 (func (result (ref null $0)))) - -(type $0 (func (result (ref null $0) (ref null $2)))) -(type $1 (func (result (ref null $1) (ref null $3)))) -(type $2 (func)) -(type $3 (func)) -(type $4 (func (result (ref null $0)))) -(type $5 (func (result (ref null $1)))) - -(type $0 (func (result (ref null $0)))) -(type $1 (func (result (ref null $0)))) - -;; Test subtyping diff --git a/test/gtest/possible-contents.cpp b/test/gtest/possible-contents.cpp index dc10c00bf..85a4e86fd 100644 --- a/test/gtest/possible-contents.cpp +++ b/test/gtest/possible-contents.cpp @@ -66,11 +66,6 @@ static std::unique_ptr<Module> parse(std::string module) { // that. class PossibleContentsTest : public testing::Test { protected: - void SetUp() override { - // Use nominal typing to test struct types. - wasm::setTypeSystem(TypeSystem::Nominal); - } - Type anyref = Type(HeapType::any, Nullable); Type funcref = Type(HeapType::func, Nullable); Type i31ref = Type(HeapType::i31, Nullable); @@ -542,6 +537,7 @@ TEST_F(PossibleContentsTest, TestStructCones) { D */ TypeBuilder builder(5); + builder.createRecGroup(0, 5); builder.setHeapType(0, Struct(FieldList{})); builder.setHeapType(1, Struct(FieldList{})); builder.setHeapType(2, Struct(FieldList{})); diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index af6613bd2..7d5f5aa98 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -156,7 +156,7 @@ TEST_F(TypeTest, ModuleTypePrinter) { EXPECT_EQ(stream.str(), "(type $1 (struct (field i32)))"); } -TEST_F(IsorecursiveTest, Basics) { +TEST_F(TypeTest, Basics) { // (type $sig (func (param (ref $struct)) (result (ref $array) i32))) // (type $struct (struct (field (ref null $array)))) // (type $array (array (mut anyref))) @@ -207,7 +207,7 @@ TEST_F(IsorecursiveTest, Basics) { EXPECT_NE(newRefNullArray, refNullArray); } -static void testDirectSelfSupertype() { +TEST_F(TypeTest, DirectSelfSupertype) { // Type is directly a supertype of itself. TypeBuilder builder(1); builder[0] = Struct{}; @@ -218,19 +218,11 @@ static void testDirectSelfSupertype() { const auto* error = result.getError(); ASSERT_TRUE(error); - if (getTypeSystem() == TypeSystem::Nominal) { - EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::SelfSupertype); - } else if (getTypeSystem() == TypeSystem::Isorecursive) { - EXPECT_EQ(error->reason, - TypeBuilder::ErrorReason::ForwardSupertypeReference); - } + EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::ForwardSupertypeReference); EXPECT_EQ(error->index, 0u); } -TEST_F(NominalTest, DirectSelfSupertype) { testDirectSelfSupertype(); } -TEST_F(IsorecursiveTest, DirectSelfSupertype) { testDirectSelfSupertype(); } - -static void testIndirectSelfSupertype() { +TEST_F(TypeTest, IndirectSelfSupertype) { // Type is indirectly a supertype of itself. TypeBuilder builder(2); builder.createRecGroup(0, 2); @@ -244,21 +236,11 @@ static void testIndirectSelfSupertype() { const auto* error = result.getError(); ASSERT_TRUE(error); - if (getTypeSystem() == TypeSystem::Nominal) { - EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::SelfSupertype); - } else if (getTypeSystem() == TypeSystem::Isorecursive) { - EXPECT_EQ(error->reason, - TypeBuilder::ErrorReason::ForwardSupertypeReference); - } else { - WASM_UNREACHABLE("unexpected type system"); - } + EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::ForwardSupertypeReference); EXPECT_EQ(error->index, 0u); } -TEST_F(NominalTest, IndirectSelfSupertype) { testIndirectSelfSupertype(); } -TEST_F(IsorecursiveTest, IndirectSelfSupertype) { testIndirectSelfSupertype(); } - -static void testInvalidSupertype() { +TEST_F(TypeTest, InvalidSupertype) { TypeBuilder builder(2); builder.createRecGroup(0, 2); builder[0] = Struct({Field(Type::i32, Immutable)}); @@ -274,10 +256,7 @@ static void testInvalidSupertype() { EXPECT_EQ(error->index, 1u); } -TEST_F(NominalTest, InvalidSupertype) { testInvalidSupertype(); } -TEST_F(IsorecursiveTest, InvalidSupertype) { testInvalidSupertype(); } - -TEST_F(IsorecursiveTest, ForwardReferencedChild) { +TEST_F(TypeTest, ForwardReferencedChild) { TypeBuilder builder(3); builder.createRecGroup(0, 2); Type refA1 = builder.getTempRefType(builder[1], Nullable); @@ -297,7 +276,7 @@ TEST_F(IsorecursiveTest, ForwardReferencedChild) { EXPECT_EQ(error->index, 1u); } -TEST_F(IsorecursiveTest, RecGroupIndices) { +TEST_F(TypeTest, RecGroupIndices) { TypeBuilder builder(5); builder.createRecGroup(0, 2); @@ -324,7 +303,7 @@ TEST_F(IsorecursiveTest, RecGroupIndices) { EXPECT_EQ(built[4].getRecGroupIndex(), 2u); } -TEST_F(IsorecursiveTest, CanonicalizeGroups) { +TEST_F(TypeTest, CanonicalizeGroups) { // Trivial types in the same group are not equivalent. TypeBuilder builderA(2); builderA.createRecGroup(0, 2); @@ -374,7 +353,7 @@ TEST_F(IsorecursiveTest, CanonicalizeGroups) { EXPECT_EQ(builtB2[0], builtB[0]); } -TEST_F(IsorecursiveTest, CanonicalizeUses) { +TEST_F(TypeTest, CanonicalizeUses) { TypeBuilder builder(8); builder[0] = makeStruct(builder, {}); builder[1] = makeStruct(builder, {}); @@ -402,7 +381,7 @@ TEST_F(IsorecursiveTest, CanonicalizeUses) { EXPECT_NE(built[4], built[6]); } -TEST_F(IsorecursiveTest, CanonicalizeSelfReferences) { +TEST_F(TypeTest, CanonicalizeSelfReferences) { TypeBuilder builder(5); // Single self-reference builder[0] = makeStruct(builder, {0}); @@ -428,7 +407,7 @@ TEST_F(IsorecursiveTest, CanonicalizeSelfReferences) { EXPECT_NE(built[3], built[4]); } -TEST_F(IsorecursiveTest, CanonicalizeSupertypes) { +TEST_F(TypeTest, CanonicalizeSupertypes) { TypeBuilder builder(6); builder[0] = Struct{}; builder[1] = Struct{}; @@ -455,7 +434,7 @@ TEST_F(IsorecursiveTest, CanonicalizeSupertypes) { EXPECT_NE(built[4], built[5]); } -TEST_F(IsorecursiveTest, HeapTypeConstructors) { +TEST_F(TypeTest, HeapTypeConstructors) { HeapType sig(Signature(Type::i32, Type::i32)); HeapType struct_(Struct({Field(Type(sig, Nullable), Mutable)})); HeapType array(Field(Type(struct_, Nullable), Mutable)); @@ -484,7 +463,7 @@ TEST_F(IsorecursiveTest, HeapTypeConstructors) { EXPECT_EQ(array, array2); } -TEST_F(IsorecursiveTest, CanonicalizeTypesBeforeSubtyping) { +TEST_F(TypeTest, CanonicalizeTypesBeforeSubtyping) { TypeBuilder builder(6); // A rec group builder.createRecGroup(0, 2); @@ -510,7 +489,7 @@ TEST_F(IsorecursiveTest, CanonicalizeTypesBeforeSubtyping) { EXPECT_TRUE(result); } -TEST_F(IsorecursiveTest, CanonicalizeBasicTypes) { +TEST_F(TypeTest, CanonicalizeBasicTypes) { TypeBuilder builder(5); Type anyref = builder.getTempRefType(builder[0], Nullable); @@ -532,7 +511,7 @@ TEST_F(IsorecursiveTest, CanonicalizeBasicTypes) { EXPECT_EQ(built[3], built[4]); } -TEST_F(IsorecursiveTest, TestHeapTypeRelations) { +TEST_F(TypeTest, TestHeapTypeRelations) { HeapType ext = HeapType::ext; HeapType func = HeapType::func; HeapType any = HeapType::any; @@ -829,7 +808,7 @@ TEST_F(IsorecursiveTest, TestHeapTypeRelations) { } } -TEST_F(IsorecursiveTest, TestSubtypeErrors) { +TEST_F(TypeTest, TestSubtypeErrors) { Type anyref = Type(HeapType::any, Nullable); Type eqref = Type(HeapType::eq, Nullable); Type funcref = Type(HeapType::func, Nullable); @@ -872,7 +851,7 @@ TEST_F(IsorecursiveTest, TestSubtypeErrors) { } // Test SubTypes utility code. -TEST_F(NominalTest, TestSubTypes) { +TEST_F(TypeTest, TestSubTypes) { Type anyref = Type(HeapType::any, Nullable); Type eqref = Type(HeapType::eq, Nullable); @@ -907,40 +886,7 @@ TEST_F(NominalTest, TestSubTypes) { } // Test reuse of a previously built type as supertype. -TEST_F(NominalTest, TestExistingSuperType) { - // Build an initial type A - Type A; - { - TypeBuilder builder(1); - builder[0] = Struct(); - auto result = builder.build(); - ASSERT_TRUE(result); - auto built = *result; - A = Type(built[0], Nullable); - } - - // Build a type B <: A using a new builder - Type B; - { - TypeBuilder builder(1); - builder[0] = Struct(); - builder.setSubType(0, A.getHeapType()); - auto result = builder.build(); - ASSERT_TRUE(result); - auto built = *result; - B = Type(built[0], Nullable); - } - - // Test that B <: A where A is the initial type A - auto superOfB = B.getHeapType().getSuperType(); - ASSERT_TRUE(superOfB); - EXPECT_EQ(*superOfB, A.getHeapType()); - EXPECT_NE(B.getHeapType(), A.getHeapType()); -} - -// Test reuse of a previously built type as supertype, where in isorecursive -// mode canonicalization is performed. -TEST_F(IsorecursiveTest, TestExistingSuperType) { +TEST_F(TypeTest, TestExistingSuperType) { // Build an initial type A1 Type A1; { @@ -993,7 +939,7 @@ TEST_F(IsorecursiveTest, TestExistingSuperType) { } // Test .getMaxDepths() helper. -TEST_F(NominalTest, TestMaxStructDepths) { +TEST_F(TypeTest, TestMaxStructDepths) { /* A | @@ -1022,7 +968,7 @@ TEST_F(NominalTest, TestMaxStructDepths) { EXPECT_EQ(maxDepths[HeapType::any], Index(4)); } -TEST_F(NominalTest, TestMaxArrayDepths) { +TEST_F(TypeTest, TestMaxArrayDepths) { HeapType A; { TypeBuilder builder(1); @@ -1043,7 +989,7 @@ TEST_F(NominalTest, TestMaxArrayDepths) { } // Test .depth() helper. -TEST_F(NominalTest, TestDepth) { +TEST_F(TypeTest, TestDepth) { HeapType A, B, C; { TypeBuilder builder(3); @@ -1086,7 +1032,7 @@ TEST_F(NominalTest, TestDepth) { } // Test .iterSubTypes() helper. -TEST_F(NominalTest, TestIterSubTypes) { +TEST_F(TypeTest, TestIterSubTypes) { /* A / \ diff --git a/test/gtest/type-test.h b/test/gtest/type-test.h index 49c6381fa..f029b8027 100644 --- a/test/gtest/type-test.h +++ b/test/gtest/type-test.h @@ -5,19 +5,10 @@ #define wasm_test_gtest_type_test_h // Helper test fixture for managing the global type system state. -template<wasm::TypeSystem system> -class TypeSystemTest : public ::testing::Test { - wasm::TypeSystem originalSystem; +class TypeTest : public ::testing::Test { protected: - void SetUp() override { - originalSystem = wasm::getTypeSystem(); - wasm::setTypeSystem(system); - } - void TearDown() override { - wasm::destroyAllTypesForTestingPurposesOnly(); - wasm::setTypeSystem(originalSystem); - } + void TearDown() override { wasm::destroyAllTypesForTestingPurposesOnly(); } // Utilities wasm::Struct makeStruct(wasm::TypeBuilder& builder, @@ -32,8 +23,4 @@ protected: } }; -using TypeTest = TypeSystemTest<wasm::TypeSystem::Isorecursive>; -using NominalTest = TypeSystemTest<wasm::TypeSystem::Nominal>; -using IsorecursiveTest = TypeSystemTest<wasm::TypeSystem::Isorecursive>; - #endif // wasm_test_gtest_type_test_h |