A new version of Kernel was added to project The Inbox:
http://source.squeak.org/inbox/Kernel-jar.1399.mcz ==================== Summary ==================== Name: Kernel-jar.1399 Author: jar Time: 2 May 2021, 3:12:37.830089 pm UUID: d288b516-c6f1-ce43-9061-2220422b8ab4 Ancestors: Kernel-jar.1398 Fix inconsistent implementation of an explicit and an implicit exception return. I'd like to return to my original proposal in http://forum.world.st/The-Inbox-Kernel-nice-1391-mcz-tp5129040p5129084.html. The problem then was a bug in #outer that confused me. The bug has been fixed and the original proposal in my opinion makes sense again - to unify how the two kinds of exception return are implemented. Theoretically it's possible to change the #return definition in the future and then the two returns would diverge. =============== Diff against Kernel-jar.1398 =============== Item was changed: ----- Method: Context>>handleSignal: (in category 'private-exceptions') ----- handleSignal: exception "Sent to handler (on:do:) contexts only. Execute the handler action block" | val | <primitive: 199> "just a marker, fail and execute the following" exception privHandlerContext: self contextTag. self deactivateHandler. "Prevent re-entering the action block, unless it is explicitely rearmed" val := [self fireHandlerActionForSignal: exception] ensure: [self reactivateHandler]. + exception return: val "return from exception handlerContext if not otherwise directed in handle block"! - self return: val "return from self if not otherwise directed in handle block"! |
Le dim. 2 mai 2021 à 15:12, <[hidden email]> a écrit :
> > A new version of Kernel was added to project The Inbox: > http://source.squeak.org/inbox/Kernel-jar.1399.mcz > > ==================== Summary ==================== > > Name: Kernel-jar.1399 > Author: jar > Time: 2 May 2021, 3:12:37.830089 pm > UUID: d288b516-c6f1-ce43-9061-2220422b8ab4 > Ancestors: Kernel-jar.1398 > > Fix inconsistent implementation of an explicit and an implicit exception return. > > I'd like to return to my original proposal in http://forum.world.st/The-Inbox-Kernel-nice-1391-mcz-tp5129040p5129084.html. The problem then was a bug in #outer that confused me. The bug has been fixed and the original proposal in my opinion makes sense again - to unify how the two kinds of exception return are implemented. Theoretically it's possible to change the #return definition in the future and then the two returns would diverge. > Maybe it has a virtue of making the return more explicit. Currently, behavior differs only if someone refines OwnException>>return: OwnException>>return: anObject self logReturn: anObject. ^super return: anObject I wonder what would be the expectations of someone using implicit return: howMany := [self countTheThings] on: OwnException do: [:exc | -1 "error condition"] Shall above construction logReturn: or not? Currently it doesn't. You are proposing that it does... I have no strong opinion. Votes? > =============== Diff against Kernel-jar.1398 =============== > > Item was changed: > ----- Method: Context>>handleSignal: (in category 'private-exceptions') ----- > handleSignal: exception > "Sent to handler (on:do:) contexts only. > Execute the handler action block" > > | val | > <primitive: 199> "just a marker, fail and execute the following" > exception privHandlerContext: self contextTag. > self deactivateHandler. "Prevent re-entering the action block, unless it is explicitely rearmed" > val := [self fireHandlerActionForSignal: exception] ensure: [self reactivateHandler]. > + exception return: val "return from exception handlerContext if not otherwise directed in handle block"! > - self return: val "return from self if not otherwise directed in handle block"! > > |
Hi Nicolas,
Nicolas Cellier wrote > Maybe it has a virtue of making the return more explicit. > Currently, behavior differs only if someone refines OwnException>>return: > OwnException>>return: anObject > self logReturn: anObject. > ^super return: anObject > I wonder what would be the expectations of someone using implicit return: > howMany := [self countTheThings] on: OwnException do: [:exc | -1 > "error condition"] > Shall above construction logReturn: or not? > Currently it doesn't. > You are proposing that it does... > I have no strong opinion. > Votes? Thanks; I don't have a strong opinion either, I just incline towards unifying; it happened to me a couple of times during debugging the two returns behaved differently which irritated me :) Your example even shows a real use case... it didn't occur to me. ANSI doesn't provide a guideline either: Implicit: If the evaluation of the exception action returns normally (as if it had returned from the #value: message), the value returned from the exception action is returned as the value of the #on:do: message that created the handler. Explicit: return: returnValue The returnValue is returned as the value of the protected block of the active exception handler. Another example: Cuis uses a modified Exception>>#return definition which means the modification applies only to the explicit behavior (the difference is insignificant but it's there). Thanks again, ----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
^[^ Jaromir
|
Actually, I guess the question can be reduced to:
Is /[] on: Exception do: []/ only a shortcut for /[] on: Exception do: [:ex | ex return] / or are they two distinct structures? If the answer is yes, we might even write: handleSignal: exception "Sent to handler (on:do:) contexts only. Execute the handler action block" <primitive: 199> "just a marker, fail and execute the following" exception privHandlerContext: self contextTag. "set exception's handlerContext" self deactivateHandler. "Prevent re-entering the action block, unless it is explicitely rearmed" [exception return: (self fireHandlerActionForSignal: exception)] ensure: [self reactivateHandler] "return from exception's handlerContext if not otherwise directed in the handler action block" ----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
^[^ Jaromir
|
Let's look into the ANSI standard, because there is not much more that
we have as a guideline: Message return "Nil is return[ed] as the value of the protected block of the active exception handler. Before returning, the exception environment and the evaluation context are restored to the same states that were in effect when the active handler was created using #on:do:. Restoring the evaluation context may result in the execution of #ensure: or #ifCurtailed: termination blocks." Message signal "If the evaluation of the exception action returns normally (as if it had returned from the #value: message), the handler environment is restored and the value returned from the exception action is returned as the value of the #on:do: message that created the handler. Before returning, any active #ensure: or #ifCurtailed: termination blocks created during evaluation of the receiver of the #on:do: message are evaluated." Since an empty block evaluates to nil, it seems that the intention is that both on: Error do: [] and on: Error do: [:e | e return] have the same effect. I am not sure if it is intentional that they wrote "may" with regards to the termination blocks for return, whereas they did not for signal. Specializing return: further is not covered by the standard, of course. One could argue that the return: message should not be sent to the Exception object when the exception action returns normally because that is not what it says in the specification of signal, and signal is not redefined by redefining return:. If we do send return: to the Exception, some code might come to rely on this implementation-specific behavior, just like the original ProgressNotificationException handling relied on the implementation of handler de/activation. Am Mo., 3. Mai 2021 um 14:40 Uhr schrieb Jaromir Matas <[hidden email]>: > > Actually, I guess the question can be reduced to: > > Is > > /[] on: Exception do: []/ > > only a shortcut for > > /[] on: Exception do: [:ex | ex return] / > > or are they two distinct structures? > > If the answer is yes, we might even write: > > handleSignal: exception > "Sent to handler (on:do:) contexts only. > Execute the handler action block" > > <primitive: 199> "just a marker, fail and execute the following" > exception privHandlerContext: self contextTag. "set exception's > handlerContext" > self deactivateHandler. "Prevent re-entering the action block, unless it is > explicitely rearmed" > [exception return: (self fireHandlerActionForSignal: exception)] ensure: > [self reactivateHandler] > "return from exception's handlerContext if not otherwise directed in the > handler action block" > > > > ----- > ^[^ Jaromir > -- > Sent from: http://forum.world.st/Squeak-Dev-f45488.html > |
Hi Jakob,
> One could argue that the return: message should not be sent to > the Exception object when the exception action returns normally > because that is not what it says in the specification of signal, and > signal is not redefined by redefining return:. Yes! I like this argument. I reopened this issue because it happened to me that the two types of return didn't return to the same context - it means one exception behaved as if it had two different handler contexts (it was caused by a bug in #outer implementation and it's fixed now). I think both returns should at least return to the same handler context if nothing else. For lack of a more realistic scenario look at this example: | x | x:=''. [ [1/0] on: ZeroDivide do: [:ex | ex signal. ex return]. "handler 1" x:=x,'1' ] on: ZeroDivide do: [:ex | ex resume]. "handler 2" x:=x,'2'. x ----> answers '2' correctly because the active handler is handler 2. However, if you replace ex return in handler 1 by the implicit return, it all of a sudden answers '12' ! It's because #handleSignal invoked for handler 1 returns to self (= handler 1) instead of to the exception's handlerContext (=handler 2). So actually, we don't need to send return to the exception to fix this, it would suffice to use just the exception's handler context to return to - what do you think? Here's what I mean: handleSignal: exception "Sent to handler (on:do:) contexts only. Execute the handler action block" | val | <primitive: 199> "just a marker, fail and execute the following" exception privHandlerContext: self contextTag. self deactivateHandler. "Prevent re-entering the action block, unless it is explicitely rearmed" val := [self fireHandlerActionForSignal: exception] ensure: [self reactivateHandler]. ----> exception privHandlerContext return: val "return from exception's handlerContext if not otherwise directed in the handler action block" ----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
^[^ Jaromir
|
Oh well, I am not even sure whether sending both resume and then
return to the Exception is even well-defined behavior according to the standard. Since return is specified referring to the "active exception handler", it should return from the on: do: handler 1 in your example. So I would in fact expect to get '12' in both cases. Handler 2 is no longer active after the resume. Am Mo., 3. Mai 2021 um 23:59 Uhr schrieb Jaromir Matas <[hidden email]>: > > Hi Jakob, > > > One could argue that the return: message should not be sent to > > the Exception object when the exception action returns normally > > because that is not what it says in the specification of signal, and > > signal is not redefined by redefining return:. > > Yes! I like this argument. I reopened this issue because it happened to me > that the two types of return didn't return to the same context - it means > one exception behaved as if it had two different handler contexts (it was > caused by a bug in #outer implementation and it's fixed now). I think both > returns should at least return to the same handler context if nothing else. > > For lack of a more realistic scenario look at this example: > > | x | > x:=''. > [ > [1/0] on: ZeroDivide do: [:ex | ex signal. ex return]. "handler 1" > x:=x,'1' > ] on: ZeroDivide do: [:ex | ex resume]. "handler 2" > x:=x,'2'. > x > > ----> answers '2' correctly because the active handler is handler 2. > > However, if you replace ex return in handler 1 by the implicit return, it > all of a sudden answers '12' ! It's because #handleSignal invoked for > handler 1 returns to self (= handler 1) instead of to the exception's > handlerContext (=handler 2). > > So actually, we don't need to send return to the exception to fix this, it > would suffice to use just the exception's handler context to return to - > what do you think? Here's what I mean: > > handleSignal: exception > "Sent to handler (on:do:) contexts only. > Execute the handler action block" > > | val | > <primitive: 199> "just a marker, fail and execute the following" > exception privHandlerContext: self contextTag. > self deactivateHandler. "Prevent re-entering the action block, unless it is > explicitely rearmed" > val := [self fireHandlerActionForSignal: exception] ensure: [self > reactivateHandler]. > ----> exception privHandlerContext return: val > "return from exception's handlerContext if not otherwise directed in the > handler action block" > > > > > ----- > ^[^ Jaromir > -- > Sent from: http://forum.world.st/Squeak-Dev-f45488.html > |
Hi Jakob,
Jakob Reschke wrote > Oh well, I am not even sure whether sending both resume and then > return to the Exception is even well-defined behavior according to the > standard. I'd say yes - e.g. in case of #outer. Jakob Reschke wrote > Since return is specified referring to the "active exception handler", > it should return from the on: do: handler 1 in your example. So I > would in fact expect to get '12' in both cases. Handler 2 is no longer > active after the resume. Great point! So now we have another question: what does it mean to re-signal an exception that has already been signaled? (This is NOT the same as #resignalAs) ANSI doesn't seem to be against this kind of re-signaling an existing exception. Here's what it says about #resume: " Message: resume If the current exception action was activated as the result of sending the message #outer to the receiver, return a resumption value as the value of the #outer message. If the receiver is a resumable exception a resumption value is returned as the value of the message that signaled the receiver. " So you're right we should expect '12' as a result rather than just '2'. Your interpretation seems to be consistent with the specification of #outer. I checked other implementations but each of them provides different answers :D (except Pharo). I used this modified test example: | x | x:=''. [ [1/0. x:=x,'B'] on: ZeroDivide do: [:ex | ex signal. x:=x,'A'. ex return]. "handler 1" x:=x,'1' ] on: ZeroDivide do: [:ex | ex resume]. "handler 2" x:=x,'2'. x Squeak/Pharo: answer inconsistently 'A2' or 'A12' depending on whether the return is explicit or implicit. VW: answers 'A12' - as a proper #outer (funny - their #outer answers 'A2' which is wrong) VA: answers 'A2' - totally flawed Cuis raises an exception; re-signaling is simply prohibited :) One more argument and then I'll leave it :) If you try the following example where #return is replaced by #resume, Squeak (and Pharo and VA) will fail completely raising a cannot return error: | x | x:=''. [ [1/0. x:=x,'B'] on: ZeroDivide do: [:ex | ex signal. x:=x,'A'. ex resume]. "handler 1" x:=x,'1' ] on: ZeroDivide do: [:ex | ex resume]. "handler 2" x:=x,'2'. x VW however answers 'AB12' which is also consistent with the #outer specification. I'd say it should be safe to implement this semantics, i.e. #outer semantics for re-signaling, even if it's not explicitly mentioned in ANSI. I've even found a real use of re-signaling an exception in the trunk - see DiskProxy>>comeFullyUpOnReload: So my final suggestion is to define the behavior for re-signaling as equivalent to #outer, to become consistent and avoid apparently incorrect answers in above mentioned examples (I'm aware this is 99.9% academic indeed): Exception>>signal "Ask ContextHandlers in the sender chain to handle this signal. The default is to execute and return my defaultAction." -----> signalContext ifNotNil: [^self outer]. "re-signalling an already signalled exception is equivalent to sending #outer" signalContext := thisContext contextTag. ^(thisContext nextHandlerContextForSignal: self) handleSignal: self best, ----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
^[^ Jaromir
|
Hi Jaromir,
Thank you for this survey! At least we can take away that we will not hurt portability much by changing anything here... Am Di., 4. Mai 2021 um 20:06 Uhr schrieb Jaromir Matas <[hidden email]>: > > One more argument and then I'll leave it :) If you try the following example > where #return is replaced by #resume, Squeak (and Pharo and VA) will fail > completely raising a cannot return error: > > | x | > x:=''. > [ > [1/0. x:=x,'B'] on: ZeroDivide do: [:ex | ex signal. x:=x,'A'. ex resume]. > "handler 1" > x:=x,'1' > ] on: ZeroDivide do: [:ex | ex resume]. "handler 2" > x:=x,'2'. > x > For non-resumable exceptions, this would be okay (although the error should not read "Cannot return"). However, since ZeroDivide is explicitly resumable, this is a bug. :-) > So my final suggestion is to define the behavior for re-signaling as > equivalent to #outer Makes total sense to me. Kind regards, Jakob |
Waouh, you're exploring dark corners ;)
Le mar. 4 mai 2021 à 21:11, Jakob Reschke <[hidden email]> a écrit : > > Hi Jaromir, > > Thank you for this survey! At least we can take away that we will not > hurt portability much by changing anything here... > > Am Di., 4. Mai 2021 um 20:06 Uhr schrieb Jaromir Matas <[hidden email]>: > > > > One more argument and then I'll leave it :) If you try the following example > > where #return is replaced by #resume, Squeak (and Pharo and VA) will fail > > completely raising a cannot return error: > > > > | x | > > x:=''. > > [ > > [1/0. x:=x,'B'] on: ZeroDivide do: [:ex | ex signal. x:=x,'A'. ex resume]. > > "handler 1" > > x:=x,'1' > > ] on: ZeroDivide do: [:ex | ex resume]. "handler 2" > > x:=x,'2'. > > x > > > > For non-resumable exceptions, this would be okay (although the error > should not read "Cannot return"). However, since ZeroDivide is > explicitly resumable, this is a bug. :-) > > > So my final suggestion is to define the behavior for re-signaling as > > equivalent to #outer > > Makes total sense to me. > +1 too, it should be fairly easy to implement (just check for nil signalContext or not in signal) > Kind regards, > Jakob > |
In reply to this post by Nicolas Cellier
Nicolas Cellier wrote
> Waouh, you're exploring dark corners ;) Hi, yeah... found some skeletons :) I'll send a fix to the Inbox closing the second issue (re-signalling) and I'll leave the first question open, i.e. whether the following two are equivalent or distinct: [] on: Exception do: [ 42 ] [] on: Exception do: [:ex | ex return: 42 ] Thanks, ----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
^[^ Jaromir
|
On Tue, May 4, 2021 at 1:30 PM Jaromir Matas <[hidden email]> wrote: Nicolas Cellier wrote I've always assumed/understood that the two are equivalent. Phrasing the question the other way around, in what ways would anyone expect [] on: Exception do: [ 42 ] [] on: Exception do: [:ex | ex return: 42 ] to differ? Thanks, _,,,^..^,,,_ best, Eliot |
In reply to this post by Jaromir Matas
On Tue, May 4, 2021 at 1:30 PM Jaromir Matas <[hidden email]> wrote: Nicolas Cellier wrote exception return: (handlerBlock cull: exception) so that if the handler block returns a value the exception is sent return: with its value. Phrasing the question the other way around, in what ways would anyone expect [] on: Exception do: [ 42 ] [] on: Exception do: [:ex | ex return: 42 ] to differ?
_,,,^..^,,,_ best, Eliot |
Hi Eliot,
Eliot Miranda-2 wrote > I've always assumed/understood that the two are equivalent. To be more > precise I've always assumed/understood that the first is short-hand for > the > second. That somewhere in the exception system there is code like > > exception return: (handlerBlock cull: exception) > > so that if the handler block returns a value the exception is sent return: > with its value. That was the idea behind this post. Currently there's no such code and so the two structures are distinct and can provide two different results as Jakob showed earlier. However, as Jakob showed too, ANSI probably does allow for such interpretation. I wish it didn't :) The current implementation doesn't even guarantee the two forms of return will return to the same context as was the case with #outer and re-signaling bugs fixed earlier. So I suggested to at least avoid that and modify the code to an equivalent of: exception handlerContext return: (handlerBlock cull: exception) Eliot Miranda-2 wrote > Phrasing the question the other way around, in what ways would anyone > expect > > [] on: Exception do: [ 42 ] > [] on: Exception do: [:ex | ex return: 42 ] > > to differ? Can't think of any... I considered them synonymous. Thanks a lot for your opinion. best, ----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
^[^ Jaromir
|
Free forum by Nabble | Edit this page |