Exact func externtype types by tlively · Pull Request #102 · WebAssembly/custom-descriptors · GitHub
Skip to content

Exact func externtype types - #102

Open
tlively wants to merge 40 commits into
mainfrom
exact-extern-functype
Open

tlively wants to merge 40 commits into
mainfrom
exact-extern-functype

Conversation

@tlively

@tlively tlively commented Jul 8, 2026

Copy link
Copy Markdown
Member

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.

tlively added 3 commits July 6, 2026 20:33
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.
@tlively
tlively requested a review from rossberg July 8, 2026 01:37
Comment thread document/core/util/macros.def Outdated
Comment on lines +93 to +96

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 :/

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured something out in #106

@tlively tlively mentioned this pull request Jul 9, 2026
4 of 51 tasks
tlively added 14 commits July 13, 2026 04:49
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 rossberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@tlively

tlively commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

Oh interesting, I used to have an exacttypeuse production similar to what you're describing, but then I switched to using heaptype because it seemed simpler. I can play around with it more, though.

# 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
Base automatically changed from struct-new-desc-syntax-validation to main October 14, 2026 14:34
@tlively tlively changed the title Exact func externtypes types Exact func externtype types Oct 15, 2026
@tlively

tlively commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

@rossberg, I updated the approach here to match your suggestion in #102 (review). This is ready for another look.

@tlively
tlively requested a review from rossberg October 15, 2026 03:09
Comment on lines +131 to +132
C |- FUNC x : FUNC exact? dt
-- if C.FUNCS[x] = exact? dt

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
C |- FUNC x : FUNC exact? dt
-- if C.FUNCS[x] = exact? dt
C |- FUNC x : FUNC edt
-- if C.FUNCS[x] = edt

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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*,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency:

Suggested change
FUNCS edt_F*,
FUNCS exactdeftype*,

Comment on lines +183 to +184
-- (Externaddr_ok: s |- FUNC funcaddr : FUNC exact? dt_F)*
-- if edt_F* = (exact? dt_F)*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- (Externaddr_ok: s |- FUNC funcaddr : FUNC exact? dt_F)*
-- if edt_F* = (exact? dt_F)*
-- (Externaddr_ok: s |- FUNC funcaddr : FUNC exactdeftype)*

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same problem here.

Comment thread document/core/appendix/properties.rst Outdated
\CMEMS & \memtype^\ast, \\
\CTABLES & \tabletype^\ast, \\
\CFUNCS & {\deftype'}^\ast, \\
\CFUNCS & (\exact^?~\deftype')^\ast, \\

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
\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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- 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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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*,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- 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*|)*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- (if x < |edt_F*|)*
-- (if x < |edt*|)*

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI explanation for the change here, if you're interested:

Defining syntax exacttypeuse = exact? typeuse and using _HT exacttypeuse in heaptype causes non-exact heap types like _HT x to 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:

  1. 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 _ :: _).
  2. al_to_el_expr wrapped any non-infix CaseE with non-empty elel in ParenE.

As a result, headless juxtaposition types like exacttypeuse rendered omitted optionals as (\epsilon~x) in generated prose (the same issue also affected globaltype = mut? valtype and fieldtype = mut? storagetype in 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.

@tlively
tlively requested a review from rossberg October 21, 2026 01:40
-- (Memtype_ok: C_0 |- mt : OK)*
-- (Tabletype_ok: C_0 |- tt : OK)*
-- if edt_F* = (exact? dt_F)*
-- if edt* = (exact? dt_F)*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line might suggest that we should introduce Exactdeftype_ok for completeness.

Comment on lines +215 to +216

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants