-
Notifications
You must be signed in to change notification settings - Fork 35
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
finally
support?
#158
Comments
In #124, I offered the suggestion of having an "unwinding" variant of branching/returning instructions that would run all intermediate |
Oh, you were suggesting adding a In short, I would expect any design with direct support for |
@RossTate I'm not sure if I understand what you are suggesting, and I also am not sure if we want to repeat the discussion between I get
Is this any different for |
Also what we should think about are:
|
Exceptional control flow can be either dynamically scoped or lexically scoped.
From #124: "One way we could enable this is to introduce an That said, you could have As for the questions, I get concerned when I see WebAssembly making such policy decisions. It suggests to me that we haven't developed the correct low-level mechanisms that would enable languages to implement their own policy within WebAssembly. |
As the OP says, the primary motivation is module size. When the J2CL team thought about how to support Java's We do not have specific numbers on the module size impact yet, as it isn't easy to get them (it requires implementing the duplication approach first). |
#124 proposes an alternative system that unbundles the search for dynamic control flow destinations from stack unwinding. Such a system is inarguably lower level (i.e. more expressive) than the currently proposed system, but that extra expressiveness comes at a great complexity cost in terms of the number of new primitives introduced. Although the presentation and exposition of Common Lisp control flow primitives was illuminating, I do not believe we currently have a large and diverse enough set of language implementors to validate a design for more general low-level control flow and unwinding primitives. All that is to say that I believe we should keep the current discussion focused on the current proposal scope of single phase exceptional control flow.
I agree that it would be great to develop such mechanisms, but they should be developed in a separate proposal and in partnership with language implementors who would use them With that being said, I would like to say that I am tentatively in favor of adding some form of
I would propose adding Another question to resolve is whether control flow transfers via I would be less inclined to add |
I think splitting this is a good idea if it makes thing simpler. But I'm not sure how we should transform this code in Java: try {
foo();
} catch (Exception e) {
throw e; // rethrow or throw, doesn't matter
} finally {
} The problem is the
I agree |
The finally clause is not the one doing the throwing in this example. |
Isn't this what the WebAssembly-level finally would do as well? |
I guess so, but should we make spec decision finer-grained than that? For example, if we hold off on any throws from a |
One possibility would be to require |
We also have to figure out what should happen to the in-flight exception or control flow payload if there is a branch (or return) out of a |
finally blocks should definitely have type []->[]. Anything else is madness. |
FYI: It looks when there is a preceding control flow action (branch / return / exception) from Also among those five languages, only Python allowed |
Thanks for doing the survey! Note, though, that Python does not allow
That is, if a |
Not sure what you mean. Our Also other than But I'm also not sure if this is worth spending time debating about even if I don't understand your comment, given that this is a side topic. |
I'll try to sum up the requirements or suggestions so far. I might be missing something or there can be cases I haven't considered; please let me know if so. I'd like to ask especially to VM folks, since I'm not very familiar with VM internals and feasibility issues there. Here "control flow transfer" means one of branches, returns, or exceptions.
I think |
I haven't thought through all of the details here yet, but one issue I wanted to raise is that with In particular, we won't know until seeing the Maybe the binary format for |
Just to understand this right, are you saying that a branch out of a try body would still run the finally block? I'm not convinced that's what should happen on the assembly level. For the machine, a jump is a jump. When compiling a language with a richer goto semantics it would be up to the compiler to translate that accordingly when it crosses a finally block. The ability to bypass a finally block is something a code generator might even want under certain circumstances. I think finally semantics should be as regular and simple as possible. The equivalence I'd expect is something like
That is, it merely ensures that B is executed on both completion and exceptional exit from A, but does not affect the meaning of explicit jumps. That is sufficient to avoid the exponential code duplication of B that you'd otherwise need, and which is the problem this feature should focus on fixing. Gotos do not create that problem, AFAICS: at worst, you'll need a local and an extra br_table after the finally.
Same question here, I am not sure if I am reading this correctly. It sounds like a br out of a finally block should be intercepted and still rethrow the exception? That would be really odd, and doesn't even match what source languages do.
Agreed, we should keep these instructions separate. |
This seems reasonable to me.
I had the opposite reading here: control flow out of the
I think I had the opposite reading here, too. @aheejin, you are proposing that |
The complexities of all these considerations is beginning to convince me that there 'should be another way' to reduce code duplication. |
Yeah, what @tlively said was what I meant. I tried to match the semantics to the source languages.
Yes, your reading is what I meant. What I meant was try-catch*-finally shouldn't be too confusing. But I don't have a strong preference here. |
@rluble @gkdn Would the semantics @rossberg suggested in #158 (comment) work for you? It'd be good to hear from you J2CL people given that currently this features is requested from J2CL. That semantics still require some code transformation, but not as many duplicated copies. |
In WebAssembly, each label can have a different type. This makes the suggestion in #158 (comment) not well aligned with WebAssembly's existing local control flow as it expects people to use That said, I'm receptive to @rossberg's "a jump is a jump" argument, which is one reason I had suggested the
I agree this is a problem. I think the leading instruction should be more informative. |
Rather than change wasm, why not compile surface-level |
@RossTate I think that's a way we can handle this. I agree |
#190 provides a new option. The JS-to-wasm conversion enables J2CL to ensure the only non-trapping exception is With that in mind, J2CL can support
|
@RossTate This is what we used to do with |
Like @aheejin, I'm having a strong déjà vu. |
For the next poor soul coming here in search of an answer, here are some good news. The new semantics with For the full deal of details, you can find our implementation and the big accompanying documentation comment at: tl;dr The scheme is a variant of #158 (comment) that uses a To give you an idea, the block $innerDone (result i32)
block $innerCatch (result exnref)
block $innerCross
try_table (catch_all_ref $innerCatch)
; [...] body of the try
local.set $innerTryResult
end ; try_table
; set destinationTagInner := 0 to mean fall-through
i32.const 0
local.set $destinationTagInner
end ; block $innerCross
; no exception thrown
ref.null exn
end ; block $innerCatch
; now we have the common code with the finally
; [...] body of the finally
; maybe re-throw
block $innerExnIsNull (param exnref)
br_on_null $innerExnIsNull
throw_ref
end
; re-dispatch after the outer finally based on $destinationTagInner
; first transfer our destination tag to the outer try's destination tag
local.get $destinationTagInner
local.set $destinationTagOuter
; now use a br_table to jump to the appropriate destination
; if 0, fall-through
; if 1, go the outer try's cross label because it is still on the way to alpha
; if 2, go to beta's cross label
; default to fall-through (never used but br_table needs a default)
br_table $innerDone $outerCross $betaCross $innerDone
end ; block $innerDone For more context, please see the full code comment that I linked above. |
We have an old issue #11 for this, but it is 4 years old so I think it is worth opening a new one.
Recently Google's J2CL team is using wasm and they asked for
finally
to be added in the spec, citing the complexity of code duplicating transformation in case there are many branches/returns from thetry
part and also code size increase, which I think is a valid concern. Withoutfinally
, in case we have four ways of exiting atry
block, such as 1. normal fallthrough 2. exception 3. return 4. branch, we might end up duplicating the same code four times.finally
support was discussed briefly years ago in #11, but people thought it could be transformed into try-catch with code duplication so it was not strictly necessary, but I'd like to ask you on your opinions now again.@jakobkummerow suggested implementing it in the engine may not be trivial either, but it can be still worth having it in the spec because of the code size.
I cc'ed some people who might be interested or working on EH now + and also the J2CL team, but please feel free to comment even if I missed you. cc @tlively @dschuff @jakobkummerow @ecmziegler @thibaudmichaud @manoskouk @ioannad @takikawa @rluble @gkdn @rossberg @RossTate
The text was updated successfully, but these errors were encountered: