Skip to content
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

Add static type information to exported resources #8562

Open
kaivol opened this issue May 6, 2024 · 8 comments
Open

Add static type information to exported resources #8562

kaivol opened this issue May 6, 2024 · 8 comments
Labels
wasm-proposal:component-model Issues related to the WebAssembly Component Model proposal wasmtime:api Related to the API of the `wasmtime` crate itself

Comments

@kaivol
Copy link

kaivol commented May 6, 2024

Problem

I use the following .wit to describe a WASM component which provides a long living operator that is initialized once and then invoked multiple times to perform some operation:

world example {
    export exports: interface {
        resource operator {
            init: static func(...) -> operator;
            run: func(...) -> ...;
        }
    }
}

Using this component from a wasmtime host is somewhat unergonomic, because calling call_init
returns a ResourceAny instance, which doesn't carry any (static) type information.
Calling methods on this resource is thus very verbose (see below), and there is a risk of mixing up resources of different types.

let resource = component.exports().operator().call_init(&mut store, ...)
component.exports().operator().call_run(&mut store, resource, ...)

Feature

Ideally, the rust-representation of the resource returned from a WASM component would contain sufficient type information to allow its (and only its) methods to be called with standard method syntax.

Benefit

Better ergonomics when working with resources (reduced error potential, less verbose syntax).

Implementation

It feels like this should not be too complicated, but maybe I'm missing something and what I'm suggesting isn't actually possible.

@alexcrichton
Copy link
Member

Thanks for the report! This is currently an intentional design decision but not because we think it's a good idea, moreso we couldn't think of a better idea at the time.

One tricky part here is that Rust's type system cannot statically rule out all errors here. For example if the exported resource had type T then that type T is only valid for a single instantiation of a component. Each instantiation should get a unique type T, but that can't be reflected in Rust's type system.

Not to say that prevents improving on the current situation of course. I think it'd be great if the Resource<T> approach could be fit in for more, albeit not ironclad, safety here.

@alexcrichton alexcrichton added wasmtime:api Related to the API of the `wasmtime` crate itself wasm-proposal:component-model Issues related to the WebAssembly Component Model proposal labels May 7, 2024
@FrankReh
Copy link
Contributor

FrankReh commented May 7, 2024

Each instantiation should get a unique type T, but that can't be reflected in Rust's type system.

I only ask because this seems a learning opportunity for people new to this technology.

Why should that be the case? If call_init is called twice, aren't the two operator's the same type? Separate instances of the same type?

@kaivol
Copy link
Author

kaivol commented May 7, 2024

@alexcrichton Thanks for the reply, it's always interesting to hear about the rationale / reason behind such design decisions!
Feel free to close this if you consider the issue to be resolved (at least for the time being).

@kaivol
Copy link
Author

kaivol commented May 7, 2024

Each instantiation should get a unique type T, but that can't be reflected in Rust's type system.

I only ask because this seems a learning opportunity for people new to this technology.

Why should that be the case? If call_init is called twice, aren't the two operator's the same type? Separate instances of the same type?

As I understand it, they are (only) of the same type in the context of the component's .wit defintion, but if they originate from different instantiations of the component they are nevertheless generally not compatible (although that should not be a problem if it is only about calling instance methods on them (?)).
When alex says Each instantiation he's talking about instantiations of the component, not instantiations of the exported resource (correct me if I am wrong).

@FrankReh
Copy link
Contributor

FrankReh commented May 7, 2024

Thank you. You are right that I didn't take the use of instantiation correctly. I still have questions about why this part of the API doesn't use the Rust type system more directly, but I won't hijack this thread further.

@alexcrichton
Copy link
Member

Yeah @kaivol is correct. If you have a resource A which exports a resource R, then if you instantiate the component twice producing A1 and A2 the R resource has a different type between A1/A2. Effectively the resource type has a bit of runtime state which indicates which component it came from. This is the part that I don't know how to reflect into the Rust type system statically.

I'm hesitant to close this though because it would be possible to create a type representing R which ensures that you're using "some R" with "some A". There'd still be a runtime check that the two match but that's probably a better situation than today where there's no guard rails whatsoever at compile time.

@FrankReh
Copy link
Contributor

FrankReh commented May 7, 2024

the R resource has a different type between A1/A2

Is it really a different type, or is it up to the implementation to keep track of which A component the R is tied to?

As an example, the host could keep a weak pointer to the component instance the R came from, and either the methods the host is trying to call use that inner weak pointer as the target, or it's used for a runtime comparison with the target.

(Just wanting to make clear what causes my confusion about the status quo. I feel very out of my depth asking this kind of question but all of the builders of this technology are being so helpful.)

I agree that doesn't solve the problem statically. The compiler won't tell you if you match a component instance with the wrong resource.


There are crates like generativity and async-local, which maybe try to solve the runtime instantiation vs compile time compatibility type problem but I'll admit I've never fully understood how to use them in anything but the simplest examples plus they don't support all architectures of interest plus the latter is riddled with unsafe calls so I'm in no way wanting to sound like an advocate but it does seem some projects are using them successfully.

@alexcrichton
Copy link
Member

Oh no you're right, the implementation keeps track of which types come from which instance and mistakes are detected dynamically. I was mostly reflecting on how I don't think there's a way in Rust to remove this dynamic check and reflect everything statically. I haven't looked into those crates though, myself, so I'll try to take a look!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wasm-proposal:component-model Issues related to the WebAssembly Component Model proposal wasmtime:api Related to the API of the `wasmtime` crate itself
Projects
None yet
Development

No branches or pull requests

3 participants