summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/subtype-exprs.h20
-rw-r--r--src/passes/StringLowering.cpp4
-rw-r--r--src/passes/Unsubtyping.cpp16
3 files changed, 38 insertions, 2 deletions
diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h
index 6b8348319..1c922521f 100644
--- a/src/ir/subtype-exprs.h
+++ b/src/ir/subtype-exprs.h
@@ -59,6 +59,21 @@ namespace wasm {
// * noteCast(Expression, Expression) - An expression's type is cast to
// another, for example, in RefCast.
//
+// In addition, we need to differentiate two situations that cause subtyping:
+// * Flow-based subtyping: E.g. when a value flows out from a block, in which
+// case the value must be a subtype of the block's type.
+// * Non-flow-based subtyping: E.g. in RefEq, being in one of the arms means
+// you must be a subtype of eqref, but your value does not flow anywhere,
+// because it is processed by the RefEq and does not send it anywhere.
+// The difference between the two matters in some users of this class, and so
+// the above functions all handle flow-based subtyping, while there is also the
+// following:
+//
+// * noteNonFlowSubtype(Expression, Type)
+//
+// This is the only signature we need for the non-flowing case since it always
+// stems from an expression that is compared against a type.
+//
// The concrete signatures are:
//
// void noteSubtype(Type, Type);
@@ -66,6 +81,7 @@ namespace wasm {
// void noteSubtype(Type, Expression*);
// void noteSubtype(Expression*, Type);
// void noteSubtype(Expression*, Expression*);
+// void noteNonFlowSubtype(Expression*, Type);
// void noteCast(HeapType, HeapType);
// void noteCast(Expression*, Type);
// void noteCast(Expression*, Expression*);
@@ -202,8 +218,8 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
void visitRefIsNull(RefIsNull* curr) {}
void visitRefFunc(RefFunc* curr) {}
void visitRefEq(RefEq* curr) {
- self()->noteSubtype(curr->left, Type(HeapType::eq, Nullable));
- self()->noteSubtype(curr->right, Type(HeapType::eq, Nullable));
+ self()->noteNonFlowSubtype(curr->left, Type(HeapType::eq, Nullable));
+ self()->noteNonFlowSubtype(curr->right, Type(HeapType::eq, Nullable));
}
void visitTableGet(TableGet* curr) {}
void visitTableSet(TableSet* curr) {
diff --git a/src/passes/StringLowering.cpp b/src/passes/StringLowering.cpp
index 1e16295cd..e0d3fbad0 100644
--- a/src/passes/StringLowering.cpp
+++ b/src/passes/StringLowering.cpp
@@ -478,6 +478,10 @@ struct StringLowering : public StringGathering {
// Only the type matters of the place we assign to.
noteSubtype(a, b->type);
}
+ void noteNonFlowSubtype(Expression* a, Type b) {
+ // Flow or non-flow is the same for us.
+ noteSubtype(a, b);
+ }
void noteCast(HeapType, HeapType) {
// Casts do not concern us.
}
diff --git a/src/passes/Unsubtyping.cpp b/src/passes/Unsubtyping.cpp
index 67a3c4e85..897511c35 100644
--- a/src/passes/Unsubtyping.cpp
+++ b/src/passes/Unsubtyping.cpp
@@ -193,6 +193,22 @@ struct Unsubtyping
noteSubtype(sub->type, super->type);
}
+ void noteNonFlowSubtype(Expression* sub, Type super) {
+ // This expression's type must be a subtype of |super|, but the value does
+ // not flow anywhere - this is a static constraint. As the value does not
+ // flow, it cannot reach anywhere else, which means we need this in order to
+ // validate but it does not interact with casts. Given that, if super is a
+ // basic type then we can simply ignore this: we only remove subtyping
+ // between user types, so subtyping wrt basic types is unchanged, and so
+ // this constraint will never be a problem.
+ if (super.isRef() && super.getHeapType().isBasic()) {
+ return;
+ }
+
+ // Otherwise, we must take this into account.
+ noteSubtype(sub, super);
+ }
+
void noteCast(HeapType src, HeapType dest) {
if (src == dest || dest.isBottom()) {
return;