@@ -160,13 +160,23 @@ pub struct OperatorValidatorAllocations {
160160
161161/// Type storage within the validator.
162162///
163- /// This is used to manage the operand stack and notably isn't just `ValType` to
164- /// handle unreachable code and the "bottom" type.
163+ /// This is used to manage the operand stack and notably isn't just `ValType`
164+ /// to handle unreachable code and the "bottom" type.
165165#[ derive( Debug , Copy , Clone ) ]
166- enum MaybeType {
166+ enum MaybeType < T = ValType > {
167+ /// A "bottom" type which represents unconstrained unreachable code. There
168+ /// are no constraints on what this type may be.
167169 Bot ,
168- HeapBot ,
169- Type ( ValType ) ,
170+ /// A "bottom" type for specifically heap types.
171+ ///
172+ /// This type is known to be a reference type, optionally the specific
173+ /// abstract heap type listed. This type can be interpeted as either
174+ /// `shared` or not-`shared`. Additionally it can be either nullable or
175+ /// not. Currently no further refinements are required for wasm
176+ /// instructions today, but this may grow in the future.
177+ HeapBot ( Option < AbstractHeapType > ) ,
178+ /// A known type with the type `T`.
179+ Type ( T ) ,
170180}
171181
172182// The validator is pretty performance-sensitive and `MaybeType` is the main
@@ -180,7 +190,14 @@ impl core::fmt::Display for MaybeType {
180190 fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
181191 match self {
182192 MaybeType :: Bot => write ! ( f, "bot" ) ,
183- MaybeType :: HeapBot => write ! ( f, "heap-bot" ) ,
193+ MaybeType :: HeapBot ( ty) => {
194+ write ! ( f, "(ref shared? " ) ?;
195+ match ty {
196+ Some ( ty) => write ! ( f, "{}bot" , ty. as_str( true ) ) ?,
197+ None => write ! ( f, "bot" ) ?,
198+ }
199+ write ! ( f, ")" )
200+ }
184201 MaybeType :: Type ( ty) => core:: fmt:: Display :: fmt ( ty, f) ,
185202 }
186203 }
@@ -198,6 +215,33 @@ impl From<RefType> for MaybeType {
198215 ty. into ( )
199216 }
200217}
218+ impl From < MaybeType < RefType > > for MaybeType < ValType > {
219+ fn from ( ty : MaybeType < RefType > ) -> MaybeType < ValType > {
220+ match ty {
221+ MaybeType :: Bot => MaybeType :: Bot ,
222+ MaybeType :: HeapBot ( ty) => MaybeType :: HeapBot ( ty) ,
223+ MaybeType :: Type ( t) => MaybeType :: Type ( t. into ( ) ) ,
224+ }
225+ }
226+ }
227+
228+ impl MaybeType < RefType > {
229+ fn as_non_null ( & self ) -> MaybeType < RefType > {
230+ match self {
231+ MaybeType :: Bot => MaybeType :: Bot ,
232+ MaybeType :: HeapBot ( ty) => MaybeType :: HeapBot ( * ty) ,
233+ MaybeType :: Type ( ty) => MaybeType :: Type ( ty. as_non_null ( ) ) ,
234+ }
235+ }
236+
237+ fn is_maybe_shared ( & self , resources : & impl WasmModuleResources ) -> Option < bool > {
238+ match self {
239+ MaybeType :: Bot => None ,
240+ MaybeType :: HeapBot ( _) => None ,
241+ MaybeType :: Type ( ty) => Some ( resources. is_shared ( * ty) ) ,
242+ }
243+ }
244+ }
201245
202246impl OperatorValidator {
203247 fn new ( features : & WasmFeatures , allocs : OperatorValidatorAllocations ) -> Self {
@@ -330,7 +374,7 @@ impl OperatorValidator {
330374 pub fn peek_operand_at ( & self , depth : usize ) -> Option < Option < ValType > > {
331375 Some ( match self . operands . iter ( ) . rev ( ) . nth ( depth) ? {
332376 MaybeType :: Type ( t) => Some ( * t) ,
333- MaybeType :: Bot | MaybeType :: HeapBot => None ,
377+ MaybeType :: Bot | MaybeType :: HeapBot ( .. ) => None ,
334378 } )
335379 }
336380
@@ -570,10 +614,33 @@ where
570614 if let Some ( expected) = expected {
571615 match ( actual, expected) {
572616 // The bottom type matches all expectations
573- ( MaybeType :: Bot , _)
617+ ( MaybeType :: Bot , _) => { }
618+
574619 // The "heap bottom" type only matches other references types,
575- // but not any integer types.
576- | ( MaybeType :: HeapBot , ValType :: Ref ( _) ) => { }
620+ // but not any integer types. Note that if the heap bottom is
621+ // known to have a specific abstract heap type then a subtype
622+ // check is performed against hte expected type.
623+ ( MaybeType :: HeapBot ( actual_ty) , ValType :: Ref ( expected) ) => {
624+ if let Some ( actual) = actual_ty {
625+ let expected_shared = self . resources . is_shared ( expected) ;
626+ let actual = RefType :: new (
627+ false ,
628+ HeapType :: Abstract {
629+ shared : expected_shared,
630+ ty : actual,
631+ } ,
632+ )
633+ . unwrap ( ) ;
634+ if !self . resources . is_subtype ( actual. into ( ) , expected. into ( ) ) {
635+ bail ! (
636+ self . offset,
637+ "type mismatch: expected {}, found {}" ,
638+ ty_to_str( expected. into( ) ) ,
639+ ty_to_str( actual. into( ) )
640+ ) ;
641+ }
642+ }
643+ }
577644
578645 // Use the `is_subtype` predicate to test if a found type matches
579646 // the expectation.
@@ -590,7 +657,7 @@ where
590657
591658 // A "heap bottom" type cannot match any numeric types.
592659 (
593- MaybeType :: HeapBot ,
660+ MaybeType :: HeapBot ( .. ) ,
594661 ValType :: I32 | ValType :: I64 | ValType :: F32 | ValType :: F64 | ValType :: V128 ,
595662 ) => {
596663 bail ! (
@@ -605,10 +672,11 @@ where
605672 }
606673
607674 /// Pop a reference type from the operand stack.
608- fn pop_ref ( & mut self , expected : Option < RefType > ) -> Result < Option < RefType > > {
675+ fn pop_ref ( & mut self , expected : Option < RefType > ) -> Result < MaybeType < RefType > > {
609676 match self . pop_operand ( expected. map ( |t| t. into ( ) ) ) ? {
610- MaybeType :: Bot | MaybeType :: HeapBot => Ok ( None ) ,
611- MaybeType :: Type ( ValType :: Ref ( rt) ) => Ok ( Some ( rt) ) ,
677+ MaybeType :: Bot => Ok ( MaybeType :: HeapBot ( None ) ) ,
678+ MaybeType :: HeapBot ( ty) => Ok ( MaybeType :: HeapBot ( ty) ) ,
679+ MaybeType :: Type ( ValType :: Ref ( rt) ) => Ok ( MaybeType :: Type ( rt) ) ,
612680 MaybeType :: Type ( ty) => bail ! (
613681 self . offset,
614682 "type mismatch: expected ref but found {}" ,
@@ -622,13 +690,22 @@ where
622690 ///
623691 /// This function returns the popped reference type and its `shared`-ness,
624692 /// saving extra lookups for concrete types.
625- fn pop_maybe_shared_ref (
626- & mut self ,
627- expected : AbstractHeapType ,
628- ) -> Result < Option < ( RefType , bool ) > > {
693+ fn pop_maybe_shared_ref ( & mut self , expected : AbstractHeapType ) -> Result < MaybeType < RefType > > {
629694 let actual = match self . pop_ref ( None ) ? {
630- Some ( rt) => rt,
631- None => return Ok ( None ) ,
695+ MaybeType :: Bot => return Ok ( MaybeType :: Bot ) ,
696+ MaybeType :: HeapBot ( None ) => return Ok ( MaybeType :: HeapBot ( None ) ) ,
697+ MaybeType :: HeapBot ( Some ( actual) ) => {
698+ if !actual. is_subtype_of ( expected) {
699+ bail ! (
700+ self . offset,
701+ "type mismatch: expected subtype of {}, found {}" ,
702+ expected. as_str( false ) ,
703+ actual. as_str( false ) ,
704+ )
705+ }
706+ return Ok ( MaybeType :: HeapBot ( Some ( actual) ) ) ;
707+ }
708+ MaybeType :: Type ( ty) => ty,
632709 } ;
633710 // Change our expectation based on whether we're dealing with an actual
634711 // shared or unshared type.
@@ -651,7 +728,7 @@ where
651728 "type mismatch: expected subtype of {expected}, found {actual}" ,
652729 )
653730 }
654- Ok ( Some ( ( actual, is_actual_shared ) ) )
731+ Ok ( MaybeType :: Type ( actual) )
655732 }
656733
657734 /// Fetches the type for the local at `idx`, returning an error if it's out
@@ -1695,8 +1772,8 @@ where
16951772 let ty = match ( ty1, ty2) {
16961773 // All heap-related types aren't allowed with the `select`
16971774 // instruction
1698- ( MaybeType :: HeapBot , _)
1699- | ( _, MaybeType :: HeapBot )
1775+ ( MaybeType :: HeapBot ( .. ) , _)
1776+ | ( _, MaybeType :: HeapBot ( .. ) )
17001777 | ( MaybeType :: Type ( ValType :: Ref ( _) ) , _)
17011778 | ( _, MaybeType :: Type ( ValType :: Ref ( _) ) ) => {
17021779 bail ! (
@@ -2666,18 +2743,12 @@ where
26662743 }
26672744
26682745 fn visit_ref_as_non_null ( & mut self ) -> Self :: Output {
2669- let ty = match self . pop_ref ( None ) ? {
2670- Some ( ty) => MaybeType :: Type ( ValType :: Ref ( ty. as_non_null ( ) ) ) ,
2671- None => MaybeType :: HeapBot ,
2672- } ;
2746+ let ty = self . pop_ref ( None ) ?. as_non_null ( ) ;
26732747 self . push_operand ( ty) ?;
26742748 Ok ( ( ) )
26752749 }
26762750 fn visit_br_on_null ( & mut self , relative_depth : u32 ) -> Self :: Output {
2677- let ref_ty = match self . pop_ref ( None ) ? {
2678- None => MaybeType :: HeapBot ,
2679- Some ( ty) => MaybeType :: Type ( ValType :: Ref ( ty. as_non_null ( ) ) ) ,
2680- } ;
2751+ let ref_ty = self . pop_ref ( None ) ?. as_non_null ( ) ;
26812752 let ( ft, kind) = self . jump ( relative_depth) ?;
26822753 let label_types = self . label_types ( ft, kind) ?;
26832754 self . pop_push_label_types ( label_types) ?;
@@ -2734,19 +2805,21 @@ where
27342805 fn visit_ref_eq ( & mut self ) -> Self :: Output {
27352806 let a = self . pop_maybe_shared_ref ( AbstractHeapType :: Eq ) ?;
27362807 let b = self . pop_maybe_shared_ref ( AbstractHeapType :: Eq ) ?;
2737- match ( a, b) {
2738- ( Some ( ( _, is_a_shared) ) , Some ( ( _, is_b_shared) ) ) => {
2808+ let a_is_shared = a. is_maybe_shared ( & self . resources ) ;
2809+ let b_is_shared = b. is_maybe_shared ( & self . resources ) ;
2810+ match ( a_is_shared, b_is_shared) {
2811+ // One or both of the types are from unreachable code; assume
2812+ // the shared-ness matches.
2813+ ( None , Some ( _) ) | ( Some ( _) , None ) | ( None , None ) => { }
2814+
2815+ ( Some ( is_a_shared) , Some ( is_b_shared) ) => {
27392816 if is_a_shared != is_b_shared {
27402817 bail ! (
27412818 self . offset,
27422819 "type mismatch: expected `ref.eq` types to match `shared`-ness"
27432820 ) ;
27442821 }
27452822 }
2746- _ => {
2747- // One or both of the types are from unreachable code; assume
2748- // the shared-ness matches.
2749- }
27502823 }
27512824 self . push_operand ( ValType :: I32 )
27522825 }
@@ -4404,35 +4477,37 @@ where
44044477 Ok ( ( ) )
44054478 }
44064479 fn visit_any_convert_extern ( & mut self ) -> Self :: Output {
4407- let extern_ref = self . pop_maybe_shared_ref ( AbstractHeapType :: Extern ) ?;
4408- let ( is_nullable, shared) = if let Some ( ( extern_ref, shared) ) = extern_ref {
4409- ( extern_ref. is_nullable ( ) , shared)
4410- } else {
4411- // TODO: propagating unshared may be incorrect here
4412- // (https://github.com/WebAssembly/shared-everything-threads/issues/80)
4413- ( false , false )
4414- } ;
4415- let heap_type = HeapType :: Abstract {
4416- shared,
4417- ty : AbstractHeapType :: Any ,
4480+ let any_ref = match self . pop_maybe_shared_ref ( AbstractHeapType :: Extern ) ? {
4481+ MaybeType :: Bot | MaybeType :: HeapBot ( _) => {
4482+ MaybeType :: HeapBot ( Some ( AbstractHeapType :: Any ) )
4483+ }
4484+ MaybeType :: Type ( ty) => {
4485+ let shared = self . resources . is_shared ( ty) ;
4486+ let heap_type = HeapType :: Abstract {
4487+ shared,
4488+ ty : AbstractHeapType :: Any ,
4489+ } ;
4490+ let any_ref = RefType :: new ( ty. is_nullable ( ) , heap_type) . unwrap ( ) ;
4491+ MaybeType :: Type ( any_ref)
4492+ }
44184493 } ;
4419- let any_ref = RefType :: new ( is_nullable, heap_type) . unwrap ( ) ;
44204494 self . push_operand ( any_ref)
44214495 }
44224496 fn visit_extern_convert_any ( & mut self ) -> Self :: Output {
4423- let any_ref = self . pop_maybe_shared_ref ( AbstractHeapType :: Any ) ?;
4424- let ( is_nullable, shared) = if let Some ( ( any_ref, shared) ) = any_ref {
4425- ( any_ref. is_nullable ( ) , shared)
4426- } else {
4427- // TODO: propagating unshared may be incorrect here
4428- // (https://github.com/WebAssembly/shared-everything-threads/issues/80)
4429- ( false , false )
4430- } ;
4431- let heap_type = HeapType :: Abstract {
4432- shared,
4433- ty : AbstractHeapType :: Extern ,
4497+ let extern_ref = match self . pop_maybe_shared_ref ( AbstractHeapType :: Any ) ? {
4498+ MaybeType :: Bot | MaybeType :: HeapBot ( _) => {
4499+ MaybeType :: HeapBot ( Some ( AbstractHeapType :: Extern ) )
4500+ }
4501+ MaybeType :: Type ( ty) => {
4502+ let shared = self . resources . is_shared ( ty) ;
4503+ let heap_type = HeapType :: Abstract {
4504+ shared,
4505+ ty : AbstractHeapType :: Extern ,
4506+ } ;
4507+ let extern_ref = RefType :: new ( ty. is_nullable ( ) , heap_type) . unwrap ( ) ;
4508+ MaybeType :: Type ( extern_ref)
4509+ }
44344510 } ;
4435- let extern_ref = RefType :: new ( is_nullable, heap_type) . unwrap ( ) ;
44364511 self . push_operand ( extern_ref)
44374512 }
44384513 fn visit_ref_test_non_null ( & mut self , heap_type : HeapType ) -> Self :: Output {
0 commit comments