Skip to content
Merged
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
4 changes: 0 additions & 4 deletions crates/wasmparser/src/validator/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4725,10 +4725,6 @@ impl ComponentNameContext {
"the `cm-implements` feature is not active",
offset,
)?;
match ty {
ComponentEntityType::Instance(_) => {}
_ => bail!(offset, "only instances can have an `external-id`"),
}
}

// Validate that the kebab name, if it has structure such as
Expand Down
42 changes: 32 additions & 10 deletions crates/wit-component/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,10 @@ impl<'a> EncodingState<'a> {
log::trace!("encoding function type for `{}`", func.name);
let idx = encoder.encode_func_type(resolve, func)?;

encoder.ty.export(&func.name, ComponentTypeRef::Func(idx));
encoder.ty.export(
crate::encoding::types::extern_name(&func.name, func.external_id.as_deref()),
ComponentTypeRef::Func(idx),
);
}

let ty = encoder.ty;
Expand Down Expand Up @@ -597,7 +600,10 @@ impl<'a> EncodingState<'a> {
let idx = self
.root_import_type_encoder(None)
.encode_func_type(resolve, func)?;
let func_idx = self.component.import(&name, ComponentTypeRef::Func(idx));
let func_idx = self.component.import(
crate::encoding::types::extern_name(name.as_str(), func.external_id.as_deref()),
ComponentTypeRef::Func(idx),
);
let prev = self.imported_funcs.insert(name, func_idx);
assert!(prev.is_none());
}
Expand Down Expand Up @@ -735,8 +741,15 @@ impl<'a> EncodingState<'a> {
.encode_func_type(resolve, func)?;
let core_name = world_func_core_names[&func.name];
let idx = self.encode_lift(module, &core_name, export_name, func, ty)?;
self.component
.export(export_string, ComponentExportKind::Func, idx, None);
self.component.export(
crate::encoding::types::extern_name(
&export_string,
func.external_id.as_deref(),
),
ComponentExportKind::Func,
idx,
None,
);
}
item @ WorldItem::Interface { id, .. } => {
let core_names = interface_func_core_names.get(export_name);
Expand Down Expand Up @@ -913,7 +926,10 @@ impl<'a> EncodingState<'a> {
match ty.kind {
TypeDefKind::Resource => {
let idx = nested.component.export(
ty.name.as_ref().expect("resources must be named"),
crate::encoding::types::extern_name(
ty.name.as_ref().expect("resources must be named"),
ty.external_id.as_deref(),
),
ComponentExportKind::Type,
resources[id],
None,
Expand All @@ -929,7 +945,7 @@ impl<'a> EncodingState<'a> {
for (i, (_, func)) in resolve.interfaces[export].functions.iter().enumerate() {
let ty = nested.encode_func_type(resolve, func)?;
nested.component.export(
&func.name,
crate::encoding::types::extern_name(&func.name, func.external_id.as_deref()),
ComponentExportKind::Func,
i as u32,
Some(ComponentTypeRef::Func(ty)),
Expand Down Expand Up @@ -994,26 +1010,30 @@ impl<'a> EncodingState<'a> {
fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>) {
self.component.type_function(None)
}
fn export_type(&mut self, idx: u32, name: &'a str) -> Option<u32> {
fn export_type(
&mut self,
idx: u32,
name: wasm_encoder::ComponentExternName<'a>,
) -> Option<u32> {
if self.export_types {
Some(
self.component
.export(name, ComponentExportKind::Type, idx, None),
)
} else {
let name = self.unique_import_name(name);
let name = self.unique_import_name(&name.name);
let ret = self
.component
.import(&name, ComponentTypeRef::Type(TypeBounds::Eq(idx)));
self.imports.insert(name, ret);
Some(ret)
}
}
fn export_resource(&mut self, name: &'a str) -> u32 {
fn export_resource(&mut self, name: wasm_encoder::ComponentExternName<'a>) -> u32 {
if self.export_types {
panic!("resources should already be exported")
} else {
let name = self.unique_import_name(name);
let name = self.unique_import_name(&name.name);
let ret = self
.component
.import(&name, ComponentTypeRef::Type(TypeBounds::SubResource));
Expand Down Expand Up @@ -2944,6 +2964,7 @@ impl<'a> Shims<'a> {
docs: Default::default(),
stability: Stability::Unknown,
span: Default::default(),
external_id: None,
},
if async_ {
AbiVariant::GuestImportAsync
Expand Down Expand Up @@ -3046,6 +3067,7 @@ fn task_return_options_and_type(
docs: Default::default(),
stability: Stability::Unknown,
span: Default::default(),
external_id: None,
};
let abi = AbiVariant::GuestImport;
let mut options = RequiredOptions::for_import(resolve, func, abi);
Expand Down
30 changes: 22 additions & 8 deletions crates/wit-component/src/encoding/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ pub trait ValtypeEncoder<'a> {
fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>);

/// Creates an export item for the specified type index.
fn export_type(&mut self, index: u32, name: &'a str) -> Option<u32>;
fn export_type(&mut self, index: u32, name: ComponentExternName<'a>) -> Option<u32>;

/// Creates a new `(type (sub resource))` export with the given name,
/// returning the type index that refers to the fresh type created.
fn export_resource(&mut self, name: &'a str) -> u32;
fn export_resource(&mut self, name: ComponentExternName<'a>) -> u32;

/// Returns the encoding maps used to encoding types such as id-to-index
/// maps.
Expand Down Expand Up @@ -235,7 +235,8 @@ pub trait ValtypeEncoder<'a> {
TypeDefKind::Unknown => unreachable!(),
TypeDefKind::Resource => {
let name = ty.name.as_ref().expect("resources must be named");
let index = self.export_resource(name);
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));
}
Expand Down Expand Up @@ -270,7 +271,9 @@ pub trait ValtypeEncoder<'a> {
index
}
};
let index = self.export_type(index, name).unwrap_or(index);
let index = self
.export_type(index, extern_name(name, ty.external_id.as_deref()))
.unwrap_or(index);

encoded = ComponentValType::Type(index);
}
Expand Down Expand Up @@ -429,6 +432,17 @@ pub trait ValtypeEncoder<'a> {
}
}

/// Helper to create a `ComponentExternName` from its component parts found
/// within a WIT AST node.
pub fn extern_name<'a>(name: &'a str, external_id: Option<&'a str>) -> ComponentExternName<'a> {
ComponentExternName {
name: name.into(),
implements: None,
external_id: external_id.map(|s| s.into()),
version_suffix: None,
}
}

pub struct RootTypeEncoder<'state, 'a> {
pub state: &'state mut EncodingState<'a>,
pub interface: Option<InterfaceId>,
Expand All @@ -445,7 +459,7 @@ impl<'a> ValtypeEncoder<'a> for RootTypeEncoder<'_, 'a> {
fn interface(&self) -> Option<InterfaceId> {
self.interface
}
fn export_type(&mut self, idx: u32, name: &'a str) -> Option<u32> {
fn export_type(&mut self, idx: u32, name: ComponentExternName<'a>) -> Option<u32> {
// When encoding types for the root the root component will export
// this type, but when encoding types for a targeted interface then we
// can't export types just yet. Interfaces will be created as an
Expand All @@ -466,7 +480,7 @@ impl<'a> ValtypeEncoder<'a> for RootTypeEncoder<'_, 'a> {
None
}
}
fn export_resource(&mut self, name: &'a str) -> u32 {
fn export_resource(&mut self, name: ComponentExternName<'a>) -> u32 {
assert!(self.interface.is_none());
assert!(self.import_types);
self.state
Expand Down Expand Up @@ -495,13 +509,13 @@ impl<'a> ValtypeEncoder<'a> for InstanceTypeEncoder<'_, 'a> {
fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>) {
(self.ty.type_count(), self.ty.ty().function())
}
fn export_type(&mut self, idx: u32, name: &str) -> Option<u32> {
fn export_type(&mut self, idx: u32, name: ComponentExternName<'a>) -> Option<u32> {
let ret = self.ty.type_count();
self.ty
.export(name, ComponentTypeRef::Type(TypeBounds::Eq(idx)));
Some(ret)
}
fn export_resource(&mut self, name: &str) -> u32 {
fn export_resource(&mut self, name: ComponentExternName<'a>) -> u32 {
let ret = self.ty.type_count();
self.ty
.export(name, ComponentTypeRef::Type(TypeBounds::SubResource));
Expand Down
14 changes: 7 additions & 7 deletions crates/wit-component/src/encoding/wit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::encoding::types::{TypeEncodingMaps, ValtypeEncoder};
use crate::encoding::types::{TypeEncodingMaps, ValtypeEncoder, extern_name};
use anyhow::Result;
use indexmap::IndexSet;
use std::collections::HashMap;
Expand Down Expand Up @@ -295,10 +295,10 @@ impl InterfaceEncoder<'_> {

for (name, func) in funcs {
let ty = self.encode_func_type(self.resolve, func)?;
self.ty
.as_mut()
.unwrap()
.export(name, ComponentTypeRef::Func(ty));
self.ty.as_mut().unwrap().export(
extern_name(name, func.external_id.as_deref()),
ComponentTypeRef::Func(ty),
);
}
let instance = self.pop_instance();
let idx = self.outer.type_count();
Expand Down Expand Up @@ -335,7 +335,7 @@ impl<'a> ValtypeEncoder<'a> for InterfaceEncoder<'a> {
None => (self.outer.type_count(), self.outer.ty().function()),
}
}
fn export_type(&mut self, index: u32, name: &'a str) -> Option<u32> {
fn export_type(&mut self, index: u32, name: ComponentExternName<'a>) -> Option<u32> {
match &mut self.ty {
Some(ty) => {
assert!(!self.import_types);
Expand All @@ -356,7 +356,7 @@ impl<'a> ValtypeEncoder<'a> for InterfaceEncoder<'a> {
}
}
}
fn export_resource(&mut self, name: &'a str) -> u32 {
fn export_resource(&mut self, name: ComponentExternName<'a>) -> u32 {
let type_ref = ComponentTypeRef::Type(TypeBounds::SubResource);
match &mut self.ty {
Some(ty) => {
Expand Down
9 changes: 8 additions & 1 deletion crates/wit-component/src/encoding/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,14 @@ impl<'a> ComponentWorld<'a> {
WorldItem::Interface { id, .. } => Some(*id),
};
let implements = resolve.implements_value(key, item);
let external_id = resolve.external_id_value(key, item);
// Note that `external_id` is only tracked for interface imports
// here. World-level functions and types all share the `None` entry
// in `import_map` but each item can have its own `external-id`
// which is emitted on a per-item basis instead.
let external_id = match item {
WorldItem::Function(_) | WorldItem::Type { .. } => None,
WorldItem::Interface { .. } => resolve.external_id_value(key, item),
};
let interface = import_map
.entry(import_map_key)
.or_insert_with(|| ImportedInterface {
Expand Down
51 changes: 31 additions & 20 deletions crates/wit-component/src/printing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ impl<O: Output> WitPrinter<O> {
self.new_item();
self.print_docs(&func.docs);
self.print_stability(&func.stability);
self.print_external_id(func.external_id.as_deref());
self.print_name_type(func.item_name(), TypeKind::FunctionFreestanding);
self.output.str(": ");
self.print_function(resolve, func)?;
Expand All @@ -193,29 +194,40 @@ impl<O: Output> WitPrinter<O> {
// Partition types defined in this interface into either those imported
// from foreign interfaces or those defined locally.
let mut types_to_declare = Vec::new();
let mut types_to_import: Vec<(_, &_, Vec<_>)> = Vec::new();
let mut types_to_import: Vec<(_, &TypeDef, Vec<_>)> = Vec::new();
for (name, ty_id) in types {
let ty = &resolve.types[ty_id];

// If `ty` points to another type, `other`, then this might actually
// be a `use`.
if let TypeDefKind::Type(Type::Id(other)) = ty.kind {
let other = &resolve.types[other];
match other.owner {
TypeOwner::None => {}

// `use` is only applicable when the owner of the current
// set of types is different than the owner of `other`. Once
// this is detected `types_to_import` is going to get
// modified.
other_owner if owner != other_owner => {
let other_name = other
.name
.as_ref()
.ok_or_else(|| anyhow!("cannot import unnamed type"))?;
if let Some((owner, stability, list)) = types_to_import.last_mut() {
if *owner == other_owner && ty.stability == **stability {

// As a convenience push onto the last set of types to
// import if it's to the same interface and with
// matching attributes.
if let Some((prev_owner, prev_ty, list)) = types_to_import.last_mut() {
if *prev_owner == other_owner
&& ty.stability == prev_ty.stability
&& ty.external_id == prev_ty.external_id
{
list.push((name, other_name));
continue;
}
}
types_to_import.push((
other_owner,
&ty.stability,
vec![(name, other_name)],
));
types_to_import.push((other_owner, ty, vec![(name, other_name)]));
continue;
}
_ => {}
Expand All @@ -231,9 +243,10 @@ impl<O: Output> WitPrinter<O> {
TypeOwner::World(id) => resolve.worlds[id].package.unwrap(),
TypeOwner::None => unreachable!(),
};
for (owner, stability, tys) in types_to_import {
for (owner, ty, tys) in types_to_import {
self.any_items = true;
self.print_stability(stability);
self.print_stability(&ty.stability);
self.print_external_id(ty.external_id.as_deref());
self.output.keyword("use");
self.output.str(" ");
let id = match owner {
Expand Down Expand Up @@ -266,6 +279,7 @@ impl<O: Output> WitPrinter<O> {
self.new_item();
self.print_docs(&resolve.types[id].docs);
self.print_stability(&resolve.types[id].stability);
self.print_external_id(resolve.types[id].external_id.as_deref());
match resolve.types[id].kind {
TypeDefKind::Resource => self.print_resource(
resolve,
Expand Down Expand Up @@ -295,6 +309,7 @@ impl<O: Output> WitPrinter<O> {
for func in funcs {
self.print_docs(&func.docs);
self.print_stability(&func.stability);
self.print_external_id(func.external_id.as_deref());

match &func.kind {
FunctionKind::Constructor(_) => {}
Expand Down Expand Up @@ -457,15 +472,7 @@ impl<O: Output> WitPrinter<O> {
// `docs`); for an inline `import x: interface { .. }` with no statement
// docs fall back to the interface definition's docs.
let docs = match item {
WorldItem::Interface {
id,
docs,
external_id,
..
} => {
if let Some(id) = external_id {
self.print_external_id(id);
}
WorldItem::Interface { id, docs, .. } => {
if docs.contents.is_some() {
Some(docs)
} else if matches!(name, WorldKey::Name(_)) {
Expand All @@ -483,6 +490,7 @@ impl<O: Output> WitPrinter<O> {
}

self.print_stability(item.stability(resolve));
self.print_external_id(resolve.external_id_value(name, item).as_deref());
self.output.keyword(import_or_export_keyword);
self.output.str(" ");
match name {
Expand Down Expand Up @@ -1185,7 +1193,10 @@ impl<O: Output> WitPrinter<O> {
}
}

fn print_external_id(&mut self, id: &str) {
fn print_external_id(&mut self, id: Option<&str>) {
let Some(id) = id else {
return;
};
self.output.keyword("@external-id");
self.output.str("(\"");
let mut buf = [0; 4];
Expand Down
Loading
Loading