Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 79 additions & 1 deletion design/mvp/WIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down