diff --git a/src/passes/Unsubtyping.cpp b/src/passes/Unsubtyping.cpp index 6b8d56f33c4..248ed995fe4 100644 --- a/src/passes/Unsubtyping.cpp +++ b/src/passes/Unsubtyping.cpp @@ -525,7 +525,6 @@ struct Unsubtyping : Pass { } if (HeapType::isSubType(*oldSuper, super)) { // sub <: oldSuper <: super - processDescribed(sub, *oldSuper, super); noteSubtype(*oldSuper, super); // We already handled sub <: oldSuper, so we're done. return; @@ -535,7 +534,6 @@ struct Unsubtyping : Pass { // super will already be in the same tree when we process them below, so // when we process casts we will know that we only need to process up to // oldSuper. - processDescribed(sub, super, *oldSuper); process(super, *oldSuper); } @@ -547,42 +545,6 @@ struct Unsubtyping : Pass { processCasts(sub, super, oldSuper); } - void processDescribed(HeapType sub, HeapType mid, HeapType super) { - // We are establishing sub <: mid <: super. If super describes the immediate - // supertype of the type sub describes, then once we insert mid between them - // we would have this: - // - // A -> super - // ^ ^ - // | mid - // | ^ - // C -> sub - // - // This violates the requirement that the descriptor of C's immediate - // supertype must be the immediate supertype of C's descriptor. To fix it, - // we have to find the type B that mid describes and insert it between A and - // C: - // - // A -> super - // ^ ^ - // B -> mid - // ^ ^ - // C -> sub - // - // We do this eagerly before we establish sub <: mid <: super so that if - // establishing that subtyping requires recursively establishing other - // subtypings, we can depend on the invariant that the described types are - // always set up correctly beforehand. - auto subDescribed = sub.getDescribedType(); - auto superDescribed = super.getDescribedType(); - if (subDescribed && superDescribed && - types.getSupertype(*subDescribed) == superDescribed) { - auto midDescribed = mid.getDescribedType(); - assert(midDescribed); - process(*subDescribed, *midDescribed); - } - } - void processDefinitions(HeapType sub, HeapType super) { if (super.isBasic()) { return; @@ -618,6 +580,11 @@ struct Unsubtyping : Pass { noteSubtype(*desc, *superDesc); } } + if (auto desc = sub.getDescribedType()) { + if (auto superDesc = super.getDescribedType()) { + noteSubtype(*desc, *superDesc); + } + } } void diff --git a/test/lit/passes/unsubtyping-desc.wast b/test/lit/passes/unsubtyping-desc.wast index 69c8d3f4d56..adfdd1a0c85 100644 --- a/test/lit/passes/unsubtyping-desc.wast +++ b/test/lit/passes/unsubtyping-desc.wast @@ -49,14 +49,13 @@ (type $A (sub (descriptor $A.desc (struct)))) ;; CHECK: (type $A.desc (sub (describes $A (struct)))) (type $A.desc (sub (describes $A (struct)))) - ;; CHECK: (type $B (sub (descriptor $B.desc (struct)))) + ;; CHECK: (type $B (sub $A (descriptor $B.desc (struct)))) (type $B (sub $A (descriptor $B.desc (struct)))) ;; CHECK: (type $B.desc (sub $A.desc (describes $B (struct)))) (type $B.desc (sub $A.desc (describes $B (struct)))) ) - ;; Now we directly require B.desc <: A.desc. This does *not* imply B <: A, so - ;; we can optimize $B (but not $B.desc). + ;; Now we require B.desc <: A.desc, which similarly implies B <: A. ;; CHECK: (global $B.desc (ref null $B.desc) (ref.null none)) (global $B.desc (ref null $B.desc) (ref.null none)) ;; CHECK: (global $A.desc (ref null $A.desc) (global.get $B.desc)) @@ -134,3 +133,76 @@ ;; CHECK: (global $bot-mid-desc (ref null $mid.desc) (struct.new_default $bot.desc)) (global $bot-mid-desc (ref null $mid.desc) (struct.new $bot.desc)) ) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct)))) + (type $top (sub (descriptor $top.desc (struct)))) + ;; CHECK: (type $mid (sub $top (descriptor $mid.desc (struct)))) + (type $mid (sub $top (descriptor $mid.desc (struct)))) + ;; CHECK: (type $bot (sub $mid (descriptor $bot.desc (struct)))) + (type $bot (sub $mid (descriptor $bot.desc (struct)))) + ;; CHECK: (type $top.desc (sub (describes $top (struct)))) + (type $top.desc (sub (describes $top (struct)))) + ;; CHECK: (type $mid.desc (sub $top.desc (describes $mid (struct)))) + (type $mid.desc (sub $top.desc (describes $mid (struct)))) + ;; CHECK: (type $bot.desc (sub $mid.desc (describes $bot (struct)))) + (type $bot.desc (sub $mid.desc (describes $bot (struct)))) + ) + + ;; Now go the other direction: + ;; + ;; top ---> top.desc + ;; ^ + ;; mid -> mid.desc |(2) + ;; ^ (1) | + ;; bot ---> bot.desc + ;; + ;; bot.desc <: top.desc implies bot <: top, but we already have bot <: mid, so + ;; that gives us bot <: mid <: top. This is only valid if we also have + ;; bot.desc <: mid.desc <: top.desc. + + ;; CHECK: (global $bot-mid (ref null $mid) (struct.new_default $bot + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: )) + (global $bot-mid (ref null $mid) (struct.new $bot (ref.null none))) + ;; CHECK: (global $bot-top-desc (ref null $top.desc) (struct.new_default $bot.desc)) + (global $bot-top-desc (ref null $top.desc) (struct.new $bot.desc)) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct)))) + (type $top (sub (descriptor $top.desc (struct)))) + ;; CHECK: (type $mid (sub $top (descriptor $mid.desc (struct)))) + (type $mid (sub $top (descriptor $mid.desc (struct)))) + ;; CHECK: (type $bot (sub $mid (descriptor $bot.desc (struct)))) + (type $bot (sub $mid (descriptor $bot.desc (struct)))) + ;; CHECK: (type $top.desc (sub (describes $top (struct)))) + (type $top.desc (sub (describes $top (struct)))) + ;; CHECK: (type $mid.desc (sub $top.desc (describes $mid (struct)))) + (type $mid.desc (sub $top.desc (describes $mid (struct)))) + ;; CHECK: (type $bot.desc (sub $mid.desc (describes $bot (struct)))) + (type $bot.desc (sub $mid.desc (describes $bot (struct)))) + ) + + ;; Same as above, but the order of the initial subtypings is reversed. + ;; + ;; top ---> top.desc + ;; ^ + ;; mid -> mid.desc |(1) + ;; ^ (2) | + ;; bot ---> bot.desc + ;; + ;; bot.desc <: top.desc implies bot <: top. When we add bot <: mid, that gives + ;; us bot <: mid <: top. This is only valid if we also have + ;; bot.desc <: mid.desc <: top.desc. + ;; CHECK: (global $bot-top-desc (ref null $top.desc) (struct.new_default $bot.desc)) + (global $bot-top-desc (ref null $top.desc) (struct.new $bot.desc)) + ;; CHECK: (global $bot-mid (ref null $mid) (struct.new_default $bot + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: )) + (global $bot-mid (ref null $mid) (struct.new $bot (ref.null none))) +)