@@ -103,35 +103,47 @@ VarRef asVarRef(Type type) {
103103// `true` iff the least upper bound exists.
104104
105105bool join (VarNullability& value, const VarNullability& other) {
106- assert (!std::get_if<Index>(&value) && !std::get_if<Index>(&other) &&
107- " joining variables not yet supported" );
108- if (value == other) {
106+ if (value == other || other == VarNullability{NonNullable}) {
109107 return true ;
110108 }
111- value = Nullable;
112- return true ;
109+ if (value == VarNullability{NonNullable}) {
110+ value = other;
111+ return true ;
112+ }
113+ if (value == VarNullability{Nullable} || other == VarNullability{Nullable}) {
114+ value = Nullable;
115+ return true ;
116+ }
117+ WASM_UNREACHABLE (" joining distinct nullability variables not supported" );
113118}
114119
115120bool join (VarExactness& value, const VarExactness& other) {
116- assert (!std::get_if<Index>(&value) && !std::get_if<Index>(&other) &&
117- " joining variables not yet supported" );
118- if (value == other) {
121+ if (value == other || other == VarExactness{Exact}) {
119122 return true ;
120123 }
121- value = Inexact;
122- return true ;
124+ if (value == VarExactness{Exact}) {
125+ value = other;
126+ return true ;
127+ }
128+ if (value == VarExactness{Inexact} || other == VarExactness{Inexact}) {
129+ value = Inexact;
130+ return true ;
131+ }
132+ WASM_UNREACHABLE (" joining distinct exactness variables not supported" );
123133}
124134
125135bool join (VarSharedness& value, const VarSharedness& other) {
126- assert (!std::get_if<Index>(&value) && !std::get_if<Index>(&other) &&
127- " joining variables not yet supported" );
128136 if (value == other || std::get_if<BottomShare>(&other)) {
129137 return true ;
130138 }
131139 if (std::get_if<BottomShare>(&value)) {
132140 value = other;
133141 return true ;
134142 }
143+ if (std::get_if<Index>(&value) || std::get_if<Index>(&other)) {
144+ WASM_UNREACHABLE (
145+ " joining non-bottom sharedness with variable not supported" );
146+ }
135147 return false ;
136148}
137149
@@ -147,32 +159,60 @@ bool join(VarAbsHeapType& value, const VarAbsHeapType& other) {
147159 return join (value.share , other.share ) && join (value.ht , other.ht );
148160}
149161
150- bool join (VarDefHeapType& value, const VarDefHeapType& other) {
151- if (value.ht != other.ht ) {
152- VarExactness inexact = Inexact;
153- return join (value.ht , other.ht ) && join (value.exact , inexact) &&
154- join (inexact, other.exact );
162+ bool joinAbsAndDef (VarHeapType& value,
163+ const VarAbsHeapType& abs,
164+ const VarDefHeapType& def) {
165+ VarSharedness share = abs.share ;
166+ if (!join (share, VarSharedness{def.ht .getShared ()})) {
167+ return false ;
155168 }
156- return join (value.exact , other.exact );
157- }
158-
159- bool join (VarAbsHeapType& value, const VarDefHeapType& other) {
160- WASM_UNREACHABLE (" TODO" );
169+ // Since `def.ht.getShared()` is a concrete Shareability, the joined `share`
170+ // is guaranteed to equal `def.ht.getShared()`.
171+ auto lub =
172+ HeapType::getLeastUpperBound (abs.ht .getBasic (def.ht .getShared ()), def.ht );
173+ if (!lub) {
174+ return false ;
175+ }
176+ if (lub->isBasic ()) {
177+ value = VarAbsHeapType{share, lub->getBasic (Unshared)};
178+ } else {
179+ assert (*lub == def.ht );
180+ value = def;
181+ }
182+ return true ;
161183}
162184
163- bool join (VarDefHeapType& value, const VarAbsHeapType& other) {
164- WASM_UNREACHABLE (" TODO" );
185+ bool joinDefAndDef (VarHeapType& value,
186+ const VarDefHeapType& a,
187+ const VarDefHeapType& b) {
188+ auto lub = HeapType::getLeastUpperBound (a.ht , b.ht );
189+ if (!lub) {
190+ return false ;
191+ }
192+ if (lub->isBasic ()) {
193+ value = VarAbsHeapType{lub->getShared (), lub->getBasic (Unshared)};
194+ return true ;
195+ }
196+ VarExactness exact = a.ht == b.ht ? a.exact : VarExactness{Inexact};
197+ if (!join (exact, b.exact )) {
198+ return false ;
199+ }
200+ value = VarDefHeapType{exact, *lub};
201+ return true ;
165202}
166203
167204bool join (VarHeapType& value, const VarHeapType& other) {
168- assert (!std::get_if<Index>(&value) && !std::get_if<Index>(&other));
169205 if (value == other || std::get_if<BottomHeapType>(&other)) {
170206 return true ;
171207 }
172208 if (std::get_if<BottomHeapType>(&value)) {
173209 value = other;
174210 return true ;
175211 }
212+ if (std::get_if<Index>(&value) || std::get_if<Index>(&other)) {
213+ WASM_UNREACHABLE (
214+ " joining non-bottom heap type with variable not supported" );
215+ }
176216 auto * abs = std::get_if<VarAbsHeapType>(&value);
177217 auto * otherAbs = std::get_if<VarAbsHeapType>(&other);
178218 auto * def = std::get_if<VarDefHeapType>(&value);
@@ -181,13 +221,13 @@ bool join(VarHeapType& value, const VarHeapType& other) {
181221 return join (*abs, *otherAbs);
182222 }
183223 if (abs && otherDef) {
184- return join ( *abs, *otherDef);
224+ return joinAbsAndDef (value, *abs, *otherDef);
185225 }
186226 if (def && otherAbs) {
187- return join (*def , *otherAbs);
227+ return joinAbsAndDef (value , *otherAbs, *def );
188228 }
189229 if (def && otherDef) {
190- return join ( *def, *otherDef);
230+ return joinDefAndDef (value, *def, *otherDef);
191231 }
192232 WASM_UNREACHABLE (" unexpected variant" );
193233}
@@ -201,11 +241,20 @@ bool join(Type& value, const Type& other) {
201241 return value != Type::none;
202242}
203243
204- bool join (VarRef& value, const Type& other) { WASM_UNREACHABLE (" TODO" ); }
244+ bool join (VarRef& value, const Type& other) {
245+ if (!other.isRef ()) {
246+ return false ;
247+ }
248+ return join (value, asVarRef (other));
249+ }
205250
206251bool join (VarType& value, const VarType& other) {
207252 assert (!std::get_if<Index>(&value) && !std::get_if<Index>(&other));
208- if (value == other) {
253+ if (value == other || other == VarType{Type (Type::unreachable)}) {
254+ return true ;
255+ }
256+ if (value == VarType{Type (Type::unreachable)}) {
257+ value = other;
209258 return true ;
210259 }
211260 auto * ref = std::get_if<VarRef>(&value);
@@ -227,11 +276,8 @@ bool join(VarType& value, const VarType& other) {
227276 return true ;
228277 }
229278 if (type && otherRef) {
230- if (!type->isRef ()) {
231- return false ;
232- }
233- auto r = asVarRef (*type);
234- if (!join (r, *otherRef)) {
279+ VarRef r = *otherRef;
280+ if (!join (r, *type)) {
235281 return false ;
236282 }
237283 value = canonicalizeRef (r);
@@ -449,10 +495,11 @@ template<typename T> struct AssignmentClasses {
449495 // Setting a variable equal to itself is a no-op.
450496 return true ;
451497 }
452- // Make sure we have a class for i.
453- classes.reserve (i + 1 );
454- values.reserve (i + 1 );
455- while (values.size () <= i) {
498+ // Make sure we have classes for i and *other.
499+ size_t maxIndex = other ? std::max (i, *other) : i;
500+ classes.reserve (maxIndex + 1 );
501+ values.reserve (maxIndex + 1 );
502+ while (values.size () <= maxIndex) {
456503 [[maybe_unused]] size_t set = classes.addSet ();
457504 assert (set == values.size ());
458505 values.push_back (std::nullopt );
@@ -1031,19 +1078,20 @@ bool PrincipalType::compose(const PrincipalType& next) {
10311078
10321079 apply (assignments, *this );
10331080 // If a type variable was instantiated with bottom type (i.e. unreachable) due
1034- // to popping from an unreachable stack, the result type may end with some
1035- // number of unreachables. This is nonsensical, since `unreachable` is not a
1036- // concrete type. We could have alternatively left the variables
1037- // uninstantiated, but it would have no corresponding introduction on the left
1038- // hand side of the type. Even better is to canonicalize to empty results.
1039- // Since the new principal type is unreachable, this has the same semantics as
1040- // having an unconstrained result variable.
1041- if (!results.empty ()) {
1042- auto & last = results[results.size () - 1 ];
1043- if (auto * t = std::get_if<Type>(&last); t && *t == Type::unreachable) {
1044- results.clear ();
1045- }
1046- }
1081+ // to popping from an unreachable stack, `results` may start with some number
1082+ // of `unreachable`s at the bottom of the stack. This is nonsensical, since
1083+ // `unreachable` is not a concrete type. We could have alternatively left the
1084+ // variables uninstantiated, but they would have no corresponding introduction
1085+ // on the left hand side of the type. Even better is to remove the leading
1086+ // `unreachable`s at the bottom of `results`. Since the new principal type is
1087+ // unreachable (`*`), unconstrained elements at the bottom of the stack are
1088+ // absorbed into `*`, while any concrete types above them remain at the top of
1089+ // the stack.
1090+ auto firstNonBottom =
1091+ std::find_if (results.begin (), results.end (), [](const VarType& t) {
1092+ return t != VarType{Type (Type::unreachable)};
1093+ });
1094+ results.erase (results.begin (), firstNonBottom);
10471095
10481096 canonicalizeVars (*this );
10491097 return true ;
0 commit comments