Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/wit-component/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ impl<'a> EncodingState<'a> {
self.component
.alias_export(instance, name, ComponentExportKind::Type)
}
TypeOwner::None => panic!("resources must have an owner"),
TypeOwner::Package(_) | TypeOwner::None => panic!("resources must have an owner"),
}
}

Expand Down
165 changes: 96 additions & 69 deletions crates/wit-component/src/encoding/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl<'a> TypeEncodingMaps<'a> {
}

/// Inserts that `id` was encoded as `index`.
fn insert(&mut self, resolve: &'a Resolve, id: TypeId, index: u32) {
pub(crate) fn insert(&mut self, resolve: &'a Resolve, id: TypeId, index: u32) {
// Always record the id=>index mapping.
self.id_to_index.insert(id, index);

Expand Down Expand Up @@ -121,6 +121,15 @@ pub trait ValtypeEncoder<'a> {
/// imported type into this index space.
fn import_type(&mut self, interface: InterfaceId, id: TypeId) -> u32;

/// Imports the package-scope type `id`, returning the index of the imported
/// type into this index space.
///
/// Returns `None` when this encoder defines package-scope types itself
/// rather than depending on them.
fn import_package_type(&mut self, _resolve: &'a Resolve, _id: TypeId) -> Result<Option<u32>> {
Ok(None)
}

/// Returns the identifier of the interface that generation is for.
fn interface(&self) -> Option<InterfaceId>;

Expand Down Expand Up @@ -193,72 +202,27 @@ pub trait ValtypeEncoder<'a> {

let ty = &resolve.types[id];

// If this type is imported from another interface then return
// it as it was bound here with an alias.
// If this type is imported from another interface, or from
// package scope, then return it as it was bound here with an
// alias.
log::trace!("encode type name={:?} {:?}", ty.name, &ty.kind);
if let Some(index) = self.maybe_import_type(resolve, id) {
if let Some(index) = self.maybe_import_type(resolve, id)? {
self.type_encoding_maps().insert(resolve, id, index);
return Ok(ComponentValType::Type(index));
}

// Resources are named by the export that creates them rather
// than by a structural definition, so they don't participate in
// the define-then-name dance below.
if let TypeDefKind::Resource = &ty.kind {
let name = ty.name.as_ref().expect("resources must be named");
let index = self.export_resource(extern_name(name, ty.external_id.as_deref()));
self.type_encoding_maps().id_to_index.insert(id, index);
return Ok(ComponentValType::Type(index));
}

// ... and failing all that insert the type export.
let mut encoded = match &ty.kind {
TypeDefKind::Record(r) => self.encode_record(resolve, r)?,
TypeDefKind::Tuple(t) => self.encode_tuple(resolve, t)?,
TypeDefKind::Flags(r) => self.encode_flags(r)?,
TypeDefKind::Variant(v) => self.encode_variant(resolve, v)?,
TypeDefKind::Option(t) => self.encode_option(resolve, t)?,
TypeDefKind::Result(r) => self.encode_result(resolve, r)?,
TypeDefKind::Enum(e) => self.encode_enum(e)?,
TypeDefKind::List(ty) => {
let ty = self.encode_valtype(resolve, ty)?;
let (index, encoder) = self.defined_type();
encoder.list(ty);
ComponentValType::Type(index)
}
TypeDefKind::Map(key_ty, value_ty) => {
let key = self.encode_valtype(resolve, key_ty)?;
let value = self.encode_valtype(resolve, value_ty)?;
let (index, encoder) = self.defined_type();
encoder.map(key, value);
ComponentValType::Type(index)
}
TypeDefKind::FixedLengthList(ty, elements) => {
let ty = self.encode_valtype(resolve, ty)?;
let (index, encoder) = self.defined_type();
encoder.fixed_length_list(ty, *elements);
ComponentValType::Type(index)
}
TypeDefKind::Type(ty) => self.encode_valtype(resolve, ty)?,
TypeDefKind::Future(ty) => self.encode_future(resolve, ty)?,
TypeDefKind::Stream(ty) => self.encode_stream(resolve, ty)?,
TypeDefKind::Unknown => unreachable!(),
TypeDefKind::Resource => {
let name = ty.name.as_ref().expect("resources must be named");
let index =
self.export_resource(extern_name(name, ty.external_id.as_deref()));
self.type_encoding_maps().id_to_index.insert(id, index);
return Ok(ComponentValType::Type(index));
}
TypeDefKind::Handle(Handle::Own(id)) => {
let ty = match self.encode_valtype(resolve, &Type::Id(*id))? {
ComponentValType::Type(index) => index,
_ => panic!("must be an indexed type"),
};
let (index, encoder) = self.defined_type();
encoder.own(ty);
ComponentValType::Type(index)
}
TypeDefKind::Handle(Handle::Borrow(id)) => {
let ty = match self.encode_valtype(resolve, &Type::Id(*id))? {
ComponentValType::Type(index) => index,
_ => panic!("must be an indexed type"),
};
let (index, encoder) = self.defined_type();
encoder.borrow(ty);
ComponentValType::Type(index)
}
};
let mut encoded = self.encode_typedef_structure(resolve, id)?;

if let Some(name) = &ty.name {
let index = match encoded {
Expand Down Expand Up @@ -287,20 +251,83 @@ pub trait ValtypeEncoder<'a> {
})
}

/// Optionally imports `id` from a different interface, returning the index
/// of the imported type into this index space.
/// Encodes the structure of `id`'s definition into this index space.
///
/// Unlike [`ValtypeEncoder::encode_valtype`] this never names the result, so
/// it's suitable for restating a definition that is going to be named some
/// other way, such as by the `import` that binds a package-scope type.
fn encode_typedef_structure(
&mut self,
resolve: &'a Resolve,
id: TypeId,
) -> Result<ComponentValType> {
Ok(match &resolve.types[id].kind {
TypeDefKind::Record(r) => self.encode_record(resolve, r)?,
TypeDefKind::Tuple(t) => self.encode_tuple(resolve, t)?,
TypeDefKind::Flags(r) => self.encode_flags(r)?,
TypeDefKind::Variant(v) => self.encode_variant(resolve, v)?,
TypeDefKind::Option(t) => self.encode_option(resolve, t)?,
TypeDefKind::Result(r) => self.encode_result(resolve, r)?,
TypeDefKind::Enum(e) => self.encode_enum(e)?,
TypeDefKind::List(ty) => {
let ty = self.encode_valtype(resolve, ty)?;
let (index, encoder) = self.defined_type();
encoder.list(ty);
ComponentValType::Type(index)
}
TypeDefKind::Map(key_ty, value_ty) => {
let key = self.encode_valtype(resolve, key_ty)?;
let value = self.encode_valtype(resolve, value_ty)?;
let (index, encoder) = self.defined_type();
encoder.map(key, value);
ComponentValType::Type(index)
}
TypeDefKind::FixedLengthList(ty, elements) => {
let ty = self.encode_valtype(resolve, ty)?;
let (index, encoder) = self.defined_type();
encoder.fixed_length_list(ty, *elements);
ComponentValType::Type(index)
}
TypeDefKind::Type(ty) => self.encode_valtype(resolve, ty)?,
TypeDefKind::Future(ty) => self.encode_future(resolve, ty)?,
TypeDefKind::Stream(ty) => self.encode_stream(resolve, ty)?,
TypeDefKind::Handle(Handle::Own(id)) => {
let ty = match self.encode_valtype(resolve, &Type::Id(*id))? {
ComponentValType::Type(index) => index,
_ => panic!("must be an indexed type"),
};
let (index, encoder) = self.defined_type();
encoder.own(ty);
ComponentValType::Type(index)
}
TypeDefKind::Handle(Handle::Borrow(id)) => {
let ty = match self.encode_valtype(resolve, &Type::Id(*id))? {
ComponentValType::Type(index) => index,
_ => panic!("must be an indexed type"),
};
let (index, encoder) = self.defined_type();
encoder.borrow(ty);
ComponentValType::Type(index)
}
TypeDefKind::Resource => unreachable!("resources have no structure to encode"),
TypeDefKind::Unknown => unreachable!(),
})
}

/// Optionally imports `id` from a different interface or from package scope,
/// returning the index of the imported type into this index space.
///
/// Returns `None` if `id` can't be imported.
fn maybe_import_type(&mut self, resolve: &Resolve, id: TypeId) -> Option<u32> {
let ty = &resolve.types[id];
let owner = match ty.owner {
fn maybe_import_type(&mut self, resolve: &'a Resolve, id: TypeId) -> Result<Option<u32>> {
let owner = match resolve.types[id].owner {
TypeOwner::Interface(i) => i,
_ => return None,
TypeOwner::Package(_) => return self.import_package_type(resolve, id),
TypeOwner::World(_) | TypeOwner::None => return Ok(None),
};
if Some(owner) == self.interface() {
return None;
return Ok(None);
}
Some(self.import_type(owner, id))
Ok(Some(self.import_type(owner, id)))
}

fn encode_optional_valtype(
Expand Down
Loading
Loading