Repository navigation
Conversation
And struct.new_default_desc. Also update the valiation for struct.new and struct.new_default to require empty descriptors. Add an explanation of expansion to desctype to the validation conventions section and teach SpecTec about the new ExpandDesc relation to avoid errors during rendering.
Update the syntax of func externtypes and func types in the typing context to include exactness. This allows specifying exact function imports and properly typing ref.func as exact for exact imports and defined functions and otherwise as inexact.
There was a problem hiding this comment.
Having the externtype and context store explicitly EXACT or INEXACT exactness instead of just containing an EXACT? is important for ensuring that both cases are handled everywhere. At the same time, it is annoying because heaptype has a different representation of exactness (... | typeuse | EXACT typeuse), so we require this auxiliary conversion function.
Do you have a recommendation for how to simplify this? I'm thinking that maybe we should be using heaptype instead of deftype/typeuse in the externtype and context definitions because heaptype already has exactness information built-in, and we would be able to avoid conversions that way. But then we might need a new form of expansion that can fish the type use out of both exact and inexact heap types instead :/
Previously the definition of `heaptype` was `... | EXACT typeuse | typeuse`, but this made it impossible to treat the exactness as a variable. Refactor the definition to `... | _HT exact? typeuse` instead. This is similar to how nullability is treated in reference types, except that the `_HT` tag will not be displayed in the rendered spec.
…nctype Update the typing context to store heap types for functions.
rossberg
left a comment
There was a problem hiding this comment.
Generalising the syntax of function types to arbitrary heaptypes is a bit nasty. Maybe we need an auxiliary form of type representing qualified type uses like exact? typeuse? Having a name and meta variable for that might also avoid the intrusive appearance of the _HT constructor everywhere, like in the other PR.
|
Oh interesting, I used to have an |
# Conflicts: # specification/wasm-latest/1.2-syntax.types.spectec # specification/wasm-latest/1.3-syntax.instructions.spectec # spectec/src/backend-prose/gen.ml # spectec/test-frontend/TEST.md
…nctype # Conflicts: # document/core/valid/conventions.rst # specification/wasm-latest/1.2-syntax.types.spectec # specification/wasm-latest/2.4-validation.modules.spectec # spectec/test-frontend/TEST.md
|
@rossberg, I updated the approach here to match your suggestion in #102 (review). This is ready for another look. |
| C |- FUNC x : FUNC exact? dt | ||
| -- if C.FUNCS[x] = exact? dt |
There was a problem hiding this comment.
| C |- FUNC x : FUNC exact? dt | |
| -- if C.FUNCS[x] = exact? dt | |
| C |- FUNC x : FUNC edt | |
| -- if C.FUNCS[x] = edt |
There was a problem hiding this comment.
Same issue here. We can't use an exactdeftype here because externtype is defined as ... | FUNC exact? typeuse. The same fix might apply.
| ---- | ||
| -- if C' = {TYPES dt'*, GLOBALS gt_I*, FUNCS dt_I* dt*, REFS x*} | ||
| -- if C' = {TYPES dt'*, GLOBALS gt_I*, FUNCS edt_I* edt*, REFS x*} | ||
| -- if edt* = (EXACT dt)* |
There was a problem hiding this comment.
It would be appropriate if Func_ok itself returned the edt now, since that's what functions are classified with.
(Nice side effect: you can rename dt' to plain dt.)
| MEMS memtype*, | ||
| TABLES tabletype*, | ||
| FUNCS deftype_F*, | ||
| FUNCS edt_F*, |
There was a problem hiding this comment.
For consistency:
| FUNCS edt_F*, | |
| FUNCS exactdeftype*, |
| -- (Externaddr_ok: s |- FUNC funcaddr : FUNC exact? dt_F)* | ||
| -- if edt_F* = (exact? dt_F)* |
There was a problem hiding this comment.
| -- (Externaddr_ok: s |- FUNC funcaddr : FUNC exact? dt_F)* | |
| -- if edt_F* = (exact? dt_F)* | |
| -- (Externaddr_ok: s |- FUNC funcaddr : FUNC exactdeftype)* |
| \CMEMS & \memtype^\ast, \\ | ||
| \CTABLES & \tabletype^\ast, \\ | ||
| \CFUNCS & {\deftype'}^\ast, \\ | ||
| \CFUNCS & (\exact^?~\deftype')^\ast, \\ |
There was a problem hiding this comment.
| \CFUNCS & (\exact^?~\deftype')^\ast, \\ | |
| \CFUNCS & \exactdeftype^\ast, \\ |
| C |- FUNC deftype_1 <: FUNC deftype_2 | ||
| -- Deftype_sub: C |- deftype_1 <: deftype_2 | ||
| C |- FUNC exact_1? deftype_1 <: FUNC exact_2? deftype_2 | ||
| -- Heaptype_sub: C |- (_HT exact_1? deftype_1) <: (_HT exact_2? deftype_2) |
There was a problem hiding this comment.
| -- Heaptype_sub: C |- (_HT exact_1? deftype_1) <: (_HT exact_2? deftype_2) | |
| -- Heaptype_sub: C |- _HT exact_1? deftype_1 <: _HT exact_2? deftype_2 |
| syntax deftype hint(desc "defined type") = | ||
| | _DEF rectype n hint(show %.%) | ||
|
|
||
| syntax exactdeftype hint(desc "function type") hint(show (exact? deftype)) = exact? deftype |
There was a problem hiding this comment.
Hm, this show hint is rather hacky, it won't work correctly if somebody needs to use something like exactdeftype_i anywhere. I see that you avoid that by always expanding it out, but it would probably be cleaner if we introduced exactdeftype properly as a new type form.
| MEMS mt*, | ||
| TABLES tt*, | ||
| FUNCS dt_F*, | ||
| FUNCS edt_F*, |
There was a problem hiding this comment.
| FUNCS edt_F*, | |
| FUNCS edt*, |
| -- (Globaltype_ok: C_0 |- gt : OK)* | ||
| -- (Memtype_ok: C_0 |- mt : OK)* | ||
| -- (Tabletype_ok: C_0 |- tt : OK)* | ||
| -- if edt_F* = (exact? dt_F)* |
There was a problem hiding this comment.
| -- if edt_F* = (exact? dt_F)* | |
| -- if edt* = (exact? dt_F)* |
| -- (Resulttype_ok: C_0 |- t*: OK)* | ||
| -- (Resulttype_ok: C_0 |- t'* : OK)? | ||
| -- (if x < |dt_F*|)* | ||
| -- (if x < |edt_F*|)* |
There was a problem hiding this comment.
| -- (if x < |edt_F*|)* | |
| -- (if x < |edt*|)* |
There was a problem hiding this comment.
AI explanation for the change here, if you're interested:
Defining syntax
exacttypeuse = exact? typeuseand using_HT exacttypeusein heaptype causes non-exact heap types like_HT xto elaborate with a nested CaseE for exacttypeuse, whose mixop ([None; None; None]) has no leading constructor atom (hd = None).Previously in spectec/src/backend-prose/render.ml:
- case_to_el_exprs only stripped EpsE (omitted optional arguments) when hd <> None (in order to preserve \epsilon on the LHS of infix notations like \epsilon \rightarrow t^\ast, where elal is None :: Some _ :: _).
- al_to_el_expr wrapped any non-infix CaseE with non-empty elel in ParenE.
As a result, headless juxtaposition types like
exacttypeuserendered omitted optionals as (\epsilon~x) in generated prose (the same issue also affectedglobaltype = mut? valtypeandfieldtype = mut? storagetypein test-prose/TEST.md, which rendered immutable types as (\epsilon~t) and (\epsilon~zt)).Stripping EpsE for all non-infix mixops (not is_infix, where is_infix is None :: Some _ :: _) and omitting ParenE when a headless mixop reduces to a single element (None :: _, , []) fixes the prose rendering.
| -- (Memtype_ok: C_0 |- mt : OK)* | ||
| -- (Tabletype_ok: C_0 |- tt : OK)* | ||
| -- if edt_F* = (exact? dt_F)* | ||
| -- if edt* = (exact? dt_F)* |
There was a problem hiding this comment.
This line might suggest that we should introduce Exactdeftype_ok for completeness.
There was a problem hiding this comment.
The crash shouldn't be, but yes, you can only combine proper variant types that way.
The trick would be to move the _HT constructor (under some appropriate rename) into exacttypeuse, which turns it into a variant type. Then the constructor is only needed when an exacttypeuse is de/composed, and not each time a typeuse is injected into heaptype.

Update the syntax of func externtypes and func types in the typing context to include exactness. This allows specifying exact function imports and properly typing ref.func as exact for exact imports and defined functions and otherwise as inexact.