WebAssembly / component-model

Repository for design and specification of the Component Model
Other
898 stars 75 forks source link

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

Open peterhuene opened 6 months ago

peterhuene commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 3 months ago

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.