Skip to content

Commit 2371dca

Browse files
authored
Refactor type checks when popping refs (#1730)
This commit refactors users of `pop_ref` during validation to be able to pass in an expected type. This removes the need for a few manual `is_subtype` checks and helps centralize type-checking in one location.
1 parent c5bd644 commit 2371dca

5 files changed

Lines changed: 48 additions & 68 deletions

File tree

crates/wasmparser/src/validator/operators.rs

Lines changed: 22 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,8 @@ where
605605
}
606606

607607
/// Pop a reference type from the operand stack.
608-
fn pop_ref(&mut self) -> Result<Option<RefType>> {
609-
match self.pop_operand(None)? {
608+
fn pop_ref(&mut self, expected: Option<RefType>) -> Result<Option<RefType>> {
609+
match self.pop_operand(expected.map(|t| t.into()))? {
610610
MaybeType::Bot | MaybeType::HeapBot => Ok(None),
611611
MaybeType::Type(ValType::Ref(rt)) => Ok(Some(rt)),
612612
MaybeType::Type(ty) => bail!(
@@ -626,7 +626,7 @@ where
626626
&mut self,
627627
expected: AbstractHeapType,
628628
) -> Result<Option<(RefType, bool)>> {
629-
let actual = match self.pop_ref()? {
629+
let actual = match self.pop_ref(None)? {
630630
Some(rt) => rt,
631631
None => return Ok(None),
632632
};
@@ -881,18 +881,8 @@ where
881881
let unpacked_index = UnpackedIndex::Module(type_index);
882882
let mut hty = HeapType::Concrete(unpacked_index);
883883
self.resources.check_heap_type(&mut hty, self.offset)?;
884-
// If `None` is popped then that means a "bottom" type was popped which
885-
// is always considered equivalent to the `hty` tag.
886-
if let Some(rt) = self.pop_ref()? {
887-
let expected = RefType::new(true, hty).expect("hty should be previously validated");
888-
let expected = ValType::Ref(expected);
889-
if !self.resources.is_subtype(ValType::Ref(rt), expected) {
890-
bail!(
891-
self.offset,
892-
"type mismatch: funcref on stack does not match specified type",
893-
);
894-
}
895-
}
884+
let expected = RefType::new(true, hty).expect("hty should be previously validated");
885+
self.pop_ref(Some(expected))?;
896886
self.func_type_at(type_index)
897887
}
898888

@@ -1130,51 +1120,31 @@ where
11301120

11311121
/// Common helper for `ref.test` and `ref.cast` downcasting/checking
11321122
/// instructions. Returns the given `heap_type` as a `ValType`.
1133-
fn check_downcast(
1134-
&mut self,
1135-
nullable: bool,
1136-
mut heap_type: HeapType,
1137-
inst_name: &str,
1138-
) -> Result<ValType> {
1123+
fn check_downcast(&mut self, nullable: bool, mut heap_type: HeapType) -> Result<RefType> {
11391124
self.resources
11401125
.check_heap_type(&mut heap_type, self.offset)?;
11411126

1142-
let sub_ty = RefType::new(nullable, heap_type)
1143-
.map(ValType::from)
1144-
.ok_or_else(|| {
1145-
BinaryReaderError::new("implementation limit: type index too large", self.offset)
1146-
})?;
1147-
1148-
let sup_ty = self.pop_ref()?.unwrap_or_else(|| {
1149-
sub_ty
1150-
.as_reference_type()
1151-
.expect("we created this as a reference just above")
1152-
});
1153-
let sup_ty = RefType::new(true, self.resources.top_type(&sup_ty.heap_type()))
1127+
let sub_ty = RefType::new(nullable, heap_type).ok_or_else(|| {
1128+
BinaryReaderError::new("implementation limit: type index too large", self.offset)
1129+
})?;
1130+
let sup_ty = RefType::new(true, self.resources.top_type(&heap_type))
11541131
.expect("can't panic with non-concrete heap types");
11551132

1156-
if !self.resources.is_subtype(sub_ty, sup_ty.into()) {
1157-
bail!(
1158-
self.offset,
1159-
"{inst_name}'s heap type must be a sub type of the type on the stack: \
1160-
{sub_ty} is not a sub type of {sup_ty}"
1161-
);
1162-
}
1163-
1133+
self.pop_ref(Some(sup_ty))?;
11641134
Ok(sub_ty)
11651135
}
11661136

11671137
/// Common helper for both nullable and non-nullable variants of `ref.test`
11681138
/// instructions.
11691139
fn check_ref_test(&mut self, nullable: bool, heap_type: HeapType) -> Result<()> {
1170-
self.check_downcast(nullable, heap_type, "ref.test")?;
1140+
self.check_downcast(nullable, heap_type)?;
11711141
self.push_operand(ValType::I32)
11721142
}
11731143

11741144
/// Common helper for both nullable and non-nullable variants of `ref.cast`
11751145
/// instructions.
11761146
fn check_ref_cast(&mut self, nullable: bool, heap_type: HeapType) -> Result<()> {
1177-
let sub_ty = self.check_downcast(nullable, heap_type, "ref.cast")?;
1147+
let sub_ty = self.check_downcast(nullable, heap_type)?;
11781148
self.push_operand(sub_ty)
11791149
}
11801150

@@ -2696,15 +2666,15 @@ where
26962666
}
26972667

26982668
fn visit_ref_as_non_null(&mut self) -> Self::Output {
2699-
let ty = match self.pop_ref()? {
2669+
let ty = match self.pop_ref(None)? {
27002670
Some(ty) => MaybeType::Type(ValType::Ref(ty.as_non_null())),
27012671
None => MaybeType::HeapBot,
27022672
};
27032673
self.push_operand(ty)?;
27042674
Ok(())
27052675
}
27062676
fn visit_br_on_null(&mut self, relative_depth: u32) -> Self::Output {
2707-
let ref_ty = match self.pop_ref()? {
2677+
let ref_ty = match self.pop_ref(None)? {
27082678
None => MaybeType::HeapBot,
27092679
Some(ty) => MaybeType::Type(ValType::Ref(ty.as_non_null())),
27102680
};
@@ -2715,41 +2685,27 @@ where
27152685
Ok(())
27162686
}
27172687
fn visit_br_on_non_null(&mut self, relative_depth: u32) -> Self::Output {
2718-
let ty = self.pop_ref()?;
27192688
let (ft, kind) = self.jump(relative_depth)?;
27202689

27212690
let mut label_types = self.label_types(ft, kind)?;
2722-
match (label_types.next_back(), ty) {
2723-
(None, _) => bail!(
2691+
let expected = match label_types.next_back() {
2692+
None => bail!(
27242693
self.offset,
27252694
"type mismatch: br_on_non_null target has no label types",
27262695
),
2727-
(Some(ValType::Ref(_)), None) => {}
2728-
(Some(rt1 @ ValType::Ref(_)), Some(rt0)) => {
2729-
// Switch rt0, our popped type, to a non-nullable type and
2730-
// perform the match because if the branch is taken it's a
2731-
// non-null value.
2732-
let ty = rt0.as_non_null();
2733-
if !self.resources.is_subtype(ty.into(), rt1) {
2734-
bail!(
2735-
self.offset,
2736-
"type mismatch: expected {} but found {}",
2737-
ty_to_str(rt0.into()),
2738-
ty_to_str(rt1)
2739-
)
2740-
}
2741-
}
2742-
(Some(_), _) => bail!(
2696+
Some(ValType::Ref(ty)) => ty,
2697+
Some(_) => bail!(
27432698
self.offset,
27442699
"type mismatch: br_on_non_null target does not end with heap type",
27452700
),
2746-
}
2701+
};
2702+
self.pop_ref(Some(expected.nullable()))?;
27472703

27482704
self.pop_push_label_types(label_types)?;
27492705
Ok(())
27502706
}
27512707
fn visit_ref_is_null(&mut self) -> Self::Output {
2752-
self.pop_ref()?;
2708+
self.pop_ref(None)?;
27532709
self.push_operand(ValType::I32)?;
27542710
Ok(())
27552711
}

tests/local/function-references/bad-call-ref-ty.wast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
(call_ref $t (local.get $f))
66
)
77
)
8-
"funcref on stack does not match specified type")
8+
"expected (ref null $type), found funcref")
99

1010
(assert_invalid
1111
(module

tests/local/gc/br-on-non-null.wast

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(assert_invalid
2+
(module
3+
(func
4+
block $l (result anyref)
5+
ref.null func
6+
br_on_non_null $l
7+
unreachable
8+
end
9+
drop
10+
)
11+
)
12+
"expected anyref, found funcref")

tests/snapshots/local/function-references/bad-call-ref-ty.wast.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "assert_invalid",
66
"line": 2,
77
"filename": "bad-call-ref-ty.0.wasm",
8-
"text": "funcref on stack does not match specified type",
8+
"text": "expected (ref null $type), found funcref",
99
"module_type": "binary"
1010
},
1111
{
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"source_filename": "tests/local/gc/br-on-non-null.wast",
3+
"commands": [
4+
{
5+
"type": "assert_invalid",
6+
"line": 2,
7+
"filename": "br-on-non-null.0.wasm",
8+
"text": "expected anyref, found funcref",
9+
"module_type": "binary"
10+
}
11+
]
12+
}

0 commit comments

Comments
 (0)