Skip to content

Commit de775dd

Browse files
authored
Beef up validity assertions on Resolve (#1785)
This commit extends the `Resolve::assert_valid` function with more assertions about the structure of worlds notably to guarantee they are "elaborated" meaning that they always list all dependencies of imports. This is required by bindings generators and encoding. This property is already upheld internally and is intended to reflect a preexisting property with dynamic assertion checks. The underlying motivation for this is to assist in the development and fuzzing of #1784.
1 parent 564157e commit de775dd

3 files changed

Lines changed: 328 additions & 33 deletions

File tree

crates/wit-parser/src/lib.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub use sizealign::*;
2323
mod resolve;
2424
pub use resolve::{Package, PackageId, Remap, Resolve};
2525
mod live;
26-
pub use live::LiveTypes;
26+
pub use live::{LiveTypes, TypeIdVisitor};
2727

2828
#[cfg(feature = "serde")]
2929
use serde_derive::Serialize;
@@ -808,6 +808,18 @@ pub enum FunctionKind {
808808
Constructor(TypeId),
809809
}
810810

811+
impl FunctionKind {
812+
/// Returns the resource, if present, that this function kind refers to.
813+
pub fn resource(&self) -> Option<TypeId> {
814+
match self {
815+
FunctionKind::Freestanding => None,
816+
FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) => {
817+
Some(*id)
818+
}
819+
}
820+
}
821+
}
822+
811823
impl Function {
812824
pub fn item_name(&self) -> &str {
813825
match &self.kind {
@@ -819,6 +831,17 @@ impl Function {
819831
}
820832
}
821833

834+
/// Returns an iterator over the types used in parameters and results.
835+
///
836+
/// Note that this iterator is not transitive, it only iterates over the
837+
/// direct references to types that this function has.
838+
pub fn parameter_and_result_types(&self) -> impl Iterator<Item = Type> + '_ {
839+
self.params
840+
.iter()
841+
.map(|(_, t)| *t)
842+
.chain(self.results.iter_types().copied())
843+
}
844+
822845
/// Gets the core export name for this function.
823846
pub fn core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> {
824847
match interface {

crates/wit-parser/src/live.rs

Lines changed: 159 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
2-
Function, FunctionKind, InterfaceId, Resolve, Type, TypeDefKind, TypeId, WorldId, WorldItem,
2+
Function, FunctionKind, InterfaceId, Resolve, Type, TypeDef, TypeDefKind, TypeId, WorldId,
3+
WorldItem,
34
};
45
use indexmap::IndexSet;
56

@@ -18,36 +19,88 @@ impl LiveTypes {
1819
}
1920

2021
pub fn add_interface(&mut self, resolve: &Resolve, iface: InterfaceId) {
22+
self.visit_interface(resolve, iface);
23+
}
24+
25+
pub fn add_world(&mut self, resolve: &Resolve, world: WorldId) {
26+
self.visit_world(resolve, world);
27+
}
28+
29+
pub fn add_world_item(&mut self, resolve: &Resolve, item: &WorldItem) {
30+
self.visit_world_item(resolve, item);
31+
}
32+
33+
pub fn add_func(&mut self, resolve: &Resolve, func: &Function) {
34+
self.visit_func(resolve, func);
35+
}
36+
37+
pub fn add_type_id(&mut self, resolve: &Resolve, ty: TypeId) {
38+
self.visit_type_id(resolve, ty);
39+
}
40+
41+
pub fn add_type(&mut self, resolve: &Resolve, ty: &Type) {
42+
self.visit_type(resolve, ty);
43+
}
44+
}
45+
46+
impl TypeIdVisitor for LiveTypes {
47+
fn before_visit_type_id(&mut self, id: TypeId) -> bool {
48+
!self.set.contains(&id)
49+
}
50+
51+
fn after_visit_type_id(&mut self, id: TypeId) {
52+
assert!(self.set.insert(id));
53+
}
54+
}
55+
56+
/// Helper trait to walk the structure of a type and visit all `TypeId`s that
57+
/// it refers to, possibly transitively.
58+
pub trait TypeIdVisitor {
59+
/// Callback invoked just before a type is visited.
60+
///
61+
/// If this function returns `false` the type is not visited, otherwise it's
62+
/// recursed into.
63+
fn before_visit_type_id(&mut self, id: TypeId) -> bool {
64+
let _ = id;
65+
true
66+
}
67+
68+
/// Callback invoked once a type is finished being visited.
69+
fn after_visit_type_id(&mut self, id: TypeId) {
70+
let _ = id;
71+
}
72+
73+
fn visit_interface(&mut self, resolve: &Resolve, iface: InterfaceId) {
2174
let iface = &resolve.interfaces[iface];
2275
for (_, id) in iface.types.iter() {
23-
self.add_type_id(resolve, *id);
76+
self.visit_type_id(resolve, *id);
2477
}
2578
for (_, func) in iface.functions.iter() {
26-
self.add_func(resolve, func);
79+
self.visit_func(resolve, func);
2780
}
2881
}
2982

30-
pub fn add_world(&mut self, resolve: &Resolve, world: WorldId) {
83+
fn visit_world(&mut self, resolve: &Resolve, world: WorldId) {
3184
let world = &resolve.worlds[world];
3285
for (_, item) in world.imports.iter().chain(world.exports.iter()) {
33-
self.add_world_item(resolve, item);
86+
self.visit_world_item(resolve, item);
3487
}
3588
}
3689

37-
pub fn add_world_item(&mut self, resolve: &Resolve, item: &WorldItem) {
90+
fn visit_world_item(&mut self, resolve: &Resolve, item: &WorldItem) {
3891
match item {
39-
WorldItem::Interface { id, .. } => self.add_interface(resolve, *id),
40-
WorldItem::Function(f) => self.add_func(resolve, f),
41-
WorldItem::Type(t) => self.add_type_id(resolve, *t),
92+
WorldItem::Interface { id, .. } => self.visit_interface(resolve, *id),
93+
WorldItem::Function(f) => self.visit_func(resolve, f),
94+
WorldItem::Type(t) => self.visit_type_id(resolve, *t),
4295
}
4396
}
4497

45-
pub fn add_func(&mut self, resolve: &Resolve, func: &Function) {
98+
fn visit_func(&mut self, resolve: &Resolve, func: &Function) {
4699
match func.kind {
47100
// This resource is live as it's attached to a static method but
48101
// it's not guaranteed to be present in either params or results, so
49102
// be sure to attach it here.
50-
FunctionKind::Static(id) => self.add_type_id(resolve, id),
103+
FunctionKind::Static(id) => self.visit_type_id(resolve, id),
51104

52105
// The resource these are attached to is in the params/results, so
53106
// no need to re-add it here.
@@ -57,70 +110,146 @@ impl LiveTypes {
57110
}
58111

59112
for (_, ty) in func.params.iter() {
60-
self.add_type(resolve, ty);
113+
self.visit_type(resolve, ty);
61114
}
62115
for ty in func.results.iter_types() {
63-
self.add_type(resolve, ty);
116+
self.visit_type(resolve, ty);
64117
}
65118
}
66119

67-
pub fn add_type_id(&mut self, resolve: &Resolve, ty: TypeId) {
68-
if self.set.contains(&ty) {
69-
return;
120+
fn visit_type_id(&mut self, resolve: &Resolve, ty: TypeId) {
121+
if self.before_visit_type_id(ty) {
122+
self.visit_type_def(resolve, &resolve.types[ty]);
123+
self.after_visit_type_id(ty);
70124
}
71-
match &resolve.types[ty].kind {
125+
}
126+
127+
fn visit_type_def(&mut self, resolve: &Resolve, ty: &TypeDef) {
128+
match &ty.kind {
72129
TypeDefKind::Type(t)
73130
| TypeDefKind::List(t)
74131
| TypeDefKind::Option(t)
75-
| TypeDefKind::Future(Some(t)) => self.add_type(resolve, t),
132+
| TypeDefKind::Future(Some(t)) => self.visit_type(resolve, t),
76133
TypeDefKind::Handle(handle) => match handle {
77-
crate::Handle::Own(ty) => self.add_type_id(resolve, *ty),
78-
crate::Handle::Borrow(ty) => self.add_type_id(resolve, *ty),
134+
crate::Handle::Own(ty) => self.visit_type_id(resolve, *ty),
135+
crate::Handle::Borrow(ty) => self.visit_type_id(resolve, *ty),
79136
},
80137
TypeDefKind::Resource => {}
81138
TypeDefKind::Record(r) => {
82139
for field in r.fields.iter() {
83-
self.add_type(resolve, &field.ty);
140+
self.visit_type(resolve, &field.ty);
84141
}
85142
}
86143
TypeDefKind::Tuple(r) => {
87144
for ty in r.types.iter() {
88-
self.add_type(resolve, ty);
145+
self.visit_type(resolve, ty);
89146
}
90147
}
91148
TypeDefKind::Variant(v) => {
92149
for case in v.cases.iter() {
93150
if let Some(ty) = &case.ty {
94-
self.add_type(resolve, ty);
151+
self.visit_type(resolve, ty);
95152
}
96153
}
97154
}
98155
TypeDefKind::Result(r) => {
99156
if let Some(ty) = &r.ok {
100-
self.add_type(resolve, ty);
157+
self.visit_type(resolve, ty);
101158
}
102159
if let Some(ty) = &r.err {
103-
self.add_type(resolve, ty);
160+
self.visit_type(resolve, ty);
104161
}
105162
}
106163
TypeDefKind::Stream(s) => {
107164
if let Some(ty) = &s.element {
108-
self.add_type(resolve, ty);
165+
self.visit_type(resolve, ty);
109166
}
110167
if let Some(ty) = &s.end {
111-
self.add_type(resolve, ty);
168+
self.visit_type(resolve, ty);
112169
}
113170
}
114171
TypeDefKind::Flags(_) | TypeDefKind::Enum(_) | TypeDefKind::Future(None) => {}
115172
TypeDefKind::Unknown => unreachable!(),
116173
}
117-
assert!(self.set.insert(ty));
118174
}
119175

120-
pub fn add_type(&mut self, resolve: &Resolve, ty: &Type) {
176+
fn visit_type(&mut self, resolve: &Resolve, ty: &Type) {
121177
match ty {
122-
Type::Id(id) => self.add_type_id(resolve, *id),
178+
Type::Id(id) => self.visit_type_id(resolve, *id),
123179
_ => {}
124180
}
125181
}
126182
}
183+
184+
#[cfg(test)]
185+
mod tests {
186+
use super::{LiveTypes, Resolve};
187+
188+
fn live(wit: &str, ty: &str) -> Vec<String> {
189+
let mut resolve = Resolve::default();
190+
resolve.push_str("test.wit", wit).unwrap();
191+
let (_, interface) = resolve.interfaces.iter().next_back().unwrap();
192+
let ty = interface.types[ty];
193+
let mut live = LiveTypes::default();
194+
live.add_type_id(&resolve, ty);
195+
196+
live.iter()
197+
.filter_map(|ty| resolve.types[ty].name.clone())
198+
.collect()
199+
}
200+
201+
#[test]
202+
fn no_deps() {
203+
let types = live(
204+
"
205+
package foo:bar;
206+
207+
interface foo {
208+
type t = u32;
209+
}
210+
",
211+
"t",
212+
);
213+
assert_eq!(types, ["t"]);
214+
}
215+
216+
#[test]
217+
fn one_dep() {
218+
let types = live(
219+
"
220+
package foo:bar;
221+
222+
interface foo {
223+
type t = u32;
224+
type u = t;
225+
}
226+
",
227+
"u",
228+
);
229+
assert_eq!(types, ["t", "u"]);
230+
}
231+
232+
#[test]
233+
fn chain() {
234+
let types = live(
235+
"
236+
package foo:bar;
237+
238+
interface foo {
239+
resource t1;
240+
record t2 {
241+
x: t1,
242+
}
243+
variant t3 {
244+
x(t2),
245+
}
246+
flags t4 { a }
247+
enum t5 { a }
248+
type t6 = tuple<t5, t4, t3>;
249+
}
250+
",
251+
"t6",
252+
);
253+
assert_eq!(types, ["t5", "t4", "t1", "t2", "t3", "t6"]);
254+
}
255+
}

0 commit comments

Comments
 (0)