Skip to content

Commit 4844d90

Browse files
authored
[Custom Descriptors] Ensure descriptor validity in Unsubtyping (#7743)
We already partially handled the interaction between descriptors and subtyping by ensuring that B <: A implies B.desc <: A.desc if B.desc and A.desc both exist. However, there was a subtler situation we did not handle: A -> A.desc ^ ^ | B.desc | ^ C -> C.desc This subtyping violates the constraint that the descriptor of C's immediate supertype must be the immediate supertype of C's descriptor. To fix this, find the described type B to insert between C and A before establishing C.desc <: B.desc <: A.desc.
1 parent bd1e685 commit 4844d90

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

src/passes/Unsubtyping.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ struct Unsubtyping : Pass {
492492
}
493493
if (HeapType::isSubType(*oldSuper, super)) {
494494
// sub <: oldSuper <: super
495+
processDescribed(sub, *oldSuper, super);
495496
noteSubtype(*oldSuper, super);
496497
// We already handled sub <: oldSuper, so we're done.
497498
return;
@@ -501,6 +502,7 @@ struct Unsubtyping : Pass {
501502
// super will already be in the same tree when we process them below, so
502503
// when we process casts we will know that we only need to process up to
503504
// oldSuper.
505+
processDescribed(sub, super, *oldSuper);
504506
process(super, *oldSuper);
505507
}
506508

@@ -512,6 +514,42 @@ struct Unsubtyping : Pass {
512514
processCasts(sub, super, oldSuper);
513515
}
514516

517+
void processDescribed(HeapType sub, HeapType mid, HeapType super) {
518+
// We are establishing sub <: mid <: super. If super describes the immediate
519+
// supertype of the type sub describes, then once we insert mid between them
520+
// we would have this:
521+
//
522+
// A -> super
523+
// ^ ^
524+
// | mid
525+
// | ^
526+
// C -> sub
527+
//
528+
// This violates the requirement that the descriptor of C's immediate
529+
// supertype must be the immediate supertype of C's descriptor. To fix it,
530+
// we have to find the type B that mid describes and insert it between A and
531+
// C:
532+
//
533+
// A -> super
534+
// ^ ^
535+
// B -> mid
536+
// ^ ^
537+
// C -> sub
538+
//
539+
// We do this eagerly before we establish sub <: mid <: super so that if
540+
// establishing that subtyping requires recursively establishing other
541+
// subtypings, we can depend on the invariant that the described types are
542+
// always set up correctly beforehand.
543+
auto subDescribed = sub.getDescribedType();
544+
auto superDescribed = super.getDescribedType();
545+
if (subDescribed && superDescribed &&
546+
types.getSupertype(*subDescribed) == superDescribed) {
547+
auto midDescribed = mid.getDescribedType();
548+
assert(midDescribed);
549+
process(*subDescribed, *midDescribed);
550+
}
551+
}
552+
515553
void processDefinitions(HeapType sub, HeapType super) {
516554
if (super.isBasic()) {
517555
return;

test/lit/passes/unsubtyping-desc.wast

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,75 @@
6262
;; CHECK: (global $A.desc (ref null $A.desc) (global.get $B.desc))
6363
(global $A.desc (ref null $A.desc) (global.get $B.desc))
6464
)
65+
66+
(module
67+
(rec
68+
;; CHECK: (rec
69+
;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct))))
70+
(type $top (sub (descriptor $top.desc (struct))))
71+
;; CHECK: (type $mid (sub $top (descriptor $mid.desc (struct))))
72+
(type $mid (sub $top (descriptor $mid.desc (struct))))
73+
;; CHECK: (type $bot (sub $mid (descriptor $bot.desc (struct))))
74+
(type $bot (sub $mid (descriptor $bot.desc (struct))))
75+
;; CHECK: (type $top.desc (sub (describes $top (struct))))
76+
(type $top.desc (sub (describes $top (struct))))
77+
;; CHECK: (type $mid.desc (sub $top.desc (describes $mid (struct))))
78+
(type $mid.desc (sub $top.desc (describes $mid (struct))))
79+
;; CHECK: (type $bot.desc (sub $mid.desc (describes $bot (struct))))
80+
(type $bot.desc (sub $mid.desc (describes $bot (struct))))
81+
)
82+
83+
;; top -> top.desc
84+
;; ^
85+
;; |(2) mid -> mid.desc
86+
;; | ^ (1)
87+
;; bot -> bot.desc
88+
;;
89+
;; bot <: top implies bot.desc <: top.desc, but we already have
90+
;; bot.desc <: mid.desc, so that gives us bot.desc <: mid.desc <: top.desc.
91+
;; This is only valid if we also have bot <: mid <: top.
92+
93+
;; CHECK: (global $bot-mid-desc (ref null $mid.desc) (struct.new_default $bot.desc))
94+
(global $bot-mid-desc (ref null $mid.desc) (struct.new $bot.desc))
95+
;; CHECK: (global $bot-top (ref null $top) (struct.new_default $bot
96+
;; CHECK-NEXT: (ref.null none)
97+
;; CHECK-NEXT: ))
98+
(global $bot-top (ref null $top) (struct.new $bot (ref.null none)))
99+
)
100+
101+
(module
102+
(rec
103+
;; CHECK: (rec
104+
;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct))))
105+
(type $top (sub (descriptor $top.desc (struct))))
106+
;; CHECK: (type $mid (sub $top (descriptor $mid.desc (struct))))
107+
(type $mid (sub $top (descriptor $mid.desc (struct))))
108+
;; CHECK: (type $bot (sub $mid (descriptor $bot.desc (struct))))
109+
(type $bot (sub $mid (descriptor $bot.desc (struct))))
110+
;; CHECK: (type $top.desc (sub (describes $top (struct))))
111+
(type $top.desc (sub (describes $top (struct))))
112+
;; CHECK: (type $mid.desc (sub $top.desc (describes $mid (struct))))
113+
(type $mid.desc (sub $top.desc (describes $mid (struct))))
114+
;; CHECK: (type $bot.desc (sub $mid.desc (describes $bot (struct))))
115+
(type $bot.desc (sub $mid.desc (describes $bot (struct))))
116+
)
117+
118+
;; Same as above, but the order of the initial subtypings is reversed.
119+
;;
120+
;; top -> top.desc
121+
;; ^
122+
;; |(1) mid -> mid.desc
123+
;; | ^ (2)
124+
;; bot -> bot.desc
125+
;;
126+
;; bot <: top implies bot.desc <: top.desc. When we add bot.desc <: mid.desc,
127+
;; that gives us bot.desc <: mid.desc <: top.desc. This is only valid if we
128+
;; also have bot <: mid <: top.
129+
130+
;; CHECK: (global $bot-top (ref null $top) (struct.new_default $bot
131+
;; CHECK-NEXT: (ref.null none)
132+
;; CHECK-NEXT: ))
133+
(global $bot-top (ref null $top) (struct.new $bot (ref.null none)))
134+
;; CHECK: (global $bot-mid-desc (ref null $mid.desc) (struct.new_default $bot.desc))
135+
(global $bot-mid-desc (ref null $mid.desc) (struct.new $bot.desc))
136+
)

0 commit comments

Comments
 (0)