Skip to content

Support multiple imports of the same interface with different names #287

Closed

Description

@peterhuene

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>.

Activity

  1. alexcrichton commented on Jan 2, 2024

    @alexcrichton
    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).

  2. lukewagner commented on Jan 3, 2024

    @lukewagner
    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?

  3. alexcrichton commented on Jan 4, 2024

    @alexcrichton
    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.

  4. rossberg commented on Jan 9, 2024

    @rossberg
    Member

    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. ;) )

  5. lukewagner commented on Mar 18, 2024

    @lukewagner
    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.

  6. lukewagner commented on Mar 19, 2025

    @lukewagner
    Member

    On the subject of roundtripping (and, in general, on how these look at the raw component level), I just wanted to capture an idea that came up earlier but I forgot about until this came up recently: instead of adding a whole new "implements" attribute on imports/exports (distinct from the name and type), we could instead put the information into the name in a structured way (similar to what we've been doing with method, constructor, static and, recently, async). For example, for the WIT:

    world w {
      import one: wasi:keyvalue/store;
      import two: wasi:keyvalue/store;
    }

    the WAT could be:

    (component
      (import "[implements=<wasi:keyvalue/store>]one" (instance ...))
      (import "[implements=<wasi:keyvalue/store>]two" (instance ...))
    )

    (or some bikeshedded variation thereof).

  7. changed the title [-]WIT: world `extern-type` rule is too restrictive[/-] [+]Support multiple imports of the same interface with different names[/+] on Jun 5, 2025
  8. added 2 commits that reference this issue on Feb 25, 2026
    0c29ac6
    b6beda9
  9. Felix-El commented on May 4, 2026

    @Felix-El

    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.

    When I discovered the awesome #613 but this is the same gap I'm seeing - and it leaves plainname-interfaces especially limited when used resources enter the game.

    Consider wasi:http/handler, which uses resources from wasi:http/types:

    world my-world {
        import primary-handler: wasi:http/handler;
        import fallback-handler: wasi:http/handler;
    }

    The resources request and response are defined in wasi:http/types, not in handler itself. Under current world encoding rules a single wasi:http/types import is implicitly injected, and both plain-named imports share the same aliased $request/$response from it — so handles are interchangeable across primary-handler and fallback-handler. That may or may not be the intended semantics, but either way the question doesn't even arise for resources defined within the interface, because there is no way to name them at all.

    If wasi:http/handler instead defined its own resource directly:

    interface handler {
        resource request;
        handle: func(r: request) -> u32;
    }

    then each plain-named import gets a fresh abstract request type — per the note in PR #613's Explainer that "resource types defined in the interface are treated as distinct for each import." But since a plain-named import cannot appear as a use-path target, those fresh types are permanently anonymous. There is no way to write:

    use primary-handler.{request};  // no such syntax

    to bring them into scope for other type definitions in the world. With an interfacename import you get that for free, since wasi:http/handler can always be used as a use target. With a plain name, the types are effectively sealed inside the instance type — visible to the component model's type checker but unreachable from WIT source.

    This creates an asymmetry: plain-named imports are strictly less expressive than interface-named ones whenever the interface exposes resource types, whether defined internally or via use. The complementary use-path extension Luke mentioned seems necessary to close that gap before plain-named imports can be used in practice for any non-trivial WASI interface.

    I guess what we need is actually a single, more fundamental extension (at world scope): allow overriding the use dependencies of an interface at the import/export site. Concretely, when writing

    import primary-handler: wasi:http/handler with { wasi:http/types => primary-types };
    import fallback-handler: wasi:http/handler with { wasi:http/types => fallback-types };
    // now needed as well:
    import primary-types: wasi:http/types
    import fallback-types: wasi:http/types

    the with clause substitutes which world import satisfies the wasi:http/types dependency slot for each plain-named import, giving each its own request/response type identity. It should probably also be possible to:

    import primary-handler: wasi:http/handler with { wasi:http/types => both-types };
    import fallback-handler: wasi:http/handler with { wasi:http/types => primary-handler[wasi:http/types] };
    // now needed as well:
    import both-types: wasi:http/types

    This is not a new binary-level concept — the wrapping component-type in the binary encoding already "parameterizes" an interface's instance type over its use dependencies (WIT.md describes this as "∀T.{instance type}"). So the semantics exist at the component model level; what's missing is WIT syntax to surface them.

    Point 1 from above (making plain-named imports addressable via use) then follows as a consequence: once you've named the substituted dependency (primary-types), its types are reachable by use primary-types.{request} like any other world import.

    Does it make sense? Is anything beyond #613 happening in that direction?

  10. lukewagner commented on May 5, 2026

    @lukewagner
    Member

    Thanks @Felix-El; I think you're exactly right. #613 really just exacerbates the need for two existing WIT expressivity gaps which require the two features (with and use-of-plain-names) you mention above. I'm not aware of an imminent plan to work on these, but given the general goal of getting WIT co-expressive with WAT before 1.0 (#614), I think we'll need to. Happy to work with you or anyone else who wants to work on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions