Hi Christoph,
My understanding was we agreed that the above return was illegal (despite the fact it is "technically" possible to proceed).
As part of the "[squeak-dev] Re: Resuming on BlockCannotReturn exception" thread (https://lists.squeakfoundation.org/archives/list/squeak-dev@lists.squeakfoun...) Eliot wrote on Nov 11, 2023:
quote
IMO a better example is | expr | expr := true. [[expr ifTrue: [^ 1]. ^2] on: BlockCannotReturn do: [:ex | self halt. ex resume] ] fork
Here IMO we want a) the resume attempt to cause another BlockCannotReturn and/or terminate the computation opening a debugger which highlights ^1 b) we never want to execute ^2
unquote
On Nov 19, 2023 you asked:
quote
[[true ifTrue: [^ 1]] on: BlockCannotReturn do: #resume ] fork. "Illegal return from ^1"
Is this really illegal? Remember that resuming an exception is generally something special, and as this exception refers to a return instruction, resuming it is something I would definitely consider metaprogramming or worse. Thinking of ^ 1 as a syntactic sugar for "thisContext home return: 1", I would actually find it plausible that resuming from the exception would skip the #return: send. However, I have not read the entire conversation, so if Eliot says something different, please ignore this objection. :-)
unquote
Apologies for copying the responses into the message but I can't figure out how to reference the particular messages in the thread. The thread is super long and maybe you missed it. Or do you have some other concern on your mind?
Thanks for discussing these issues.
PS: Sorry for not responding to our other "open" discussions; I look forward to finding some time. Thanks for your patience.
Jaromir
On 25-Feb-24 12:29:38 AM, christoph.thiede@student.hpi.uni-potsdam.de wrote:
Hi Jaromir, Eliot, all,
forgive me for awakening the dead again, but in attempt to clean up some old inbox versions, I have stumbled upon this again. While I think you have fixed all the BCR issues (?), this point seems unresolved:
- Not in all situations, the receiver of #cannotReturn: is actually
unable to resume. Consider this example for a disproof:
a := [true ifTrue: [^ 1]. 2]. "Both statements need to be executed separately in a Workspace so
that [a outerContext sender] becomes nil!"
a value.
In this situation, it is valid to resume from BlockCannotReturn and
currently also possible in the Trunk. Note that BlockCannotReturn even overrides #isResumable to answer true, though the class comment discrecommends resuming it.
Have we ever come to an aggreement whether we want to support this - run the second statement, press Proceed, get the result 2 or not? After searching on the mailing list for more than 30 minutes, I still have not found a definite answer to that question. :D What I have found is: Currently, this does not work because we are niling the pc in Context>>#cannotReturn:to:. If I comment out that line, it would work. Why do we need to nil it out? Do we already have an example, preferably a test in the trunk, for that? :-)
Best, Christoph
Sent from Squeak Inbox Talk https://github.com/hpi-swa-lab/squeak-inbox-talk
On 2021-11-28T19:10:59+01:00, mail@jaromir.net wrote:
Hi Christoph,
I just want to update this conversation with the latest: finally I
found a solution that avoids BCR crashes in the new #teminate (Kernel-jar.1426) without the need for additional states or exemptions (so ProceedBlockCannotReturn says goodbye and your proposal in Kernel-ct.1405 is fully compatible with Kernel-jar.1426 without any further changes).
Thanks again for pointing out the weak spots in my solution! :)
Best,
^[^ Jaromir
Sent from Squeak Inbox Talk
On 2021-11-17T21:54:33+01:00, mail at jaromir.net wrote:
Hi Christoph,
I've addressed your comments regarding the BlockCannotReturn
behavior in [1] and copy my comments here (and expand a bit):
Isn't that ProceedBlockCannotReturn tautologous? I think that by
actively proceeding from a BlockCannotReturn error users already accept that they are going to resume execution in another way.
Well, the very proceeding from a BlockCannotReturn error sort of
violates common sense but during our lengthy discussion you convinced me it makes a very good sense when troubleshooting :) The idea is by no means trivial - unlike hitting Proceed :) So an extra warning can't hurt...
But more importantly, I need something to know the user let the
process continue after reaching the BlockCannotReturn error - thus the new ProceedBlockCannotReturn exception which allows Process >> #complete:to: to deal with the new course of events.
It's just that introducing ProceedBlockCannotReturn seemed the
least intrusive to the code and it's working. It allowed me to remove the extra state I introduced earlier and hated it :)
Apart from that, the message text of your new warning is not
correct if self pc <= self endPC. :-)
Yes, and I'd like to make the warning message more verbose so even
if someone hit Proceed without much thinking they could get an idea what's about to happen :) Should the warning interfere with some potential automation efforts we could come up with some alternative way.
Handling BlockCannotReturn error is no doubt a separate issue from
#terminate and I included your patch in Kernel-jar.1414 along with the main termination code because it perfectly complements it and prevents the disastrous crashes while allowing to Proceed the BCR error when troubleshooting (or maybe even for exception handling, I'll have to refresh my memory: I'm referring to your example:
"Both statements need to be executed separately in a Workspace" a := [true ifTrue: [^ 1] yourself] [a value] on: BlockCannotReturn do: [:ex | ex resume]
).
To your other great example:
sender := thisContext swapSender: nil. true ifTrue: [^ 1]. "Proceed the BlockCannotReturn" thisContext privSender: sender. ^ 2
I think this should eventually answer 2. Apparently, the VM
already has reset the pc in this example so we are helpless here.
[...] the computation terminated is also wrong, IMO, you should
get a BlockCannotReturn here.
Yes, I agree; your example clearly shows there's definitely more to
the problem that needs to be studied and discussed :) I don't like the 'computation terminated' message either; it's seems to me a bit out of place (I mean calling the Debugger directly rather than raisin an exception - it confuses me) and maybe even the whole #cannotReturn: should be simplified - I just didn't want to start messing with this when working on #terminate :)
I very much look forward to your further comments and ideas.
Best,
Jaromir
[1]
http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-November/216999....
^[^ Jaromir
Sent from Squeak Inbox Talk
On 2021-08-22T17:33:59+02:00, christoph.thiede at
student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
sorry, I had already addressed your ProceedBlockCannotReturn
warning in [1] instead - but this thread is definitely the better place to continue this discussion. :-)
As already mentioned there, I doubt that warning the user again
after he/she already had deliberately proceeded from an interpreter error makes much sense. But this is also not a strong opinion, so once again I'm calling for a third opinion here!
To now address this example:
sender := thisContext swapSender: nil. true ifTrue: [^ 1]. "Proceed the BlockCannotReturn" thisContext privSender: sender. ^ 2
Very unfortunately, I cannot reproduce any longer what I have
reported there myself. But the computation terminated is also wrong, IMO, you should get a BlockCannotReturn here. closureOrNil is nil here because the compiler does inline the ifTrue instruction - add a yourself behind the [^ 1] and you will get a correct BlockCannotReturn which you also can resume from to return the 2. Anyway, I originally brought the example to illustrate that all the VM optimizations with inlined message sends and whatever else could possibly destroy our assumptions about returnable blocks and contexts. But for the superordinate discussion here, I just would treat this as an imperfect VM implementation but nothing to worry about. :D
BUT
if you debug it and: A) step through the ^1 - you get Message not understood B) step into ^1 - you don't get any error and can happily
continue
Ouh, these are several simulator bugs. And if you forego the
enclosing [] value and step into the ^1, you get just a different MNU ... This should be addressed, too. :-)
Without the enclosing [] value, I could fix both buttons by
inserting the following in Context >> #return:from: right after the assignment to newTop which you can try out by loading Kernel-ct.1409 frmo the inbox.
With the enclosing [] value, however, it is not that easy. Case
B) you mentioned is worst. In this example, the simulated snippet answers 1 instead of 2! If you debug the simulator ("debug button action" of the "Into" button in the last step), you can see that the difference here is that unless the VM executor, the simulator knows about the home context of the top context even if the latter has a nil sender (see Context >> #methodReturnTop). I can't tell which one of them is wrong, but they are not consistent, this is not good. ^^
Best, Christoph
[1]
http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-August/216211.ht...
Sent from Squeak Inbox Talk
On 2021-05-30T11:50:52-05:00, m at jaromir.net wrote:
Hi Christoph,
... my point here is: Proceeding from an error almost always
doesn't seem
"right". :-) It is always a decision by the debugging
programmer to
override the default control flow and switch to the "next
plausible
alternative control flow", i.e., resume as if the error would
have never
been raised. Applied to the attempt to return from a method,
for me, this
means to ignore the return (thinking of it in message sends:
to ignore the
"thisContext (home) return"). Yeah, and if there is no
further statement
after that return, my best understanding of the user's
intention to
"proceed" would be to return to the place from where the
block has been
invoked ...
Agreed :) The more I think about it the more I like it ;) And
well, the
non-local return could have been a typo anyways... so actually,
this makes
the best sense and preserves all options open for the user -
perfect!
Also, can you convince me why you would need some extra state
in the
exception for this?
No, I hated adding an extra state :) The only thing I really
need for the
full #terminate to work correctly is a way to distinguish
between normal
proceeding the computation and resuming the unwind procedure
when the
BlockCannotReturn error occurs inside an unwind block currently
being
evaluated. All I need then is a Warning the computation
proceeds beyond the
BlockCannotReturn; here's what I mean:
cannotReturn: result ����closureOrNil ifNotNil: [ ��������| resumptionValue | ��������resumptionValue := self cannotReturn: result to: self
home sender.
��������ProceedBlockCannotReturn new signal: 'This block has
ended, continue with
sender?'. ��������self pc > self endPC ifTrue: [ ������������"This block has ended, continue with sender" ������������thisContext privSender: self sender]. ��������^ resumptionValue]. ����Processor debugWithTitle: 'Computation has been
terminated!' translated
full: false
So if you're fine with this addition, full #terminate would
recognize when
it's beyond BlockCannotReturn and would continue unwinding the
non-local
return accordingly.
I think it's useful to warn the user about such an unusual (and
new) option
as Proceeding safely beyond BlockCannotReturn anyway :)
Argh, here is another example which does not yet match my
expectations:
sender := thisContext swapSender: nil. true ifTrue: [^ 1]. "Proceed the BlockCannotReturn" thisContext privSender: sender. ^ 2
I think this should eventually answer 2. Apparently, the VM
already has
reset the pc in this example so we are helpless here.
There's something wrong with this example :) (or my
understanding of it)
- if you print-it or do-it you get Computation terminated
instead of Block
cannot return
- so I wrapped it in [] value
[sender := thisContext swapSender: nil. true ifTrue: [^ 1]. "Proceed the BlockCannotReturn" thisContext privSender: sender. ^ 2] value
Now it raises BlockCannotReturn and returns 2 with your
changeset (if
Proceeded)...
BUT
if you debug it and: A) step through the ^1 - you get Message not understood B) step into ^1 - you don't get any error and can happily
continue
I'm confused...
Many thanks for your comments and your proposed solution to
cannot return;
I'll update #terminate and remove my previous attempts from the
Inbox.
best,
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html