Skip to content

Update exact function imports#75

Merged
tlively merged 5 commits intomainfrom
update-exact-func-import
Nov 5, 2025
Merged

Update exact function imports#75
tlively merged 5 commits intomainfrom
update-exact-func-import

Conversation

@tlively
Copy link
Member

@tlively tlively commented Oct 31, 2025

Address feedback from #72, including by using (exact, deftype) pairs
instead of full heaptypes where possible in the interpreter. Update the
overview accordingly.

Also fix #74 by introducing a new externtype binary encoding for exact
function imports rather than using an s33 in the existing function
import variant.

Address feedback from #72, including by using (exact, deftype) pairs
instead of full heaptypes where possible in the interpreter. Update the
overview accordingly.

Also fix #74 by introducing a new externtype binary encoding for exact
function imports rather than using an s33 in the existing function
import variant.
@tlively tlively requested a review from rossberg October 31, 2025 14:27
@tlively
Copy link
Member Author

tlively commented Oct 31, 2025

Happy to update the binary encoding here pending the result of the discussion in #74.

| MemoryX x -> ExternMemoryT (memory c x)
| TableX x -> ExternTableT (table c x)
| FuncX x -> ExternFuncT (func c x)
| FuncX x -> ExternFuncT (Inexact, Def (snd (func c x)))
Copy link
Member

Choose a reason for hiding this comment

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

See comment below.

0x05 x:typeidx => func exact x
```

Note that we do not add support for exactly exported functions.
Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit lost, what is the point of exact imports if there are no exact exports?

That said, an export wouldn't require the exact attribute, that's inherited from what's exported. (Another reason why the encoding of the attribute should be separate from the im/export kind opcode range.)

Copy link
Member Author

Choose a reason for hiding this comment

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

The static types of exports don't seem to matter too much because instantiation checks the dynamic types of imports rather than their static types. I expect this to start mattering more once we have some form of static module linking, for example in the component model.

Assuming we do want to express exact exports, though, the choice about whether to use the exact attribute on exports is not obvious. As you say, we can always just export with the precise internal type, exact or not, without the exact attribute on the export. On the other hand, requiring the exact attribute for exact exports would allow for "hiding" the exactness of an exported function by choosing not to use the exact attribute on the export. That has the potential benefit that refining the type of the function export as the module evolves would not break linking, so perhaps @lukewagner has a strong opinion that we should support this? OTOH, I'm not sure how useful this would be in practice since function subtyping is not typically used outside of GC languages.

Copy link
Member Author

Choose a reason for hiding this comment

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

The implementation in this PR is conservative in that it always exports functions with inexact types, but I'm not sure how to test this given that it doesn't make a difference for instantiation.

Copy link
Member

Choose a reason for hiding this comment

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

First of all, if we wanted to enable some form of type hiding at exports, then the right design would be to allow giving a full function type. That would allow exporting with any supertype of choice. Exactness is just one possible aspect of this.

But none of this makes any sense as long as imports are checked against the original dynamic type anyway — which is not something we are able to change. So "type hiding" for exports doesn't actually hide anything in any meaningful or useful manner. Even the seemingly hiding "static" export type, including exactness, can be "cast" back to the original type by funnelling the function through an auxiliary module that imports and reexports it with its original type.

All this makes the idea of such after-the-fact type hiding on exports entirely broken and pointless.

A correct implementation of such a feature would actually have to modify the dynamic type of the function. But that can only be done by wrapping it into a new function object. Which in turn is something a module can already do manually, by exporting a wrapper function. In either case, hiding exactness would still be broken.

The bottom line is that after-the-fact type hiding on exports, for exactness or otherwise, does not work in Wasm. If somebody needs to restrict the type of exports, then export the right thing. If we want to avoid exactness foot guns, then the only solution is to axe exact imports. I am very much in favour of that, until we understand the problem and the benefit better. It would also simplify the proposal and make it more conservative.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we're in violent agreement about the meaninglessness of the static export type :)

Not doing any kind of type hiding seems fine to me.

I will update this so that the export type is the same as the internal type. This does not require the exact bit to be set on the export, so I will continue disallowing its use there.

Does that sound good?

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good to me. I'd still vote for removing exact imports as well, as I don't think they carry their weight and there is no in-language use-case for them. But happy to discuss separately.

| 0x02 -> ExternMemoryT (memorytype s)
| 0x03 -> ExternGlobalT (globaltype s)
| 0x04 -> ExternTagT (tagtype s)
| 0x05 -> ExternFuncT (Exact, typeuse idx s)
Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think this should change to 0x40.

Copy link
Member Author

Choose a reason for hiding this comment

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

0x20 to allow for reinterpreting this as a signed LEB in the future, right?

@tlively tlively requested a review from rossberg November 4, 2025 21:33
@tlively tlively merged commit 9477928 into main Nov 5, 2025
1 check passed
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.

Current exact import s33 encoding is not backward compatible

2 participants