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

WIT: world extern-type rule is too restrictive #287

Open
peterhuene opened this issue Dec 20, 2023 · 5 comments
Open

WIT: world extern-type rule is too restrictive #287

peterhuene opened this issue Dec 20, 2023 · 5 comments

Comments

@peterhuene
Copy link
Collaborator

peterhuene commented Dec 20, 2023

Currently the extern-type rule in the WIT grammar is:

extern-type ::= func-type ';' | 'interface' '{' interface-items* '}'

This means that both the import-item and export-item rules in a world may only have named function or inlined interface items.

For example:

world foo {
    import a: func();
    export b: interface {
        bar: func();
    }
}

However, it may be desirable to have a single interface definition that is imported or exported under different names in a world or to simply import or export a "known" interface with a different kebab name.

The former is currently not possible in the grammar, but it is possible with copy-and-paste:

world foo {
    import a: interface {
        bar: func();
    }

    import b: interface {
        bar: func();
    }
}

This obviously duplicates the type information on every inline interface definition.

What I propose with this issue is to add use-path as a case to the extern-type rule.

This would allow for the following:

interface bar {
    bar: func();
}

world foo {
   import a: bar;
   import b: bar;
}

and also for:

world foo {
   import my-http-handler: wasi:http/incoming-handler;
}

Note: there may be ambiguity introduced by extending the extern-type rule in the current wit-parser implementation as right now it allows lexing foo: bar as a package name made up of three tokens (<id>, ':', <id>); a hypothetical import foo: bar:baz/qux in a world might be difficult for it to parse as it will likely see that as an import of a package path with nested namespaces. The wac parser lexes package paths as individual tokens and therefore prohibits whitespace in the path, so that would lex as <import-keyword>, <id>, :, <package-path> rather than <import-keyword>, <package-path>.

@alexcrichton
Copy link
Collaborator

In terms of parsing I think that's easy enough to fix with a bit more lookahead on the wit-parser side of things, but another aspect to consider here is round-tripping this construct through the binary encoding of components. I think that may be possible by perhaps exporting an instance type and then using that as the type of the exported concrete instances (or something like that).

@lukewagner
Copy link
Member

Regarding how the WIT<-->binary roundtrip could work (which I think might run into some trouble trying to use imports/exports on first consideration): one alternative (but maybe bad) idea is to add and use an implements annotation on imports and exports, which is an idea that has come up for other reasons separately.

The original idea of implements is that when you have an import like (import "foo" (implements "wasi:http/handler") (instance ...)), the implements attribute is neither part of the name (which is the plainname foo) nor the type (which is (instance ...)); it's a third field that gives you a sortof "hint" about the semantic contract of foo. Because it's not a name nor a type, the implements annotation doesn't participate in validation (all typing rules ignore it), and also I think include would fail if you attempt to union two worlds with the same plainname, even if they have the same implements (they don't get unified, whereas they would if they had the same interfacename). Instead, implements could be useful as a hint to developer tooling in some contexts to provide helpful suggestions (say, to fill an autocomplete list if you're writing WAC in an IDE).

So, building on this C-M idea: maybe the WIT-level meaning of this new syntactic form @peterhuene is suggesting (which I agree seems useful) is that import foo: wasi:http/handler turns into (import "foo" (implements "wasi:http/handler") (instance ...)). Thoughts?

@alexcrichton
Copy link
Collaborator

That seems plausible to me yeah, the validation of the implements tag would need to happen during WIT decoding-from-wasm but that seems tractable and isn't much more difficult than other forms of extra validation at that layer already happening.

@rossberg
Copy link
Member

rossberg commented Jan 9, 2024

FTR, I made the same observation. If you want WIT to scale, then there really ought to be a declaration form for naming interface types. (And as Luke is aware, I strongly believe that the off interpretation of "interface" in WIT is at odds with doing this in the natural manner. ;) )

@lukewagner
Copy link
Member

Capturing a point made earlier by @dicej for posterity: as soon as you add this feature, you also want a complementary extension to WIT to be able to refer to the plainname identifiers from later uses, so that types can be shared as much as with interfacenames.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants