-
Notifications
You must be signed in to change notification settings - Fork 694
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
call_indirect versus abstraction #1343
Comments
Thanks for this detailed writeup, Ross! I have small question: in the "
Is this also subject to the caveat from the the previous section that the problem is only present once we add variant subtyping to the typed function references? |
It is not. All the issues in the subtyping section are independent of type imports, and all the issues in the type imports section are independent of subtyping. With respect to the particular issue you're asking about, consider that a value of type |
Similar question for the exporting for I can imagine some sort of subtyping relation used for this, e.g. if |
With type exports, a module specifies a signature, like Note, though, that the signature itself says nothing about Hopefully that clarifies the issue a bit! |
Thanks, that clarifies things. I'll have a question about the subtyping section (sorry to jump around): I'm following the scenario where we want IB executing And you added that (due to the typed function references) we also need
Should this be compatibility between "expected signature and the import signature"? Since we are assuming we have made call_indirect compare import signature with expected signature. If the compatibility is checked between expected and import, then later, |
Techniques 1 and 2 are given as two orthogonal ways to get that indirect call to work. Unfortunately, technique 1 is incompatible with typed function references, and technique 2 is likely too expensive. So neither of these seem likely to work out. Thus the rest of the section considers what happens if we use neither of these and just stick with simple equality-comparison between the expected and defined signature. Sorry for the confusion; not having a planned semantics means I have to discuss three potential semantics. |
Be careful not to jump to too many conclusions. ;) My assumption is that Now, these seemingly contradictive requirements can actually be reconciled fairly easily, as long as we make sure that the types usable with One established way of enforcing that is to introduce the notion of exact types into the type system. An exact type has no subtypes, only supertypes, and we'd have With that, we can require the target type at A module could also require exact types on function imports, if it wanted to make sure that it can only be instantiated with functions that succeed an intended runtime check. That's all that's needed to ensure that the current implementation technique of a simple pointer comparison on canonicalised function types remains valid. It's independent of what other subtyping there is, or how fancy we make function subtyping. (FWIW, I discussed this with Luke a while ago, and planned to create a PR, but it was blocked on pending changes to the subtyping story, and which proposal that now moves to.) (One downside is that refining a function definition to a subtype is no longer a backwards-compatible change in general, at least not if its exact type has been used anywhere. But that drawback is unavoidable under our constraints, regardless of how exactly we enforce them.) A couple of asides:
AFAICS, it's not feasible to disallow ref.func on functions that involve reference types. That would severely cripple many use cases, i.e., everything involving first-class functions operating on
Can you elaborate? I don't see the connection. |
I'm not sure what conclusion you're referring to. My stated conclusion is that there are a number of issues with
Exact types are hardly an established solution. If anything, exact types have established problems that its proponents are still working to address. Interestingly, here is a thread where the TypeScript team originally saw how exact types of the form you're proposing could solve some problems, but then they eventually [realized)(https://github.com/microsoft/TypeScript/issues/12936#issuecomment-284590083) that exact types introduced more problems than they solved. (Note for context: that discussion was prompted by Flow's exact object types, which are not actually a form of exact type (in the theoretical sense) but instead simply disallow the object-analog of prefix subtyping.) I could imagine us replaying that thread here. As an example of how these sorts of problems could play out for WebAssembly, suppose we had not deferred subtyping. The type of Now maybe you have some different version of exact types in mind, but it would take a while to check that this different version somehow addresses the many open problems with exact types. So my point here is not to throw out this solution, but to recognize that it's not obvious that this is the solution and to not make decisions with that expectation.
You're referencing a long sentence. Which part of it would you like me to elaborate on? One guess is that you might be missing the overall issue with
Yeah, so this is something I was hoping to get more information on. My understanding is that the primary use case of Y'all know the code bases targeting this feature far better than I, so if y'all know of some real programs needing this functionality now, it'd be very helpful to provide a few examples of the usage patterns in need here. Besides being useful for figuring out if we need to support this functionality right now, if the functionality is needed now then these examples would be helpful in informing how best to quickly provide it while addressing the issues above. |
The parentheses are key here. I'm not sure what exactly they have in mind in that thread, but it doesn't seem to be the same thing. Otherwise, statements like "it's assumed that a type For low-level type systems, weren't exact types a crucial ingredient even in some of your own papers?
No disagreement here. Not having subtypes is the purpose of exact types.
Right, the combination
Are you sure that you aren't confusing levels now? A function type Maybe you are mixing up
You are somehow assuming that you'll be able to export a type without its definition as a means to define an abstract data type. Clearly, that approach doesn't work in the presence of dynamic type casts (call_indirect or otherwise). That's why I keep saying that we'll need newtype-style type abstraction, not ML-style type abstraction.
Yes, but that's not the sole use case of |
Okay, so you seem to be agreeing that exact types do nothing to address the issue with As I said, every solution has tradeoffs. You seem to be presuming that your solution has only the tradeoffs you yourself have identified, and you seem to be presuming that the CG would prefer your solution over others. I, too, have a potential solution to this problem. It guarantees constant-time checks, is based on a technology already used in virtual machines, addresses all the problems here (I believe), doesn't require adding any new types, and actually adds additional functionality to WebAssembly with known applications. However, I am not presuming that it works as I expect and that I have not overlooked some shortcoming because you and others have not had a chance to look it over. I am also not presuming that the CG would prefer its tradeoffs over those of alternative options. Instead, I am trying to figure out what we can do to give us time to analyze the options so that the CG, rather than just me, can be the one who makes an informed decision on this cross-cutting topic.
The key word in your sentence is will. I am fully aware that there are applications of |
This seems like a fundamental issue to me. Is enabling the confidentiality of the definitions of exported types a goal of the type imports proposal? I gather from this thread that @RossTate thinks it should be a goal and @rossberg thinks it is not currently a goal. Lets discuss and agree on this question before discussing solutions so that we can all work from the same set of assumptions. |
Yes, if by that you mean the question of how to add a feature for defining abstract data types. There are a number of ways how type abstraction can work consistently, but such a feature is further down the road.
The
It is a goal, but an abstract data type mechanism is a separate feature. And such a mechanism must be designed such that it does not affect the design of imports. If it did, then we would be doing it very wrong -- abstraction has to be ensured at the definition site, not the use site. Fortunately, though, this is not rocket science, and the design space is fairly well-expored. |
Thanks, @rossberg, that makes sense. Adding abstraction primitives in a follow-up proposal after type imports and exports sounds fine to me, but it would be great if we could write down the details of how we plan to do that somewhere soon. The design of type imports and exports constrains and informs the design of abstract type imports and exports, so it is important that we have a good idea of how the abstraction will work down the road before we finalize the initial design. |
In addition to detailing that plan, since this issue with |
@tlively, yes, agreed. Plus various other things I meant to write up for a while. Will do once I've worked through all the fallout from #69. ;) @RossTate, because that would make abstract data types incompatible with casts. Just because I want to prevent others from seeing through an abstract type, I don't necessarily want to prevent them (or myself) from casting to an abstract type. Creating such a false dichotomy would break central use cases of casts. For example, of course I want to be able to pass a value of abstract type to a polymorphic function. |
@rossberg Can you clarify what this central use case you have in mind is? My best guess at interpreting your example is trivially solvable, but maybe you mean something else. |
@RossTate, consider polymorphic functions. Short of Wasm-generics, when compiling them using up/down casts from anyref, then it should be possible to use them with values of abstract type like any other, without extra wrapping into another objects. You generally want to be able to treat values of abstract type like any other. |
Okay, let's consider polymorphic functions, and let's suppose the imported type is
In other words, because modules rely on norms about how surface-level values are represented in order to implement things like polymorphic functions, and abstract imported types like Handle do not satisfy these norms, wrapping of values is inevitable. This is the same reason one of the original applications for On the other hand, you've demonstrated that castable Also, now that (I think) I better understand how you intend to use exact types, I realize that your intent addresses neither of the two major problems I called attention to with
So this is not a trivial problem to solve. That is why, given the time constraints, I would prefer to focus on evaluating how to give us time solve it properly. I don't think it should be necessary to first have a discussion about whether |
Static type abstraction is insufficient in a language with dynamic type casts. Because static abstraction relies on parametricity, and casts break that. There is nothing new about that, papers have been written about it. Other abstraction mechanisms are needed in such a context. Trying to work around that by restricting the use of abstract types defeats their purpose. Consider the WASI use case. It should not matter whether a WASI module and any type it exports is implemented by the host or in Wasm. If you arbitrarily restrict user-defined abstract types, then a Wasm implementation would no longer be interchangeable with a host implementation in general.
Huh? It is part of the subtyping rules, so does by definition.
I didn't say it did. I said that this one is not a problem with call_indirect itself, but a question of picking a suitable type abstraction mechanism for a language with casts. As an aside, there is no compelling reason why compiling OCaml (or any similar language) should require the introduction of variant types. Even if that could be slightly faster in theory (which I doubt would be the case in current-gen engines, more likely the contrary), variant types are a significant complication that should not be necessary for the MVP. I don't quite share your appetite for premature complexity. ;) Re equality on functions: there are languages, such as Haskell or SML, that do not support that, so might benefit directly from func refs. OCaml throws for structural equality and explicitly has implementation-defined behaviour for physical one. It is left open whether that allows always returning false or throwing for functions, but either might well be sufficient in practice, and worth exploring before committing to costly extra wrapping. [As a meta comment, I would really appreciate if you toned down your lecturing and perhaps considered the idea that this is a world where, maybe, the set of competent people is not singleton and that traces of brains have occasionally been applied before.] |
Heard.
My advice here is based on consultation with multiple experts.
These experts I have consulted with include authors of some of said papers. Now, as an attempt to check that I have correctly synthesized their advice, I just e-mailed another author of some of said papers, one whom I have not discussed this topic before. Here is what I asked:
Here was their response:
This is in line with my advice. Now, of course, this is a simplification of the problem at hand, but we have made an effort to investigate the problem more specifically to WebAssembly, and so far our exploration has suggested that this expectation continues even to hold even at the scale of WebAssembly except for Note that the theorems you are referring to apply to languages in which all values are castable. This observation is where we got the idea to restrict castability.
I do not understand the claims you are making. We have considered the WASI use case. By we, I am including multiple experts in security and even specifically capability-based security. As a meta comment, I would really appreciate not needing to appeal to authority or to the CG to have my suggestions heard. I suggested that restricting casts would enable ensuring static parametricity even in the presence of casts. You immediately disregarded that suggestion, appealing to prior papers to justify that dismissal. Yet when I offered this same suggestion to an author of those papers, they immediately came to the same conclusion as I did and as you could have. Before that, I suggested that evaluating potential solutions would be a long process. You disregarded that suggestion, insisting that you (all on your own) had solved the problem, pulling us both into this long conversation. It is extremely difficult to make progress, and to keep from getting frustrated, when one's suggestions are repeatedly dismissed so casually. (I should clarify that I am not trying to dismiss your suggestion as a possible solution here; I am trying to demonstrate that it is not the only solution and so should be evaluated alongside various others.) |
I think having a detailed design nailed down and scrutinized that addresses the concerns raised in this issue is important and timely: I don't actually think abstract types should be considered a farther-away feature; WASI needs them now-ish. I also have hopes that That being said, I don't see the hazard with allowing If there isn't a hazard, perhaps we could dial down this intense discussion to a less-intense discussion in the Type Imports proposal (where I still think we should include proper abstract type support)? |
Sure. I think it's a good idea to examine if there's a hazard or not. Regarding WASI, the design is still very much in flux, but one option that still seems viable is to use something like Currently, So one of my questions, that now I'm noticing was not stated clearly in my original post, is do people want |
Thanks for explicitly raising this question. FWIW, I have never understood |
Hmm, let me see if I can clarify. I suspect you're already on board with a bunch of what follows, but it's easier for me to just start from scratch. From the perspective of a wasm module, However, the environment of the module does not have to be the host and Hopefully that was concrete enough to paint the right picture, while not too concrete to get lost in details. There are obviously a number of patterns like this. So I guess another way to phrase the question is, do we want |
The only part that sounds questionable to me is "what E could do is instantiate M with "MonitoredRef" as M's |
I don't know of any such plans either; I just also don't know if anyone had considered the option. That is, should In my original post, I indicated that either way is manageable. The tradeoff of going for the "primitive" interpretation is that |
Ah hah, yes, this explains the difference in understanding: I agree with @tlively that In the absence of downcasts, this fact makes wasm near-useless for implementing/virtualizing WASI APIs which is why the plan for WASI has been to transition from |
When we add type imports, we can treat modules without type imports but with But before going back and forth on that, I think it would help to gauge if we're all on the same page about something. Let me know if you agree or disagree with the following statement and why: "Once type imports are available, modules have no reason to use |
I agree with this statement in the abstract. In practice I think externref will remain common in web contexts for referring to external JS objects because it requires no additional configuration at instantiation time. But that’s just a prediction and I wouldn’t mind if I turn out to be wrong and everyone switches to using type imports after all. The value of externref is that we can have it sooner than we can have richer mechanisms like type imports. I would rather keep externref simple and see it fall out of use than awkwardly shoehorn it into being something more powerful later on when there are more elegant alternatives. |
Right, the idea is that externref is the "primitive" type of foreign pointers. To abstract over the implementation details of a reference type, you'll need something else: something like anyref or a type import. @lukewagner, I'd be on board with widening the scope of the type imports proposal, if that's preferable. But the trade-off is that the proposal would take longer. I was under the impression that type imports could be useful without in-language abstract data types, and hence desirable sooner rather than later. It would mean that you could already express and use WASI interfaces, but not yet implement them in Wasm. But either way is fine with me. |
Excellent. Then I assume you have noticed that yours truly is the author of a couple of these papers himself, in case you're looking for more authority. :)
Sigh. I'd give the same reply to that specific question. But this question embodies several specific assumptions, e.g. about the nature of casts, and about a rather unusual distinction between bounded and unbounded quantification that rarely exists in any programming language. And I'd suppose that's for a reason. When I said "static type abstraction is insufficient" then I didn't mean that it isn't technically possible (of course it is), but that it isn't practically adequate. In practice, you don't want a bifurcation between type abstraction and subtyping/castability (or between parametric and non-parametric types), because that would artificially break composition based on casts.
If you receive a value of abstract type then you might still want to forget its exact type, e.g. to put it into some kind of union, and later recover it through a downcast. You might want to do that for the same reason you may want that for any other reference type. Type abstraction shouldn't get in the way of certain usage patterns that are valid with regular types of the same sort. Your answer seems to be: so what, wrap everything into auxiliary types at respective use sites, e.g. into variants. But that could imply substantial wrapping/unwrapping overhead, it requires more complex type system features, and it is more complicated to use. I think this is what several of our disagreements come down to: whether the MVP should support unions of reference types, or whether it should require the introduction of and encoding with explicit variant types. For better or worse, unions are a natural match for the heap interface of typical engines, and they are easy and cheap to support today. Variants not so much, they are a much more researchy approach that likely would induce extra overhead and less predictable performance, at least in existing engines. And I'm saying that as a type systems person that much prefers variants over unions in other circumstances, such as user-facing languages. ;)
May I kindly suggest that conversations about various proposals might work better if started by asking respective champions about things that aren't clear, e.g. specific rationales or future plans (which aren't always obvious or written up yet), before assuming the absence of an answer and making broad assertions and suggestions based on these assumptions? |
Yes, which makes it extremely problematic when you suggest that there are papers claiming my suggestion does not work, even though you know that my suggestion specifically addresses the conditions those claims were made under.
This is an opinion, not a fact (making it something perfectly reasonable for us to disagree on). I would say that there are no language-agnostic industry typed assembly languages for multi-language systems, and so it is impossible to make claims about practice. This is something that deserves a thorough (separate) discussion. For that discussion, it would help for you to first provide some detailed case studies so that the CG can compare the tradeoffs.
WebAssembly/proposal-type-imports#4, WebAssembly/proposal-type-imports#6, and WebAssembly/proposal-type-imports#7 each essentially asked for more specifics on this plan. The last of these punts the issue to GC, but WebAssembly/gc#86 points out that the current GC proposal does not in fact support dynamic abstraction mechanisms. On the meta level, we were asked to put this discussion aside and focus on the topic at hand. I found @tlively's response to my question very helpful. I am actually quite interested in getting specifically your thoughts on that question. |
Nope. I thought that comment might have implied agreement with his response, but I wanted to confirm first. Thanks! @lukewagner, what are your thoughts? |
I agree with above that I'd like to take @rossberg up on the offer to expand the scope of the Type Imports proposal so that it covers the ability for wasm to implement abstract types. Once we can nail that down, I think it'll unblock further discussion around function references and subtyping. |
Awesome. Then we're all on the same page (and I, too, think @tlively provides a nice summary of the tradeoffs involved and the rationale for a decision). So And I also would like to take up the offer to expand the scope of Type Imports. Many of the major applications of type imports need abstraction, so it seems natural to me for abstraction to be part of that proposal. In the meanwhile, although we've addressed the pressing question about Thanks! |
call_indirect
has been a very useful feature for WebAssembly. However, the efficiency and good behavior of the instruction has implicitly relied on the simplicity of wasm's type system. In particular, every wasm value has exactly one (static) type it belongs to. This property conveniently avoids a number of known problems with untyped function calls in typed languages. But now that wasm is extending beyond numeric types, it has gotten to the point where we need to understand these problems and keep them in mind.call_indirect
fundamentally works by comparing the caller's expected signature against the callee's defined signature. With just numeric types, WebAssembly had the property that these signatures were equal if and only if a direct call to the function referenced by the correspondingfuncref
would have type-checked. But there are two reasons that will soon not be true:call_indirect
can be used to access the function with its private defined signature rather than just its weaker public signature (an issue that was just discovered and so has not yet been discussed).call_indirect
to work properly on exported functions whose exported signatures reference that exported type. However, if a malicious module knows what the definition of that exported type is, they can usecall_indirect
to convert back and forth between the exported type and its intended-to-be-secret definition becausecall_indirect
only compares signatures at run time, when the two types are indeed the same. Thus a malicious module can usecall_indirect
to access secrets meant to be abstracted by the exported type, and can usecall_indirect
to forge values of the exported type that may violate security-critical invariants not captured in the definition of the type itself.In both of the above situations,
call_indirect
can be used to bypass the abstraction of a module's exported signature. As I mentioned, so far this hasn't been a concern because wasm only had numeric types. And originally I thought that, by deferring subtyping, all concerns regardingcall_indirect
had also effectively been deferred. But what I recently realized is that, by removing subtyping, the "new" type (namedexternref
in WebAssembly/reference-types#87) is effectively a stand-in for an abstract type import. If that's what people would like it to actually be, then unfortunately we need to take into consideration the above interaction betweencall_indirect
and type imports.Now there are many potential ways to address the above issues with
call_indirect
, but each has its tradeoffs, and it is simply much too large a design space to be able to come to a decision on quickly. So I am not suggesting that we solve this problem here and now. Rather, the decision to be made at the moment is whether to buy time to solve the problem properly with respect toexternref
. In particular, if we for now restrictcall_indirect
andfunc.ref
to only type-check when the associated signature is entirely numeric, then we serve all the core-wasm use cases of indirect calls and at the same time leave room for all the potential solutions to the above issues. However, I do not know if this restriction is practical, both in terms of implementation effort and in terms of whether it obstructs the applications ofexternref
that people are waiting for. The alternative is to leavecall_indirect
andfunc.ref
as is. It is just possible that this means that, depending on the solution we arrive at,externref
might not be instantiable like a true type import would be, and/or thatexternref
might (ironically) not be able to have any supertypes (e.g. might not be able to be a subtype ofanyref
if we do eventually decide to addanyref
).I, speaking for just myself, consider both options manageable. While I do have a preference, I am not strongly pushing the decision to go one way or the other, and I believe y'all have better access to the information necessary to come to a well-informed decision. I just wanted y'all to know that there is a decision to be made, and at the same time I wanted to establish awareness of the overarching issue with
call_indirect
. If you would like a more thorough explanation of that issue than what the summary above provides, please read the following.call_indirect
versus Abstraction, in DetailI'll use the notation
call_indirect[ti*->to*](func, args)
, where[ti*] -> [to*]
is the expected signature of the function,func
is simply a funcref (rather that a funcref table and an index), andargs
are theto*
values to pass to the function. Similarly, I'll usecall($foo, args)
for a direct call of the function with index$foo
passing argumentsargs
.Now suppose
$foo
is the index of a function with declared input typesti*
and output typesto*
. You might expect thatcall_indirect[ti*->to*](ref.func($foo), args)
is equivalent tocall($foo, args)
. Indeed, that is the case right now. But it's not clear that we can maintain that behavior.call_indirect
and SubtypingOne example potential problem came up in the discussion of subtyping. Suppose the following:
tsub
is a subtype oftsuper
$fsub
that was defined with type[] -> [tsub]
$fsuper
with type[] -> [tsuper]
$fsub
as$fsuper
(which is sound to do—even if it's not possible now, this issue is about potential upcoming problems)Now consider what should happen if IB executes
call_indirect[ -> tsuper](ref.func($fsuper))
. Here are the two outcomes that seem most plausible:If we were to choose outcome 1, realize that we would likely need to employ one of two techniques to make this possible:
call_indirect
compare with the import signature rather than the definition signature.If you prefer technique 1, realize that it won't work once we add Typed Function References (with variant subtyping). That is,
func.ref($fsub)
will be aref ([] -> [tsub])
and also aref ([] -> [tsuper])
, and yet technique 1 will not be sufficient to keepcall_indirect[ -> super](ref.func($fsub))
from trapping. This means outcome 1 likely requires technique 2, which has concerning performance implications.So let's consider outcome 2 a bit more. The implementation technique here is to check if the expected signature of the
call_indirect
in IB is equal to the signature of the definition of$fsub
in IA. At first the major downside of this technique might seem to be that it traps on a number of calls that are safe to execute. However, another downside is that it potentially introduces a security leak for IA.To see how, let's switch up our example a bit and suppose that, although instance IA internally defines
$fsub
to have type[] -> [tsub]
, instance IA only exports it with type[] -> [tsuper]
. Using the technique for outcome 2, instance IB can (maliciously) executecall_indirect[ -> tsub]($fsuper)
and the call will succeed. That is, IB can usecall_indirect
to circumvent the narrowing IA did to its function's signature. At best, that means IB is dependent on an aspect of IA that is not guaranteed by IA's signature. At worst, IB can use this to access internal state that IA might have intentionally been concealing.call_indirect
and Type ImportsNow let's put subtyping aside and consider type imports. For convenience, I am going to talk about type imports, rather than just reference-type imports, but that detail is inconsequential. For the running example here, suppose the following:
module instance IC defines a type
capability
and exports the type but not its definition as$handle
module instance IC exports a function
$do_stuff
that was defined with type[capability] -> []
but exported with type[$handle] -> []
module MD imports a type
$extern
and a function$run
with type[$extern] -> []
module instance ID is module MD instantiated with IA's exported
$handle
as$extern
and with IA's exported$do_stuff
as$run
What this example sets up is two modules where one module does stuff with the other module's values without knowing or being allowed to know what those values are. For example, this pattern is the planned basis for interacting with WASI.
Now let's suppose instance ID has managed to get a value
e
of type$extern
and executescall_indirect[$extern -> ](ref.func($run), e)
. Here are the two outcomes that seem most plausible:Outcome 2 makes
call_indirect
pretty much useless with imported types. So for outcome 1, realize that the input type$extern
is not the defined input type of$do_stuff
(which instead iscapability
), so we would likely need to use one of two techniques to bridge this gap:call_indirect
compare with the import signature rather than the definition signature.$extern
in instance ID representscapability
.If you prefer technique 1, realize that it once again won't work once we add Typed Function References. (The fundamental reason is the same as with subtyping, but it'd take even more text to illustrate the analog here.)
That leaves us with technique 2. Unfortunately, once again this presents a potential security issue. To see why, suppose ID is malicious and wants to get at the contents of
$handle
that IC had kept secret. Suppose further that ID has a good guess as to what$handle
really represents, namelycapability
. ID can define the identity function$id_capability
of type[capability] -> [capability]
. Given a valuee
of type$extern
, ID can then executecall_indirect[$extern -> capability](ref.func($id_capability), e)
. Using technique 2, this indirect call will succeed because$extern
representscapability
at run time, and ID will get the rawcapability
thate
represents back. Similarly, given a valuec
of typecapability
, ID can executecall_indirect[capability -> $extern](ref.func($id_capability), c)
to forgec
into an$extern
.Conclusion
Hopefully I've made it clear that
call_indirect
has a number of significant upcoming performance, semantic, and/or security/abstraction issues—issues that WebAssembly has been fortunate to have avoided so far. Unfortunately, due tocall_indirect
being part of core WebAssembly, these issues crosscut a number of proposals in progress. At the moment, I think it would be best to focus on the most pressing such proposal, Reference Types, where we need to decide whether or not to restrictcall_indirect
andfunc.ref
to only numeric types for now—a restriction we might be able to relax depending on how we eventually end up solving the overarching issues withcall_indirect
.(Sorry for the long post. I tried my best to explain complex interactions of cross-module compile-time-typing-meets-run-time-typing features and demonstrate the importance of those interactions as concisely as possible.)
The text was updated successfully, but these errors were encountered: