New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implement support for resources for C and Rust generators #604
Conversation
81233ac
to
a8400d6
Compare
As a result, there's no This will upend the |
Thanks for the feedback, @peterhuene. I'll see if I can bring back the |
Ah my bad, I forgot about that use case when I first suggested the Rust macro changes! Brainstorming around that though, what if cargo-component generated the macro? For example cargo-component could generate: macro_rules! export_foo {
($i:ident) => ($crate::wit_bindgen::generate!({
world: "filled in by cargo-component",
path: "filled in by cargo-component",
world_exports: $i,
}));
} or something along those lines, more-or-less where the user only provides their own "stuff" which is mixed with cargo-component's "stuff" which handles all the WIT files and where they are and whatnot. |
So it probably doesn't make sense to wrap the However, I think this can be solved with a proc macro that's specific to @dicej let's move forward on your implementation as is and I will start to implement a proc macro replacement for the bindings crate in |
Sorry for the communication lag -- I'm just finishing up a rewrite that brings back the |
I do think it would be good though to avoid duplicating too much, and one thing I've wanted to do for the longest time is to support wasm-encoded WIT packages to the macro, and perhaps that would work here? |
Ah ok, if the export macro sticks around I'll take a look at that when it's ready. |
I've pushed the update. Here's an example of how to use the macro with resources: https://github.com/dicej/wit-bindgen/blob/56a36d79be8aaed9dbd1bf965497fdc0fa442b4b/crates/rust/tests/codegen.rs#L230-L269 |
I think I may be missing something, but I'm also not following how this works with exported resource types? Given this input:
I think the output Rust code doesn't compile, and I'm seeing things like:
pub struct Foo {
pub x: OwnX<Self>,
}
// ...
pub trait E1 {
fn a(f: Foo) -> wit_bindgen::rt::vec::Vec<OwnX<Self>>;
}
// ...
pub struct Foo {
pub x: OwnX<Self>,
} So I think I'm basically lost in how a user-defined type gets wired up into the bindings generated for these types (e.g. some |
I think I just haven't been imaginative enough to consider all the ways resources can be used, especially across interfaces. I'll study your example and see if I can make it work properly. BTW, I suspected just appending |
So all we need to do here is patch Rust to support polymorphic modules. Problem solved! More seriously: I finally understand what @alexcrichton means about how pervasive the generics would need to be to support the |
Yep, totally fine. I'll take care of things on the |
Even with the macro-less implementation, I need to do some work to get resource aliases working per Alex's latest example. I'll push an update when that's ready. |
We were talking a bit earlier about this, but here's some codegen tests which I think are failing currently: world foo {
resource x
export foo: func() -> x
} interface x {
resource x
foo: func() -> x
}
world foo {
export x // or `import`
} |
To clarify from earlier though, I definitely don't want to create undue work for cargo-component and/or you @peterhuene, so I want to confirm that you think that's still the case? For example I'd imagine that ideally |
@alexcrichton regarding your second WIT example above: it seems Any opinions on how to differentiate the names to avoid conflicts? One option would be to define the trait one level up in the module hierarchy and refer to it as |
Ah true! In that case I'll file an issue and that can be handled separately. I'd agree though that the best solution is probably to lift it up one level. |
@alexcrichton I don't think the I envision it implemented with:
Today, a component looks like this for use bindings::{Example, export};
struct Component;
impl Example for Component {
// ...
}
export!(Component); and after these changes: struct Component;
impl Example for Component {
// ...
}
// Crate/name of macro TBD
cargo_component_bindings::generate!(Component); Where it is expecting all exported interfaces to be impl on the same type (unless there's a compelling reason for having multiple implementation types for the interface exports?). I'd imagine it to use a similar syntax for specifying the resource types: cargo_component_bindings::generate!({
implementor: Component,
resources: {
a::b: MyB,
// ...
}
}); Maybe the macro could have some default expected type names, like |
Ok cool that all sounds great to me and I feel better confirming that this won't require cargo-component to reimplement parsing or anything like that. The DX bits around |
FYI, I'm nearing the end of my day and will be out on vacation all next week, but I'm planning to add more test cases when I return on the 17th. |
While the Rust code looks really nice the generated C code for resource-drop-own and -borrow seems to have a wrong module name (own uses C style with underscores and borrow seems to use the world name). Update: cpetig@18ca99c contains a potential fix. |
813a9fc
to
f452d89
Compare
I think I've addressed all the feedback so far, so I've marked this "ready for review". @peterhuene I'm happy to make any additional macro changes you think are appropriate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for helping to push this all forward! I've left some comments mainly on the C generator for now and from my read the Rust generator is similarly shaped so they're probably similarly applicable there, but curious what you think about those before diving much more into the Rust generator
self.src, | ||
r#" | ||
#[derive(Debug)] | ||
pub struct {camel} {{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is sort of similar to the C generator I think, but I'd ideally expect this to be present during the type declaration of the resource type itself. Will that not work out though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that we need to generate the trait for the resource, which requires looping over the functions (constructor, methods, static functions) in the resource, and import_interface
is where we have access to those, not type_resource
, AFAICT. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry yeah those bits will need to happen at the end, but type declarations I'd hope could happen during the type_foo
functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could certainly do that. The other reason I did it this way was to keep the type declaration for each resource close to its trait declaration, but I guess it would be nice to have all the types declared together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's not critical really, although I think it's more important for C where items must be lexically "sorted" rather than the Rust style "dear rustc please figure it out"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It already dawned on me that lexical sorting might be the only reasonable order for C++ to define types from more complex wit structures (I need to generate both host and guest side).
The combination of a method receiving/returning a (typedef to a) record and a record containing a typedef and a resource is quite common, so I didn't see any natural ordering for these header parts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm I'm not sure I quite understand, although @cpetig it sounds like you're saying that some patterns are going to be difficult to implement in C++? Could you sketch out a *.wit
and hypothetical header with bindings "generated" to help me better understand the issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I tried to support your assessment that lexical sorting will be needed by a C or C++ code generator and might have replied a bit too terse. I am currently developing above mentioned C++ generators in https://github.com/cpetig/wit-bindgen/tree/resources/autosar and that directory contains two wit files which show this problem:
ErrorDomain (resource) depends on ErrorCodeType (typedef), ErrorCode (record) depends on the first resource and is aliased a lot (typedef), e.g. any of the future-X resources depend on the ErrorCode alias in radar.
[I am perfectly aware that these generators need re-work as I created them as I was exploring the code base]
I realized last night that the C binding generator isn't generating separate types when importing and exporting the same interface containing a resource, unlike the Rust generator. It only generates the import version, not the export one. One way to address that is to prefix all export names with |
This also provides stubs for the Java, Go, and Markdown generators. In addition to resource support, I've changed how the Rust guest generator handles exports per a conversation with Alex. Instead of generating polymorphic functions plus an `export_{name}!` to monomorphize them, we now require trait implementations to be specified as parameters to the `wit_bindgen::generate!` macro. This allows us to avoid complicated use of generics when exporting resources. For example: ``` // in foo.wit: world foo { export a: func() export b: interface { c: func() resource d { e: func() } } } ``` ``` wit_bindgen::generate!({ path: "foo.wit", world_exports: MyFoo, interface_exports: { "b": MyB } resource_exports: { "b::d": MyD } }); struct MyFoo; impl Foo for MyFoo { fn a() { ... } } struct MyB; impl exports::b::B for MyB { fn c() { ... } } struct MyD; impl exports::b::D for MyD { fn e(&self) { ... } } ``` One side-effect of the above change is that the generator needs to create stub implementations to make the codegen tests work, plus we have to be careful to avoid name conflicts among top-level exports (hence renaming the top level exports of lift-lower-foreign.wit to avoid clashes with issue573.wit). Note that there are no runtime tests yet since the Wasmtime implementation of resources is not complete as of this writing. Signed-off-by: Joel Dice <joel.dice@fermyon.com> replace hard-coded interface names with values from WIT Signed-off-by: Joel Dice <joel.dice@fermyon.com> add resource alias test and make it work Signed-off-by: Joel Dice <joel.dice@fermyon.com> update dependencies Signed-off-by: Joel Dice <joel.dice@fermyon.com> replace imports with panicking stubs on non-Wasm targets This should address the Windows CI failures. Signed-off-by: Joel Dice <joel.dice@fermyon.com> fix handling of resources imported at world level Signed-off-by: Joel Dice <joel.dice@fermyon.com> add missing cfg attribute Signed-off-by: Joel Dice <joel.dice@fermyon.com> parse export types as `syn::Path` This allows them to refer to types external to the current module. Signed-off-by: Joel Dice <joel.dice@fermyon.com> fix import module names in C generator Thanks to @cpetig for the patch: cpetig@18ca99c Signed-off-by: Joel Dice <joel.dice@fermyon.com> add more resources tests; fix issues found Signed-off-by: Joel Dice <joel.dice@fermyon.com> report missing export parameters gracefully The Rust generator requires parameters when exporting functions or interfaces, so `export_func` and `export_interface` now return a `Result<()>`. Previously, it panicked, which made for an alarming user experience. Signed-off-by: Joel Dice <joel.dice@fermyon.com> change macro syntax per review feedback Signed-off-by: Joel Dice <joel.dice@fermyon.com> improve `--exports` docs and error reporting Signed-off-by: Joel Dice <joel.dice@fermyon.com> fix resource alias handling in C generator Signed-off-by: Joel Dice <joel.dice@fermyon.com>
Yeah I think that'd work well 👍 |
Signed-off-by: Joel Dice <joel.dice@fermyon.com>
Signed-off-by: Joel Dice <joel.dice@fermyon.com>
This is all looking good to me, thanks again for working on this @dicej! I'm happy to merge whenever at this point basically and while it's probably not bug-free wit-bindgen was not bug-free beforehand anyway so I think it'd be good to start plumbing this change around and it's ok to have follow-up bug fixes |
@alexcrichton sounds good. I'm working on making the C generator generate distinct types for resources which are both imported and exported since the current behavior of completely ignoring the export is not acceptable. The only other thing on my to-do list is to eliminate the |
Yeah I think that's ok to do as a follow-up as it'll change the bindings a bit at most I think |
Signed-off-by: Joel Dice <joel.dice@fermyon.com>
Alex and I had a quick out-of-band conversation about importing and exporting the same resource in the C generator, and we decided to punt on it for now since it will require a significant refactor. For the time being, I've added a With that, I think it's ready to merge from my perspective. I'll open issues for the above and the |
👍 |
I had meant to include this in bytecodealliance#604 but forgot to `git add` it. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
I had meant to include this in #604 but forgot to `git add` it. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
This also provides stubs for the Java, Go, and Markdown generators.
In addition to resource support, I've changed how the Rust guest generator
handles exports per a conversation with Alex. Instead of generating polymorphic
functions plus an
export_{name}!
to monomorphize them, we now require traitimplementations to be specified as parameters to the
wit_bindgen::generate!
macro. This allows us to avoid complicated use of generics when exporting
resources.
For example:
One side-effect of the above change is that the generator needs to create stub
implementations to make the codegen tests work, plus we have to be careful to
avoid name conflicts among top-level exports (hence renaming the top level
exports of lift-lower-foreign.wit to avoid clashes with issue573.wit).
Note that there are no runtime tests yet since the Wasmtime implementation of
resources is not complete as of this writing.