-
Notifications
You must be signed in to change notification settings - Fork 118
Add (implements "I") to plainname imports to allow multiple imports
#613
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
b6beda9
6cddcf6
e3fc63f
fd4c67b
7ada4ae
e54f2a5
b4564cb
3cdb1f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -366,6 +366,31 @@ world union-my-world-b { | |
| } | ||
| ``` | ||
|
|
||
| When a world being included contains plain-named imports or exports that | ||
| reference a named interface (using the `id: use-path` syntax), the `with` | ||
| keyword renames the plain-name label while preserving the underlying | ||
| `[implements=<I>]` annotation in the encoding. For example: | ||
|
|
||
| ```wit | ||
| package local:demo; | ||
|
|
||
| interface store { | ||
| get: func(key: string) -> option<string>; | ||
| } | ||
|
|
||
| world base { | ||
| import cache: store; | ||
| } | ||
|
|
||
| world extended { | ||
| include base with { cache as my-cache } | ||
|
alexcrichton marked this conversation as resolved.
|
||
| } | ||
| ``` | ||
|
|
||
| In this case, `extended` has a single import with the plain name `my-cache` | ||
| that implements `local:demo/store`, equivalent to writing | ||
| `import my-cache: store;` directly. | ||
|
|
||
| `with` cannot be used to rename interface names, however, so the following | ||
| world would be invalid: | ||
| ```wit | ||
|
|
@@ -1381,9 +1406,30 @@ export-item ::= 'export' id ':' extern-type | |
| import-item ::= 'import' id ':' extern-type | ||
| | 'import' use-path ';' | ||
|
|
||
| extern-type ::= func-type ';' | 'interface' '{' interface-items* '}' | ||
| extern-type ::= func-type ';' | 'interface' '{' interface-items* '}' | use-path ';' | ||
| ``` | ||
|
|
||
| The third case of `extern-type` allows a named interface to be imported or | ||
| exported with a custom [plain name]. For example: | ||
|
|
||
| ```wit | ||
| world my-world { | ||
| import primary: wasi:keyvalue/store; | ||
| import secondary: wasi:keyvalue/store; | ||
| export my-handler: wasi:http/handler; | ||
| } | ||
| ``` | ||
|
|
||
| Here, `primary` and `secondary` are two distinct imports that both have the | ||
| instance type of the `wasi:keyvalue/store` interface. The plain name of the | ||
| import is the `id` before the colon (e.g., `primary`), not the interface name. | ||
| This contrasts with `import wasi:keyvalue/store;` (without the `id :` prefix), | ||
| which would create a single import using the full interface name | ||
| `wasi:keyvalue/store`. Similarly, the export `my-handler` has the instance type | ||
| of `wasi:http/handler` but uses the plain name `my-handler` instead of the full | ||
| interface name, which is useful when a component wants to export the same | ||
| interface multiple times or simply use a more descriptive name. | ||
|
alexcrichton marked this conversation as resolved.
|
||
|
|
||
| Note that worlds can import types and define their own types to be exported | ||
| from the root of a component and used within functions imported and exported. | ||
| The `interface` item here additionally defines the grammar for IDs used to refer | ||
|
|
@@ -2061,6 +2107,52 @@ This duplication is useful in the case of cross-package references or split | |
| packages, allowing a compiled `world` definition to be fully self-contained and | ||
| able to be used to compile a component without additional type information. | ||
|
|
||
| When a world imports or exports a named interface with a custom plain name | ||
| (using the `id: use-path` syntax), the encoding uses the `[implements=<I>]` | ||
| annotation defined in [Explainer.md](Explainer.md#import-and-export-definitions) to indicate which | ||
| interface the instance implements. For example, the following WIT: | ||
|
|
||
| ```wit | ||
| package local:demo; | ||
|
|
||
| interface store { | ||
| get: func(key: string) -> option<string>; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just realized that there's a pretty nuanced technical design point (that iiuc the current impl is getting getting right atm) that this example should explicate via the generated WAT: Could you expand this example to include a Next, after the WAT, could you add a second variation of this example which factors out the
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done now, if you'd like to review |
||
| } | ||
|
|
||
| world w { | ||
| import one: store; | ||
| import two: store; | ||
| } | ||
| ``` | ||
|
|
||
| is encoded as: | ||
|
|
||
| ```wat | ||
| (component | ||
| (type (export "w") (component | ||
| (export "local:demo/w" (component | ||
| (import "[implements=<local:demo/store>]one" (instance | ||
| (export "get" (func (param "key" string) (result (option string)))) | ||
| )) | ||
| (import "[implements=<local:demo/store>]two" (instance | ||
| (export "get" (func (param "key" string) (result (option string)))) | ||
| )) | ||
| )) | ||
| )) | ||
| (type (export "store") (component | ||
| (export "local:demo/store" (instance | ||
| (export "get" (func (param "key" string) (result (option string)))) | ||
| )) | ||
| )) | ||
| ) | ||
| ``` | ||
|
|
||
| The `[implements=<local:demo/store>]` prefix tells bindings generators and | ||
| toolchains which interface each plain-named instance import implements, while | ||
| the labels `one` and `two` provide distinct plain names. This is a case of | ||
| the general `[implements=<interfacename>]label` pattern described in | ||
| [Explainer.md](Explainer.md#import-and-export-definitions). | ||
|
|
||
| Putting this all together, the following WIT definitions: | ||
|
|
||
| ```wit | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| ;; RUN: wast --assert default --snapshot tests/snapshots % | ||
|
alexcrichton marked this conversation as resolved.
Outdated
|
||
|
|
||
| ;; Valid: basic [implements=<...>] on instance import | ||
| (component | ||
| (import "[implements=<a:b/c>]name" (instance)) | ||
| ) | ||
|
|
||
| ;; Valid: [implements=<...>] on instance export | ||
| (component | ||
| (instance $i) | ||
| (export "[implements=<a:b/c>]name" (instance $i)) | ||
| ) | ||
|
|
||
| ;; Valid: two imports with the same interface, different labels | ||
| (component | ||
| (import "[implements=<a:b/c>]one" (instance)) | ||
| (import "[implements=<a:b/c>]two" (instance)) | ||
| ) | ||
|
|
||
| ;; Valid: [implements=<...>] with version in interface name | ||
| (component | ||
| (import "[implements=<a:b/c@1.2.3>]name" (instance)) | ||
| ) | ||
|
|
||
| ;; Valid: [implements=<...>] alongside a bare interface import of the same interface | ||
| (component | ||
| (import "a:b/c" (instance)) | ||
| (import "[implements=<a:b/c>]alt" (instance)) | ||
| ) | ||
|
|
||
| ;; Valid: in a component type | ||
| (component | ||
| (type (component | ||
| (import "[implements=<a:b/c>]one" (instance | ||
| (export "get" (func (param "key" string) (result (option string)))) | ||
| )) | ||
| (import "[implements=<a:b/c>]two" (instance | ||
| (export "get" (func (param "key" string) (result (option string)))) | ||
| )) | ||
| )) | ||
| ) | ||
|
|
||
| ;; Invalid: [implements=<...>] on func (must be instance) | ||
|
alexcrichton marked this conversation as resolved.
Outdated
|
||
| (assert_invalid | ||
| (component | ||
| (import "[implements=<a:b/c>]name" (func)) | ||
| ) | ||
| "`[implements=<a:b/c>]` must be on an instance import or export") | ||
|
|
||
| ;; Invalid: [implements=<...>] on component (must be instance) | ||
| (assert_invalid | ||
| (component | ||
| (import "[implements=<a:b/c>]name" (component)) | ||
| ) | ||
| "`[implements=<a:b/c>]` must be on an instance import or export") | ||
|
|
||
| ;; Invalid: duplicate labels after stripping annotation | ||
| (assert_invalid | ||
| (component | ||
| (import "[implements=<a:b/c>]name" (instance)) | ||
| (import "[implements=<x:y/z>]name" (instance)) | ||
| ) | ||
| "conflicts with previous name") | ||
|
|
||
| ;; Invalid: duplicate label between annotated and bare plain name | ||
| (assert_invalid | ||
| (component | ||
| (import "name" (func)) | ||
| (import "[implements=<a:b/c>]name" (instance)) | ||
| ) | ||
| "conflicts with previous name") | ||
|
|
||
| ;; Invalid: malformed interface name inside annotation | ||
| (assert_invalid | ||
| (component | ||
| (import "[implements=<NotValid>]name" (instance)) | ||
| ) | ||
| "not a valid extern name") | ||
Uh oh!
There was an error while loading. Please reload this page.