Skip to content

Commit 6ca8dc9

Browse files
committed
Clarify type visibility rules in new factored out subsection
1 parent dfa9548 commit 6ca8dc9

2 files changed

Lines changed: 298 additions & 50 deletions

File tree

design/mvp/Explainer.md

Lines changed: 82 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ more user-focused explanation, take a look at the
3333
* [Import and export definitions](#import-and-export-definitions)
3434
* [Name uniqueness](#name-uniqueness)
3535
* [Canonical interface name](#-canonical-interface-name)
36+
* [External visibility of types](#external-visibility-of-types)
3637
* [Component invariants](#component-invariants)
3738
* [JavaScript embedding](#JavaScript-embedding)
3839
* [JS API](#JS-API)
@@ -2819,56 +2820,8 @@ import uses an `instance` type, anticipating a subsequent tooling step (likely
28192820
the one that performs dependency resolution) to select, instantiate and provide
28202821
the instance.
28212822

2822-
Validation of `export` requires that all transitive uses of resource types in
2823-
the types of exported functions or values refer to resources that were either
2824-
imported or exported (concretely, via the type index introduced by an `import`
2825-
or `export`). The optional `<externtype>?` in `export` can be used to
2826-
explicitly ascribe a type to an export which is validated to be a supertype of
2827-
the definition's type, thereby allowing a private (non-exported) type
2828-
definition to be replaced with a public (exported) type definition.
28292823

2830-
For example, in the following component:
2831-
```wat
2832-
(component
2833-
(import "R1" (type $R1 (sub resource)))
2834-
(type $R2 (resource (rep i32)))
2835-
(export $R2' "R2" (type $R2))
2836-
(func $f1 (result (own $R1)) (canon lift ...))
2837-
(func $f2 (result (own $R2)) (canon lift ...))
2838-
(func $f2' (result (own $R2')) (canon lift ...))
2839-
(export "f1" (func $f1))
2840-
;; (export "f2" (func $f2)) -- invalid
2841-
(export "f2" (func $f2) (func (result (own $R2'))))
2842-
(export "f2" (func $f2'))
2843-
)
2844-
```
2845-
the commented-out `export` is invalid because its type transitively refers to
2846-
`$R2`, which is a private type definition. This requirement is meant to address
2847-
the standard [avoidance problem] that appears in module systems with abstract
2848-
types. In particular, it ensures that a client of a component is able to
2849-
externally define a type compatible with the exports of the component.
2850-
2851-
Similar to type exports, value exports may also ascribe a type to keep the precise
2852-
value from becoming part of the type and public interface.
2853-
2854-
For example:
2855-
```wat
2856-
(component
2857-
(value $url string "https://example.com")
2858-
(export "default-url" (value $url) (value string))
2859-
)
2860-
```
2861-
2862-
The inferred type of this component is:
2863-
```wat
2864-
(component
2865-
(export "default-url" (value string))
2866-
)
2867-
```
2868-
2869-
Note, that the `url` value definition is absent from the component type
2870-
2871-
### Name Uniqueness
2824+
#### Name Uniqueness
28722825

28732826
The goal of the `label`, `exportname` and `importname` productions defined and
28742827
used above is to allow automated bindings generators to map these names into
@@ -2904,7 +2857,7 @@ annotations. For example, the validation rules for `[constructor]foo` require
29042857
for details.
29052858

29062859

2907-
### 🔗 Canonical Interface Name
2860+
#### 🔗 Canonical Interface Name
29082861

29092862
An `interfacename` (as defined above) is **canonical** iff it either:
29102863

@@ -2951,6 +2904,85 @@ temporarily permitted. These non-canonical names may trigger warnings and will
29512904
be rejected at some point in the future.
29522905

29532906

2907+
#### External Visibility of Types
2908+
2909+
Resource types and certain compound value types (specifically `record`,
2910+
`variant`, `enum`, `flags`) have an additional **external visibility**
2911+
requirement when used in the function types of imports and exports:
2912+
2913+
Like other module systems with abstract types, components have to deal with the
2914+
[avoidance problem] wherein a resource type that is in-scope when defining a
2915+
function becomes out-of-scope when that function is exported and used outside the
2916+
component. For example, in the following component, the definitions of `$R` and
2917+
`$f` are valid, but when attempting to *export* `$f`, there is a problem because
2918+
client components would have no way to refer to `$R` and thus no way to write
2919+
`f`'s function type in an import:
2920+
```wat
2921+
(component
2922+
(type $R (resource (rep i32)))
2923+
(func $f (result (own $R)) (canon lift ...))
2924+
;; ⁉️ (export "f" (func $f))
2925+
)
2926+
```
2927+
2928+
To address this problem, when validating `import` and `export` definitions, the
2929+
Component Model requires that any resource type transitively used in the type of
2930+
an import or export has an *externally-visible name*. Concretely, the `typeidx`
2931+
introduced by the `import` or `export` of a `resource` type is considered an
2932+
"externally-visible name", since the `typeidx` is uniquely associated with an
2933+
`import`/`export`, which has a name. (Note: for `export`s, the `typeidx` that
2934+
gets passed *into* the `export` does *not* get retroactively named by the
2935+
`export`; only the *new* `typeidx` introduced by the `export`.) Lastly,
2936+
according to usual component acyclicity rules, *imported* types cannot
2937+
transitively refer to the external names of *exported* types.
2938+
2939+
Based on these rules, the above example can be expanded with the following valid
2940+
imports and exports:
2941+
```wat
2942+
(component
2943+
(type $R (resource (rep i32)))
2944+
(func $f (result (own $R)) (canon lift ...))
2945+
;; ❌ (export "f" (func $f)) ;; error: f's type uses $R which has no external name
2946+
(export $R' "R" (type $R)) ;; $R' is an external name
2947+
(export "f" (func $f) (func (result (own $R')))) ;; override $f's default type to use $R'
2948+
2949+
(func $f2 (result (own $R')) (canon lift ...)) ;; alternatively: define $f2 to use $R' directly
2950+
(export "f2" (func $f2)) ;; no ascription necessary
2951+
2952+
;; ❌ (import "f3" (func (result (own $R')))) ;; imports cannot depend on exports
2953+
(import "R2" (type $R2 (sub resource))) ;; $R2 is an external name
2954+
(import "f3" (func $f3 (result (own $R2)))) ;; imports can depend on imports
2955+
2956+
(func $f4 (result (own $R2)) (canon lift ...))
2957+
(export "f4" (func $f4)) ;; exports can depend on imports
2958+
)
2959+
```
2960+
2961+
The Component Model also imposes the same external-visibility requirement on
2962+
`record`, `variant`, `enum` and `flags` value types, even though these types are
2963+
purely structural types. The reason for this theoretically-unnecessary
2964+
requirement is that, in many source languages, the source-level types that
2965+
correspond to these component-level types are not structural and thus require
2966+
some fixed type name to use in generated function signatures. For example, in C,
2967+
`struct`s, `union`s and `enum`s all require a name, and in the absence of a
2968+
component- or WIT-supplied name, a bindings generator would be forced to
2969+
mechanically generate a name that will inevitably be less meaningful than what a
2970+
component or WIT author would choose.
2971+
2972+
Thus, the preceding example also applies when `resource` is swapped with
2973+
`record`, `variant`, `enum` and `flags`:
2974+
```wat
2975+
(component
2976+
(type $R (record (field "x" u32) (field "y" u32)))
2977+
(func $f (result $R) (canon lift ...))
2978+
;; ❌ (export "f" (func $f))
2979+
(export $R' "R" (type $R))
2980+
(export "f" (func $f) (func (result $R')))
2981+
... etc
2982+
)
2983+
```
2984+
2985+
29542986
## Component Invariants
29552987

29562988
Component validation rules only allow a component to import and export
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
;; Tests Explainer.md#external-visibility-of-types
2+
3+
;; resources
4+
5+
(component definition
6+
(core module $m (func (export "f") (result i32) unreachable))
7+
(core instance $i (instantiate $m))
8+
(type $R (resource (rep i32)))
9+
(func $f (result (own $R)) (canon lift (core func $i "f")))
10+
(export $R' "r" (type $R))
11+
(export "f" (func $f) (func (result (own $R'))))
12+
(func $f2 (result (own $R')) (canon lift (core func $i "f")))
13+
(export "f2" (func $f2))
14+
(import "r2" (type $R2 (sub resource)))
15+
(import "f3" (func $f3 (result (own $R2))))
16+
(func $f4 (result (own $R2)) (canon lift (core func $i "f")))
17+
(export "f4" (func $f4)))
18+
19+
(assert_invalid
20+
(component
21+
(core module $m (func (export "f") (result i32) unreachable))
22+
(core instance $i (instantiate $m))
23+
(type $R (resource (rep i32)))
24+
(func $f (result (own $R)) (canon lift (core func $i "f")))
25+
(export "f" (func $f)))
26+
"func not valid to be used as export")
27+
28+
(assert_invalid
29+
(component
30+
(core module $m (func (export "f") (result i32) unreachable))
31+
(core instance $i (instantiate $m))
32+
(type $R (resource (rep i32)))
33+
(func $f (result (own $R)) (canon lift (core func $i "f")))
34+
(export $R' "r" (type $R))
35+
(export "f" (func $f)))
36+
"func not valid to be used as export")
37+
38+
(assert_invalid
39+
(component
40+
(type $R (resource (rep i32)))
41+
(export $R' "r" (type $R))
42+
(import "f" (func (result (own $R')))))
43+
"func not valid to be used as import")
44+
45+
(assert_invalid
46+
(component
47+
(type $R (resource (rep i32)))
48+
(import "f" (func (result (own $R)))))
49+
"func not valid to be used as import")
50+
51+
;; records
52+
53+
(component definition
54+
(core module $m (func (export "f") (result i32) unreachable))
55+
(core instance $i (instantiate $m))
56+
(type $Rec (record (field "x" u32)))
57+
(func $f (result $Rec) (canon lift (core func $i "f")))
58+
(export $Rec' "rec" (type $Rec))
59+
(export "f" (func $f) (func (result $Rec'))))
60+
61+
(assert_invalid
62+
(component
63+
(core module $m (func (export "f") (result i32) unreachable))
64+
(core instance $i (instantiate $m))
65+
(type $Rec (record (field "x" u32)))
66+
(func $f (result $Rec) (canon lift (core func $i "f")))
67+
(export "f" (func $f)))
68+
"func not valid to be used as export")
69+
70+
(assert_invalid
71+
(component
72+
(core module $m (func (export "f") (result i32) unreachable))
73+
(core instance $i (instantiate $m))
74+
(type $Rec (record (field "x" u32)))
75+
(func $f (result $Rec) (canon lift (core func $i "f")))
76+
(export $Rec' "rec" (type $Rec))
77+
(export "f" (func $f)))
78+
"func not valid to be used as export")
79+
80+
(component definition
81+
(core module $m (func (export "f") (result i32) unreachable))
82+
(core instance $i (instantiate $m))
83+
(type $R (resource (rep i32)))
84+
(export $R' "r" (type $R))
85+
(type $Rec (record (field "r" (own $R'))))
86+
(export $Rec' "rec" (type $Rec))
87+
(func $f (result $Rec') (canon lift (core func $i "f")))
88+
(export "f" (func $f)))
89+
90+
(assert_invalid
91+
(component
92+
(core module $m (func (export "f") (result i32) unreachable))
93+
(core instance $i (instantiate $m))
94+
(type $R (resource (rep i32)))
95+
(type $Rec (record (field "r" (own $R))))
96+
(export $Rec' "rec" (type $Rec))
97+
(func $f (result $Rec') (canon lift (core func $i "f")))
98+
(export "f" (func $f)))
99+
"type not valid to be used as export")
100+
101+
;; enums
102+
103+
(component definition
104+
(core module $m (func (export "f") (result i32) unreachable))
105+
(core instance $i (instantiate $m))
106+
(type $E (enum "a" "b"))
107+
(func $f (result $E) (canon lift (core func $i "f")))
108+
(export $E' "e" (type $E))
109+
(export "f" (func $f) (func (result $E'))))
110+
111+
(assert_invalid
112+
(component
113+
(core module $m (func (export "f") (result i32) unreachable))
114+
(core instance $i (instantiate $m))
115+
(type $E (enum "a" "b"))
116+
(func $f (result $E) (canon lift (core func $i "f")))
117+
(export "f" (func $f)))
118+
"func not valid to be used as export")
119+
120+
;; flags
121+
122+
(component definition
123+
(core module $m (func (export "f") (result i32) unreachable))
124+
(core instance $i (instantiate $m))
125+
(type $Fl (flags "a" "b"))
126+
(func $f (result $Fl) (canon lift (core func $i "f")))
127+
(export $Fl' "fl" (type $Fl))
128+
(export "f" (func $f) (func (result $Fl'))))
129+
130+
(assert_invalid
131+
(component
132+
(core module $m (func (export "f") (result i32) unreachable))
133+
(core instance $i (instantiate $m))
134+
(type $Fl (flags "a" "b"))
135+
(func $f (result $Fl) (canon lift (core func $i "f")))
136+
(export "f" (func $f)))
137+
"func not valid to be used as export")
138+
139+
;; variants
140+
141+
(component definition
142+
(core module $m (func (export "f") (result i32) unreachable))
143+
(core instance $i (instantiate $m))
144+
(type $V (variant (case "a") (case "b")))
145+
(func $f (result $V) (canon lift (core func $i "f")))
146+
(export $V' "v" (type $V))
147+
(export "f" (func $f) (func (result $V'))))
148+
149+
(assert_invalid
150+
(component
151+
(core module $m (func (export "f") (result i32) unreachable))
152+
(core instance $i (instantiate $m))
153+
(type $V (variant (case "a") (case "b")))
154+
(func $f (result $V) (canon lift (core func $i "f")))
155+
(export "f" (func $f)))
156+
"func not valid to be used as export")
157+
158+
;; tuples/options
159+
160+
(component definition
161+
(core module $m (func (export "f") (param i32 i32 i32 i32)))
162+
(core instance $i (instantiate $m))
163+
(func $f (param "a" (tuple u32 u32)) (param "b" (option u32))
164+
(canon lift (core func $i "f")))
165+
(export "f" (func $f)))
166+
167+
(assert_invalid
168+
(component
169+
(core module $m (func (export "f") (result i32) unreachable))
170+
(core instance $i (instantiate $m))
171+
(type $R (resource (rep i32)))
172+
(func $f (result (tuple (own $R))) (canon lift (core func $i "f")))
173+
(export "f" (func $f)))
174+
"func not valid to be used as export")
175+
176+
(component definition
177+
(core module $m (func (export "f") (result i32) unreachable))
178+
(core instance $i (instantiate $m))
179+
(type $R (resource (rep i32)))
180+
(export $R' "r" (type $R))
181+
(func $f (result (tuple (own $R'))) (canon lift (core func $i "f")))
182+
(export "f" (func $f)))
183+
184+
(assert_invalid
185+
(component
186+
(core module $m
187+
(memory (export "mem") 1)
188+
(func (export "f") (result i32) unreachable))
189+
(core instance $i (instantiate $m))
190+
(type $R (resource (rep i32)))
191+
(func $f (result (option (own $R))) (canon lift (core func $i "f") (memory $i "mem")))
192+
(export "f" (func $f)))
193+
"func not valid to be used as export")
194+
195+
(assert_invalid
196+
(component
197+
(core module $m
198+
(memory (export "mem") 1)
199+
(func (export "f") (result i32) unreachable))
200+
(core instance $i (instantiate $m))
201+
(type $Rec (record (field "x" u32)))
202+
(func $f (result (tuple $Rec u32)) (canon lift (core func $i "f") (memory $i "mem")))
203+
(export "f" (func $f)))
204+
"func not valid to be used as export")
205+
206+
;; bag-of-exports:
207+
208+
(assert_invalid
209+
(component
210+
(core module $m (func (export "f") (result i32) unreachable))
211+
(core instance $i (instantiate $m))
212+
(type $R (resource (rep i32)))
213+
(func $f (result (own $R)) (canon lift (core func $i "f")))
214+
(instance $bag (export "f" (func $f)))
215+
(export "bag" (instance $bag)))
216+
"instance not valid to be used as export")

0 commit comments

Comments
 (0)