The Inbox: Kernel-jar.1399.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: Kernel-jar.1399.mcz

commits-2
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"!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Nicolas Cellier
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"!
>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jaromir Matas
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
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jaromir Matas
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
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jakob Reschke
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
>

Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jaromir Matas
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
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jakob Reschke
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
>

Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jaromir Matas
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
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jakob Reschke
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

Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Nicolas Cellier
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
>

Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jaromir Matas




-----
^[^ Jaromir
--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html

^[^ Jaromir
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jaromir Matas
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
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Eliot Miranda-2


On Tue, May 4, 2021 at 1:30 PM Jaromir Matas <[hidden email]> wrote:
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 ]

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,

-----
^[^ Jaromir
--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html


--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Eliot Miranda-2
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
> 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 ]

 
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.

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,




-----
^[^ Jaromir
--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html



--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-jar.1399.mcz

Jaromir Matas
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