[Pharo-project] [squeak-dev] Re: Problem with variable capture in blocks or ? #7532

Eliot Miranda eliot.miranda at gmail.com
Sun May 16 20:47:43 EDT 2010


On Sun, May 16, 2010 at 3:32 PM, Andreas Raab <andreas.raab at gmx.de> wrote:

> Hi Ken -
>
> Very interesting. Looks like an issue with OutOfScopeNotification. The
> problem appears to be that this code:
>
>        "..." [ :a | a ].
>        "..." a.
>
> generates an OutOfScopeNotification that is being suppressed in workspaces
> (I'm not exactly sure why that is). This is of course is interesting because
> of the highly unusual situation of a temp shadowing a global instead of
> another temp / ivar. For example, this wouldn't compile:
>
>  | a |
>  [ :a | a ].
>
> But stranglely, this does:
>
>  [ :Object | Object ].
>
> so your example code is roughly equivalent to running:
>
>        b := [ :Object | Object ].
>        c := b value: Object.
>
> When you run this line by line it works 'as expected' and when you run it
> as a single doIt you get the result of c being a "[closure] in
> UndefinedObject>>DoIt" due to the OutOfScopeNotification.
>
> Hope this explains the issue - as for how to fix it, I have no clue :-)
>

Yes, the bug is that Encoder>>encodeVariable:sourceRange:ifUnknown: doesn't
continue from a caught OutOfScopeNotification by answering the ifUnknown:
action.  Instead it returns the out-of-scope block temp.  This is a rather
serious compiler bug that I'd left unfixed because it only bites in
workspaces (apologies) and I didn't have a small example to analyse before
(thanks Ken!).  The code generated is actually equivalent to

       b := [ :a | a ].
       c := b value: <temp 0>.

Since there aren't any temps, temp 0 is top of stack, which just happens to
be the closure just created by the preceding bytecode.  e.g. look at
bytecode 49 in the following:

a := 4. b := [ :a | a ]. c := b value: a. { a. b. c } thisContext method
symbolic

The fix is simple.  When an OutOfScopeNotification is caught the Encoder
should still answer what ever it should for an out of scope variable.   In
Encoder>>encodeVariable:sourceRange:ifUnknown: the statements

(varNode isTemp and: [varNode scope < 0]) ifTrue: [
OutOfScopeNotification signal ifFalse: [ ^self notify: 'out of scope'].
].
^ varNode

should read

(varNode isTemp and: [varNode scope < 0]) ifTrue:
[^OutOfScopeNotification signal
ifTrue: [action value]
ifFalse: [self notify: 'out of scope']].
^varNode

Find attached:

!Encoder methodsFor: 'encoding' stamp: 'eem 5/16/2010 17:33'!
encodeVariable: name sourceRange: range ifUnknown: action
| varNode |
varNode := scopeTable
at: name
ifAbsent:
[(self lookupInPools: name
ifFound: [:assoc | varNode := self global: assoc name: name])
ifTrue: [varNode]
ifFalse: [^action value]].
range ifNotNil:
[name first canBeGlobalVarInitial ifTrue:
[globalSourceRanges addLast: { name. range. false }]].

(varNode isTemp and: [varNode scope < 0]) ifTrue:
[^OutOfScopeNotification signal
ifTrue: [action value]
ifFalse: [self notify: 'out of scope']].
^varNode! !

P.S. this is probably applicable to any Squeak bytecode compiler (including
eToys).  The only change in my closure compiler from Andreas' 2003 version
was changing "name first isUppercase" to "name first canBeGlobalVarInitial".


P.P.S.  Again Ken, thanks for a comprehensible example.  It always bit me in
huge doits I was using to analyse the entire system's compiled methods, and
invariably crashed the VM.  I never took the time to isolate the bug, I just
fixed the doit and continued.  Turns out to be very simple.

best
Eliot


> Cheers,
>  - Andreas
>
>
>
> On 5/16/2010 12:35 PM, Ken Causey wrote:
>
>> Well, it's nothing new but this one has stumped me:
>>
>> http://bugs.squeak.org/view.php?id=7532
>>
>> Initially I (and Frank) thought the reporter was mistaken until we
>> understood that the problem shows up when you execute the main code all
>> in one do-it.  I've since modified the original report to make this
>> clearer.
>>
>> So here it is:
>>
>> a := 4.
>> b := [ :a | a ].
>> c := b value: a.
>>
>> If you SELECT ALL OF THIS AND EXECUTE IT ALL AT ONE TIME (crucial
>> detail). The result is that a is 4, b is a BlockClosure, and c is a
>> BlockClosure not 4 as expected. Execute each statement separately and c
>> is 4.
>>
>> Alternately, from a suggestion from jmckeon, if you specify a different
>> symbol for the block argument:
>>
>> a := 4.
>> b := [ :d | d ].
>> c := b value: a.
>>
>> when executed all at one time works as you would expect: a is 4, b is a
>> BlockClosure, and c is 4. (and d is nil)
>>
>> So what's up?
>>
>> Ken
>>
>>
>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.pharo.org/pipermail/pharo-dev_lists.pharo.org/attachments/20100516/2a00a097/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Encoder-encodeVariablesourceRangeifUnknown.st
Type: application/octet-stream
Size: 726 bytes
Desc: not available
URL: <http://lists.pharo.org/pipermail/pharo-dev_lists.pharo.org/attachments/20100516/2a00a097/attachment.st>


More information about the Pharo-dev mailing list