diff --git a/design/mvp/WIT.md b/design/mvp/WIT.md index 5fe1f403..5a6ec39e 100644 --- a/design/mvp/WIT.md +++ b/design/mvp/WIT.md @@ -1134,9 +1134,54 @@ wit-file ::= (package-decl ';')? (package-items | nested-package-definition)* nested-package-definition ::= package-decl '{' package-items* '}' package-items ::= toplevel-use-item | interface-item | world-item + | gate package-typedef-item + +package-typedef-item ::= variant-items + | record-item + | flags-items + | enum-items + | type-item ``` -Essentially, these top level items are [worlds], [interfaces], [use statements][use] and other package definitions. +Essentially, these top level items are [worlds], [interfaces], [use statements][use], [package-scope types](#package-scope-types) and other package definitions. + +### Package-scope Types + +A `variant`, `record`, `flags`, `enum` or `type` may be declared directly at +package scope, without an enclosing `interface` or `world`. A package-scope type +is in scope for every `interface` and `world` in the package, with no `use` +required, since there is no container to path through: + +```wit +package local:demo; + +record point { x: u32, y: u32 } + +enum direction { north, south, east, west } + +interface api { + move-to: func(p: point); + heading: func() -> direction; +} + +world w { + export api; +} +``` + +Package-scope types share the one name space that all top-level definitions are +exported into, so a package-scope `point` conflicts with an `interface point` or +a `world point` in the same package. That shared name space is also what lets a +`namespace:package/name` path address a package-scope type from another package, +with no new syntax: `use local:demo/point;`. + +`resource` is deliberately excluded from `package-typedef-item`, which is why it +names its alternatives rather than reusing `typedef-item`. Resource types carry +identity rather than structure, so they must be imported to be shared, and the +enclosing `interface` is what an importer names. The types above are structural, +so a referencing `interface` carries them the same way it would if they were +declared inline, and no import is introduced. The same reasoning is why outer +aliases are also limited to structural types. ### Feature Gates @@ -2093,6 +2138,39 @@ This example illustrates the basic structure of interfaces: Note that there is *always* an outer wrapping component-type, even when the interface contains no `use`s. +A [package-scope type](#package-scope-types) uses the same type-export slot with +a plain type in it, so it needs no addition to this encoding scheme. The WIT: + +```wit +package local:demo; + +record point { x: u32, y: u32 } + +interface api { + move-to: func(p: point); +} +``` + +is encoded as: + +```wat +(component + (type (export "point") (record (field "x" u32) (field "y" u32))) + (type (export "api") (component + (export "local:demo/api" (instance + (type $point (record (field "x" u32) (field "y" u32))) + (export "move-to" (func (param "p" $point))) + )) + )) +) +``` + +Because `point` is structural, `api`'s wrapping component-type carries it the +same way it would if `point` were declared inside `api`, and so has no `import` +for it. This is the difference from a `use` of an `interface`, which imports the +`interface`'s instance in order to preserve the identity of the types it +exports. It is also why `resource` may not be declared at package scope. + One useful consequence of this encoding scheme is that each top-level definition is self-contained and valid (according to Component Model validation rules) independent of each other definition. This allows packages to be