summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/TypeRefining.cpp63
-rw-r--r--src/wasm-builder.h4
2 files changed, 66 insertions, 1 deletions
diff --git a/src/passes/TypeRefining.cpp b/src/passes/TypeRefining.cpp
index 864cb81b3..9a0b289e5 100644
--- a/src/passes/TypeRefining.cpp
+++ b/src/passes/TypeRefining.cpp
@@ -353,6 +353,69 @@ struct TypeRefining : public Pass {
TypeRewriter(wasm, *this).update();
ReFinalize().run(getPassRunner(), &wasm);
+
+ // After refinalizing, we may still have situations that do not validate.
+ // In some cases we can infer something more precise than can be represented
+ // in wasm, like here:
+ //
+ // (try (result A)
+ // (struct.get ..) ;; returns B.
+ // (catch
+ // (const A)
+ // )
+ //
+ // The try body cannot throw, so the catch is never reached, and we can
+ // infer the fallthrough has the subtype B. But in wasm the type of the try
+ // must remain the supertype A. If that try is written into a StructSet that
+ // we refined, that will error.
+ //
+ // To fix this, we add a cast here, and expect that other passes will remove
+ // the cast after other optimizations simplify things (in this example, the
+ // catch can be removed).
+ struct WriteUpdater : public WalkerPass<PostWalker<WriteUpdater>> {
+ bool isFunctionParallel() override { return true; }
+
+ // Only affects struct.new/sets.
+ bool requiresNonNullableLocalFixups() override { return false; }
+
+ std::unique_ptr<Pass> create() override {
+ return std::make_unique<WriteUpdater>();
+ }
+
+ void visitStructNew(StructNew* curr) {
+ if (curr->type == Type::unreachable || curr->isWithDefault()) {
+ return;
+ }
+
+ auto& fields = curr->type.getHeapType().getStruct().fields;
+
+ for (Index i = 0; i < fields.size(); i++) {
+ auto*& operand = curr->operands[i];
+ auto fieldType = fields[i].type;
+ if (!Type::isSubType(operand->type, fieldType)) {
+ operand = Builder(*getModule()).makeRefCast(operand, fieldType);
+ }
+ }
+ }
+
+ void visitStructSet(StructSet* curr) {
+ if (curr->type == Type::unreachable) {
+ return;
+ }
+
+ auto fieldType =
+ curr->ref->type.getHeapType().getStruct().fields[curr->index].type;
+
+ if (!Type::isSubType(curr->value->type, fieldType)) {
+ curr->value =
+ Builder(*getModule()).makeRefCast(curr->value, fieldType);
+ }
+ }
+ };
+
+ WriteUpdater updater;
+ updater.run(getPassRunner(), &wasm);
+ updater.runOnModuleCode(getPassRunner(), &wasm);
}
};
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index e870d6b63..a3bf22fdc 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -876,7 +876,9 @@ public:
ret->finalize();
return ret;
}
- RefCast* makeRefCast(Expression* ref, Type type, RefCast::Safety safety) {
+ RefCast* makeRefCast(Expression* ref,
+ Type type,
+ RefCast::Safety safety = RefCast::Safe) {
auto* ret = wasm.allocator.alloc<RefCast>();
ret->ref = ref;
ret->type = type;