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
7 changes: 7 additions & 0 deletions LANGUAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,13 @@ arguments of `a:b` first.
Any unsatisfied arguments will then be satisfied by the matching exports of
instance `c`, followed by any matching the exports of instance `d`.

An export matches an instantiation argument if the names are identical or if
the names are _semver-compatible_. For example, an export named
`a:b/c@0.2.0` will satisfy an instantiation argument named `a:b/c@0.2.1`, as
both are on the `0.2` track; it will not satisfy `a:b/c@0.3.0`. An exact name
match is always preferred over a semver-compatible one. The type of the export
must still be compatible with the type of the instantiation argument.

The above behavior differs from JavaScript's spread argument syntax, which is
the inspiration for this syntax, because component instantiation arguments are
_named_ and not _positional_.
Expand Down
41 changes: 33 additions & 8 deletions crates/wac-parser/src/resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ use std::{
};
use wac_graph::{
types::{
BorrowedPackageKey, DefinedType, Enum, ExternKind, Flags, FuncKind, FuncType, FuncTypeId,
Interface, InterfaceId, ItemKind, Package, PackageKey, PrimitiveType, Record, Resource,
ResourceAlias, ResourceId, SubtypeChecker, Type, UsedType, ValueType, Variant, World,
WorldId,
are_semver_compatible, BorrowedPackageKey, DefinedType, Enum, ExternKind, Flags, FuncKind,
FuncType, FuncTypeId, Interface, InterfaceId, ItemKind, Package, PackageKey, PrimitiveType,
Record, Resource, ResourceAlias, ResourceId, SubtypeChecker, Type, UsedType, ValueType,
Variant, World, WorldId,
},
CompositionGraph, DefineTypeError, EncodeError, EncodeOptions, ExportError, ImportError,
InstantiationArgumentError, NodeId, NodeKind, PackageId, Processor,
Expand Down Expand Up @@ -2461,10 +2461,35 @@ impl<'a> AstResolver<'a> {
continue;
}

// Alias a matching export of the instance
if let Some(aliased) =
self.alias_export(state, item, name, id.span, InstanceOperation::Spread)?
{
// Prefer an exact export name match, then fall back to a semver-compatible one
let export_name = {
let exports = match item.kind(&state.graph) {
ItemKind::Instance(id) => &state.graph.types()[id].exports,
_ => unreachable!("item was checked to be an instance"),
};

if exports.contains_key(name) {
Some(name.clone())
} else {
exports
.keys()
.find(|export| are_semver_compatible(export, name))
.cloned()
}
};

let Some(export_name) = export_name else {
continue;
};

// Alias the matching export of the instance
if let Some(aliased) = self.alias_export(
state,
item,
&export_name,
id.span,
InstanceOperation::Spread,
)? {
spread = true;
arguments.insert(name.clone(), (aliased, id.span));
}
Expand Down
8 changes: 8 additions & 0 deletions crates/wac-parser/tests/encoding/spread-semver.wac
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package test:comp;

// The plug exports `test:dep/iface@0.2.0`, but the socket imports
// `test:dep/iface@0.2.1`; the spread should match them via semver compatibility.
let plug = new foo:plug { };
let socket = new foo:socket { ...plug };

export socket...;
39 changes: 39 additions & 0 deletions crates/wac-parser/tests/encoding/spread-semver.wac.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
(component
(type (;0;)
(component
(type (;0;)
(instance
(type (;0;) (func))
(export (;0;) "foo" (func (type 0)))
)
)
(export (;0;) "test:dep/iface@0.2.0" (instance (type 0)))
)
)
(import "unlocked-dep=<foo:plug>" (component (;0;) (type 0)))
(instance $plug (;0;) (instantiate 0))
(alias export $plug "test:dep/iface@0.2.0" (instance (;1;)))
(type (;1;)
(component
(type (;0;)
(instance
(type (;0;) (func))
(export (;0;) "foo" (func (type 0)))
)
)
(import "test:dep/iface@0.2.1" (instance (;0;) (type 0)))
(type (;1;) (func))
(export (;0;) "bar" (func (type 1)))
)
)
(import "unlocked-dep=<foo:socket>" (component (;1;) (type 1)))
(instance $socket (;2;) (instantiate 1
(with "test:dep/iface@0.2.1" (instance 1))
)
)
(alias export $socket "bar" (func (;0;)))
(export (;1;) "bar" (func 0))
(@producers
(processed-by "wac-parser" "0.11.0")
)
)
9 changes: 9 additions & 0 deletions crates/wac-parser/tests/encoding/spread-semver/foo/plug.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(component
(core module $m
(func (export "foo"))
)
(core instance $i (instantiate $m))
(func $foo (canon lift (core func $i "foo")))
(instance $iface (export "foo" (func $foo)))
(export "test:dep/iface@0.2.0" (instance $iface))
)
9 changes: 9 additions & 0 deletions crates/wac-parser/tests/encoding/spread-semver/foo/socket.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(component
(import "test:dep/iface@0.2.1"
(instance $i
(export "foo" (func))
)
)
(alias export $i "foo" (func $foo))
(export "bar" (func $foo))
)
Loading