This is a question related to implementing a Component Model bindings generator (specially wit-bindgen-go):
Let’s say you have an interface that defines a resource type “fd” and a world that both imports and exports that interface, and a second exported interface “utils” that contains functions that accept an fd argument.
Which fd type does utils actually use? Is the CM spec ambiguous or is there a heuristic? Should the choice be represented in the wit-parser Resolve struct?
For imported functions, this is easy—use the imported fd type.
Randy Reddig has marked this topic as resolved.
Randy Reddig has marked this topic as unresolved.
What ends up happening here is a bit subtle and it's generally related to WIT conventions. The CM itself has no ambiguity, the problem arises when WIT is mapped back to the component model. To answer your question the cases are:
utils
is imported, then it uses the imported fd
utils
is exported, and fd
is not exported, then it uses an imported fd
utils
is exported, and fd
is exported, then it will use the exported fd
there's also implicit insertion of interfaces to handle here too, for example wasm-tools component wit ./my-wit
will show the "elaborated" version of a world. For example if you do export utils;
that'll implicitly insert import fd;
. If you have export fd; export utils;
, however, then no implicit insertion happens and utils
uses the exported fd
.
Interesting, thanks. Is (or was) it the intention to allow WIT to express explicitly which fd
to use?
Would it make sense for this logic to live in the wit-parser
crate, so the Resolve
structure can express whether a type is imported or exported?
Thinking about this a little more: if a component exports a type, but there is no exported constructor
nor any exported functions that return that type, then is the type actually exported?
(I was down this rabbit hole thinking about structural types like record
that might include an exported resource
. Resolving those types would have a similar challenge, e.g. which fd
to use.)
Yes the intention is to eventuall enable WIT to describe this all more explicitly as a sort of "power user" syntax, there's related discussion on https://github.com/WebAssembly/component-model/pull/308 and I think one or two other locations too.
Would it make sense for this logic to live in the wit-parser crate, so the Resolve structure can express whether a type is imported or exported?
Definitely! In retrospect this is a major design flaw of wit-parser today. The interface fd
is mistakenly represented as a single interface in Resolve
when in fact it should be duplicated for when it's both imported-and-exported.
if a component exports a type, but there is no exported
constructor
nor any exported functions that return that type, then is the type actually exported?
I think this only comes up with resource
s since all other types are "nominal" and can be constructed at any time. In such a situation as you describe the type isn't useful since no one can create it, but it's still an exported type and can participate in type-checking and such.
I was wondering if it would be possible to fix this discrepancy with resource types by making them duck-typed, and adding a new canon
function that resolves a duck-typed resource
to a locally-defined resource type.
While it might fix this particular issue, I believe that duck-typed resources would erode many guarantees you currently get with resources and perhaps create a lot of other conundrums, so I don't think it's quite so simple
I could be wrong though! That's probably best discussed in an issue
OK!
I’ve been thinking about duck-typed resources as a way to resolve the ambiguity between WIT/CM for resource
types or any type that includes resource
Definitely! In retrospect this is a major design flaw of wit-parser today. The interface
fd
is mistakenly represented as a single interface inResolve
when in fact it should be duplicated for when it's both imported-and-exported.
Where would it make sense for the wit-parser
crate to represent the imported vs exported attribute? At the type (and function) level, or at the interface
level?
It seems like this could be a backwards-compatible change to wit-parser
, in the sense that it would potentially duplicate interfaces/types/functions, with an additional attribute to signal whether they’re imported or exported?
I think this would be best represented at the interface level right now since that's the unit of import/export, and yeah I agree it should largely be backwards-compatible and would end up probably deleting more code downstream of wit-parser than would need to be added to wit-parser
To simplify the carrying of state, could we also add the attribute to TypeDef
and Function
?
I think that'd be reasonable yeah
I can file an issue on wasm-tools
repo describing this. That work?
I’d implement it, but I don’t feel confident enough in my Rust chops
mind opening an issue to start off? I've been meaning to track this for awhile
https://github.com/bytecodealliance/wasm-tools/issues/1497
Circuling back to this issue:
world exports-use-from-interface {
use a.{fd}
export b: func(f: fd);
}
it seems strange to me taht the WIT parser resolve to import the entirety of interface a
even though we only specify a.{fd}
.
In an actual component that exports b
it should only import the parts of a
it actually needs, but at the WIT layer (e.g. Resolve
) there's no way to represent a partial interface so the only option is to have an import for the whole interface
Thanks, that makes sense, @Alex Crichton . I'd like to stress the proposal that adds attribute to TypeDef
and Function
more. Imo, adding direction
to Interface
is suffice to differentiate what type to use in exported functions. Is there any particular reason for adding direction
to Function and Types?
Sorry I'm not sure I quite understand, can you clarify what you mean about direction
and the "adds attribute" part?
Alex Crichton said:
Sorry I'm not sure I quite understand, can you clarify what you mean about
direction
and the "adds attribute" part?
Oh sorry, yes I can clarify. I was referring to this issue where it proposes to add an attribute to the Interface
, Function
, and TypeDef
structs to indicate whether it is imported or exported. I assumed the name of that attribute to be direction
.
ah ok yeah no definitely makes sense to add still!
Last updated: Dec 23 2024 at 12:05 UTC