I6: Unexpected source order sensitivity causes SERIOUS errors?

[EDIT: Note that the title of the post was modified to reflect that fact that on some interpreters the error generated is serious (causing interpreter halt) instead of an RTE. See follow-up posts below for details.]

The following code causes some unexpected RTEs (run-time errors) that don’t seem to make sense:

[ InitializeParentClasses o c;
    objectloop (o ofclass Ext_Class) ! should ensure only objects with parent_class property are found
        {
            objectloop (c ofclass Class && c ~= Class or Object or Routine or String or Ext_Class)
                {
                    print "Setting ", (name) o, ".parent_class to ", (name) c, ".^";
                    o.parent_class = c; ! why does this cause an RTE "no property parent_class to set"?
                }
        }
];

! This is apparently the wrong location for the Ext_Class definition, move ahead of InitializeParentClasses to fix
Class Ext_Class
    with    parent_class;

Class   Pebble(5)
    class   Ext_Class
    with    short_name "tiny pebble";

Object  Pail; ! this will not get a parent_class and be skipped by InitializeParentClasses()

[ Main ;

    InitializeParentClasses();

    SeeAllPebbles();
    new_line;

];

[ SeeAllPebbles x ;
    objectloop (x ofclass Pebble)
        {
            print "Object ", (name) x, " is a child of ", (name) parent(x);
            if (x ofclass Pebble)
                print " and has parent_class ", (name) x.parent_class;
            print "^";
        }
];

The specific error messages produced are:

Setting Pebble_1.parent_class to class Pebble.

[** Programming error: Pebble_1 (object number 7)  has no property parent_class to set **]
Setting Pebble_2.parent_class to class Pebble.

[** Programming error: Pebble_2 (object number 8)  has no property parent_class to set **]
Setting Pebble_3.parent_class to class Pebble.

[** Programming error: Pebble_3 (object number 9)  has no property parent_class to set **]
Setting Pebble_4.parent_class to class Pebble.

[** Programming error: Pebble_4 (object number 10)  has no property parent_class to set **]
Setting Pebble_5.parent_class to class Pebble.

[** Programming error: Pebble_5 (object number 11)  has no property parent_class to set **]
Setting Pebble_6.parent_class to class Pebble.

[** Programming error: Pebble_6 (object number 12)  has no property parent_class to set **]
Object Pebble_1 is a child of class Pebble and has parent_class nothing
Object Pebble_2 is a child of class Pebble and has parent_class nothing
Object Pebble_3 is a child of class Pebble and has parent_class nothing
Object Pebble_4 is a child of class Pebble and has parent_class nothing
Object Pebble_5 is a child of class Pebble and has parent_class nothing
Object Pebble_6 is a child of class Pebble and has parent_class nothing

[Hit any key to exit.]

As noted in the source code’s comments, the cause of the issue seems to be the relative order of the definitions for Ext_Class and InitializeParentClasses. If Ext_Class comes first, then the code works as expected:

Setting Pebble_1.parent_class to class Pebble.
Setting Pebble_2.parent_class to class Pebble.
Setting Pebble_3.parent_class to class Pebble.
Setting Pebble_4.parent_class to class Pebble.
Setting Pebble_5.parent_class to class Pebble.
Setting Pebble_6.parent_class to class Pebble.
Object Pebble_1 is a child of class Pebble and has parent_class class Pebble
Object Pebble_2 is a child of class Pebble and has parent_class class Pebble
Object Pebble_3 is a child of class Pebble and has parent_class class Pebble
Object Pebble_4 is a child of class Pebble and has parent_class class Pebble
Object Pebble_5 is a child of class Pebble and has parent_class class Pebble
Object Pebble_6 is a child of class Pebble and has parent_class class Pebble

[Hit any key to exit.]

Note that the compiler does not complain that property parent_class is not defined when Ext_Class is placed after InitializeParentClasses(). This makes me think that it is doing some sort of lookahead pass, which makes me think that it should be able to pick up the property in the later Ext_Class definition. Also note that it is possible to set the parent_class of a Pebble instance in Main even when InitializeParentClasses() is causing RTEs.

It seems like, at the very least, the RTE error message is inappropriate in this case, as the objects in question clearly do have the parent_class property.

Does this count as an I6 bug? If so, is it possible to report this anywhere right now? (I see that the Mantis bug reporting system is still down at inform7.com; does anyone know when/if it will be restored?)

(And yes, I recognize that multiple inheritance means that a given object can have more than one “parent” class, so it doesn’t necessarily make sense to store the information in a single variable.)

I’ve written this up at https://gitlab.com/DavidGriffith/inform6lib/-/issues/114 and am studying the problem.

@DavidG, I think maybe this is more of a compiler issue than a Standard Library issue. It seems like the compiler looks ahead and identifies that parent_class is a legal property name, but then it somehow forgets this while compiling InitializeParentClasses(), such that an assignment to the parent_class property is judged illegal at runtime (but only within InitializeParentClasses).

I’m not sure how this is happening (or even that I’m right about the above), but routine SeeAllPebbles(), which is defined after the declaration of the parent_class property doesn’t seem to have a problem reading the property on those same objects, so the property clearly exists and is attached to the same objects that the RTEs are complaining about. (Paging @DavidK?)

I’ve not yet looked into this, but to answer an earlier question, bugs in the Inform 6 compiler (but not the library) can be reported via GitHub: https://github.com/DavidKinder/Inform6

Has anything more been done with this? I see that there is no bug report on this at the compiler repo.

I’ll just close the issue in the Library repo.

If I’m interpreting it correctly, then according to the Inform Technical Manual, the errors that I was encountering here shouldn’t happen. There is a passage in section 6.3 Lexical level 4: tokens to etokens (https://inform-fiction.org/source/tm/chapter6.txt) that states:

In most languages it would be an error to use an unknown symbol name as
a value: in C, for example, a function name cannot be used in an expression
unless it has been declared beforehand.

Inform is more tolerant: as far as expressions are concerned, any symbol can
be used earlier than the point in the source code where its value is
assigned, excepting only a local or global variable name. (This exception
is mainly for Z-machine architecture reasons, but it also makes Inform code
more legible.)

When an unknown symbol is reached, it is converted to a LARGE_NUMBER_TT and
marked as SYMBOL_MV, with the value being its index in the symbol table.
(This allows the backpatcher to know where to find the true value later.)

When a known symbol is reached which is flagged as CHANGE_SFLAG, this is
treated in the same way. (CHANGE_SFLAG is given to symbols defined by
Constant definitions whose values themselves require backpatching: for
instance, symbols defined equal to dictionary words or routine addresses.)

Given the symptoms show above, does this mean that something is going wrong during the backpatching process for the parent_class individual property of Ext_Class? Or perhaps that parent_class is not being recognized as a previously-encountered symbol when it is encountered a second time?

The RTEs appear in Inform 6.31, 6.33, 6.34 and 6.35.

You may have to dig into the compiler source to figure out what’s going on here.

@zarf, I’ve gotten some better debugging output from the compiler on this, but I’m not 100% sure that I’m reading it correctly.

With assembly tracing cranked up, the following in the translation of InitializeParentClasses() seems to correspond to the lines as seen in the source:

    8  +00036 <*> print        "Setting "               ! start of announcement by InitializeParentClasses()
                               b2 13 0a 67 2e cd 80 
    8  +0003d     call_2n      long_6 o 
                               da 2f 00 06 01 
    8  +00042     print        ".parent_class to "
                               b2 16 55 1a ea 4f 25 59 11 1b 18 03 34 80 a5 
    8  +00051     call_2n      long_6 c 
                               da 2f 00 06 02 
    8  +00056     print        ".^"                     ! end of announcement by InitializeParentClasses()
                               b2 16 45 9c a5 
    9  +0005b <*> call_vn      long_32 o long_35 c      ! <--- actual call attempting to set parent_class property?
                               f9 22 00 20 01 00 23 02
   10  +00063    .L5
   10  +00063     inc          c                        ! increment for object loop, at "}"
                               95 02

The long_32 and long_35 operands used by the call_vn opcode were a mystery. I guessed that long_32 was a reference to a veneer routine for setting properties, while long_35 seemed to correspond to the state of the symbol table at that point:

...
33  InitializeParentClasses   1:0003  0002  Routine  
34  Ext_Class          1:0004  0100  Defined constant  (?) (used) 
35  parent_class       1:0009  0100  Defined constant  (?) (used) 

Asking for dictionary output again after the Ext_Class declaration shows that the symbol table has been updated:

33  InitializeParentClasses   1:0003  0002  Routine  
34  Ext_Class          1:0019  0005  Class  (used) 
35  parent_class       1:0019  0048  Individual property  (used) 

The lines from translation of SeeAllPebbles() [where no RTE occurs] that seem of interest are:

   55  +00185 <*> print        " and has parent_class "   ! printed string inside the if clause for Pebble class
                               b2 00 d3 24 0d 1b 00 54 d7 2a 79 16 c8 44 d8 e0 
                               05 
   55  +00196     call_vs      long_10 x short_72 -> sp   ! <--- call to get parent_class value and place on stack?
                               e0 27 00 0a 01 48 00 
   55  +0019d     call_2n      long_6 sp                  ! <--- call to print name of retrieved property?
                               da 2f 00 06 00 
   55  +001a2    .L6
   56  +001a2 <*> print        "^"                        ! end of printed output
                               b2 94 e5 
   57  +001a5    .L2
   57  +001a5     inc          x                          ! increment for object loop, at "}"
                               95 01

I noticed that the values used in the two places where the code is accessing the parent_class property are different. I think that this is to be expected, with the assumption that the long_35 value (entry number of parent_class in symbol table) would be replaced as appropriate during the backpatching stage. There is a line from the backpatching portion of debug output that looks like it is backpatching the symbol for parent_classes at some point:

BP ref to symbol value applied to 0023 giving  0048 ! $23 = 35 (parent_class symbol), assigned property number $48?

To reconcile the story file with debug assembly directly via hex editor, I put in some lines in Initialise() to print the addresses of various functions and get the code offset:

IPC       417 -> $01A1 -> packed $0684 ! InitializeParentClasses()
SAP       490 -> $01EA -> packed $07A8 ! SeeAllPebbles()
RV_Pr     546 -> $0222 -> packed $0888 ! veneer
WV_Pr     256 -> $0100 -> packed $0400 ! veneer WV__Pr() -- address not reported correctly? (or just deemed unused?)
RT__ChPS 1058 -> $0422 -> packed $1088 ! veneer
code_offset   -> 0067c

After translating packed addresses and looking at the the raw hex data there is evidence of backpatching:

! from InitializeParentClasses() -- execute assignment o.parent_class = c;

    9  +0005b <*> call_vn      long_32 o long_35 c      ! <--- veneer call to RT__ChPS()
                               f9 22 00 20 01 00 23 02
                       post-BP f9 22 04 22 01 00 48 02  ! <--- hex inspection post-backpatch
                                              ** **            ($0023 [symbol 35] to $0048 [property 72]?)
                                                               NOTE: $0422 is packed address of RT__ChPS()

! from SeeAllPebbles() -- evaluate expression x.parent_class

   55  +00196     call_vs      long_10 x short_72 -> sp   ! <--- call to RV__Pr(), get parent_class value and place on stack?
                               e0 27 00 0a 01 48 00 
                       post-BP e0 27 02 22 01 48 00       ! <--- hex inspection post-backpatch
                                              **                 ($48 unchanged because parent_class defined by then?]
                                                                 NOTE: $0222 is packed address of RV__Pr()

If I’m correctly reading this, then the “call_vn long_32 o long_35 c” line (i.e. the one generating the error during execution) is a call to RT__ChPS():

[ RT__ChPS obj prop val size;
    if (obj<5 || obj>(#largest_object-255) || obj in 1 || obj.&prop==0 || (size=obj.#prop)>2 ) ! this condition met
        return RT__Err(\"set\", obj, prop, size);                                              ! observed RTE
    @put_prop obj prop val;
    #ifdef INFIX;
    if (obj has infix__watching || (debug_flag & 15))
        RT__TrPS(obj,prop,val);
    #ifnot;
    #ifdef DEBUG;
    if (debug_flag & 15) RT__TrPS(obj,prop,val);
    #endif;
    #endif;
    return val;
];

I made a temporary modification of the code to print out information relevant to the RT__ChPS() error condition.

print "<obj = ", o, " / parent(ob) = ", parent(o), ">^";
print "<obj.#prop = ", o.#parent_class, " / obj.&prop = ", o.&parent_class, ">^$

and that made it clear that, in fact (and just as the RTE reports), there is no property being assigned:

Setting Pebble_1.parent_class to class Pebble.
<obj = 7 / parent(obj) = 6>
<obj.#prop = 0 / obj.&prop = 0>

[** Programming error: Pebble_1 (object number 7)  has no property parent_class to set **]

Does the work shown above seem correct? If so, I will look at the compiler code to try to figure out why no property is being assigned. It seems like there should be no trouble here, as the symbol table has a definition of parent_class as a property and Ext_Class as a class by the time that the Pebble class is declared to inherit from Ext_Class – in other words, it’s not clear how order sensitivity could matter if backpatching is being done correctly.

I realized that this RTE is the same one that is issued when attempting to set a common property (i.e. one declared with a Property directive) on an object that does not have that property defined locally via class inheritance or its own declaration. That seems like a clue.

Another clue is that (to my surprise) the values returned by the .# and .& operators for the same property at different parts of the code do not agree.

For code compiled prior to proper declaration of parent_class property:

Setting Pebble_1.parent_class to class Pebble.
<o.3 == 975>
<o.#parent_class = 0>
<o.&parent_class = 0>

[** Programming error: Pebble_1 (object number 7)  has no property parent_class to set **]

For code compiled after proper declaration of parent_class property (but in the same program):

Object Pebble_1 is a child of class Pebble and has parent_class nothing
<x.3 == 975>
<x.#parent_class = 2>
<x.&parent_class = 983>

OK, I finally got to the bottom of this (with thanks to @eriktorbjorn for pointing out what should have been obvious long before, and to @zarf for trying to do so). It’s definitely a compiler bug. Short version:

  1. When encountering an attempt to make use of an unknown property, the compiler generates inline code and/or calls to veneer routines that assume the property is a common property, not an individual property.
  2. Although the compiler detects the need for backpatching the property identifier in this case and correctly replaces the marker value with the assigned individual property identifier, it does not rewrite or adjust the previously-generated assembly to take into account that the backpatched identifier is for an individual property as opposed to a common property.
  3. The result is code that attempts to access the Z-machine common property table with a property identifier outside the range 0 to 63. Runtime effect is interpreter-specific, with some (e.g. Frotz 2.44) returning zero value and others (e.g. Bocfel 0.6.0) reporting a VZEFH.

More follows in next post.

Regarding the above: Can it/should it/will it be fixed? There are some options.

First option: Compiler generates error message in this situation (aka “It was always wrong, so now it’s right.”) – The compiler can see that it is about to backpatch a property identifier marker value with a value greater than 63. Rather than continuing, it can report an error indicating that individual properties must be declared before use. This invalidates the quote from ITM cited several posts above, but oh, well.

Second option: Modification of veneer (aka “Always defer property handling to runtime.”) – The current behavior of the compiler assumes that an unknown property will be a common property when generating code. When it sees source code that tries to read such an unknown property, uses of .#prop or .&prop translate directly into opcodes (@get_prop_len and @get_prop_addr), while plain .prop translates into a call to veneer routine RT__ChPR(). When writing, plain .prop = translates into a call to RT__ChPS(). Note that neither RT__ChPR() nor RT__ChPS() check whether the supplied property identifier is within the legal range 0 to 63.

The behavior is quite different after the individual property’s declaration has been encountered in source code and the symbol table has been updated. After this point, veneer routines RV__Pr() and WV__Pr() are deployed for reading and writing to the property, respectively, while use of .# and .& translate (I think) into calls to veneer routines RL__Pr() and RA__Pr(), also respectively.

It seems like it would be possible to create a “one true” veneer routine for each property-related function (reading property data length, reading property address, reading property value, writing property value) and to have that routine check the property identifier to determine whether handling should be as a common or individual property. This is would fit with the philosophy mentioned in ITM 6.6:

One ambiguity remaining in the tree is that the operators ".", ".#" and ".&"
act on both common and individual properties (that is, properties handled by
the Z-machine's architecture directly, and properties handled by the run-time
veneer routines).  The decision about which is intended is made here, by
looking at the right operands of the operators.  (If it's a common property
number, or an individual property number, the decision is obvious; if it's a
variable or some compound expression, then we decide in favour of individual
properties (the run-time veneer routines can patch things up if this is a
wrong decision).)

Note that the RT__ChP* routines can be modified to check whether they are being used incorrectly, and reroute the call if they are, e.g.

Replace RT__ChPS;

[ RT__ChPS obj prop val    size;
    if (prop > 63)                                  ! NEW LINE
        return WV__Pr(obj, prop, val);              ! NEW LINE
    if (obj<5)
        print "<obj less than 5? ", (obj<5), ">^";
    if (obj>(#largest_object-255))
        print "<obj greater than largest obj num? ", (obj>(#largest_object-255)), ">^";
    if (obj in 1)
        print "<obj in 1? ", (obj in 1), ">^";
    if (obj.&prop==0)
        print "<obj.&prop is 0? ", (obj.&prop==0), ">^";
    print "<obj..&prop is ", (obj..&prop), ">^";
    if ((size=obj.#prop)>2)
        print "<obj.#prop greater than 2? ", ((obj.#prop)>2), ">^";
    if (obj<5 || obj>(#largest_object-255) || obj in 1 || obj.&prop==0 || (size=obj.#prop)>2 )
        return RT__Err("set", obj, prop, size);    ! passed size parameter ignored by RT__Err?
    @put_prop obj prop val;
    #ifdef INFIX;
    if (obj has infix__watching || (debug_flag & 15))
        RT__TrPS(obj,prop,val);
    #ifnot;
    #ifdef DEBUG;
    if (debug_flag & 15)
        RT__TrPS(obj,prop,val);
    #endif;
    #endif;
    return val;
];

This particular modification does, in fact, both eliminate the RTEs seen in my original example code and cause the .parent_class values to be set correctly by InitializeParentClasses(). However, this approach cannot address the in-line non-veneer code generated to handle the nearby .# and .& operations, so some compiler modifications are unavoidable.

Third option: Modification of compiler to adjust code when backpatching individual properties (aka “Improved but potentially more complicated backpatching.”) – This would involve making the backpatching process replace or modify the previously-generated code for individual property-accessing functions. That seems potentially messy, but it also seems like assorted adjustments to previously-generated code are already being made in the backpatching process, so maybe it wouldn’t be that bad. I don’t understand the specifics here well enough to say.

There may be other options, and one or more of the above approaches may have flaws – I’m not an expert on the compiler.

Note that basic validation of what I’m outlining can be verified with the inactive -g3 compiler switch enabled. More detailed proof requires inspection of initial assembly, logs of changes due to backpatching, and knowledge of the final packed addresses of assorted veneer routines; the first two can be obtained through use of Trace assembly 4; and the last can be obtained with suitable print statements in Initialise(). Following are some examples of debugging output from different parts of my process.

This is what the “pre-declaration” program output (i.e. output from InitializeParentClasses(), prior to formal declaration of the parent_class property) looks like without -g3 enabled:

<o.parent_class = 0>                                    ! line 8
<o.#parent_class = 0>                                   ! line 9
<o.&parent_class = 0>                                   ! line 10
Setting Pebble_1.parent_class to class Pebble.          ! line 11

[** Programming error: Pebble_1 (object number 7)  has no property parent_class to set **]  ! line 12

This is what pre-declaration output looks like with -g3 (very helpfully) enabled:

<o.parent_class = [ RT__ChPR(obj = 7, prop = 72) ]      ! <--- RT__ChPR() makes no check for .&prop == 0 in RTE logic
0>
<o.#parent_class = 0>                                   ! <--- evaluates to zero at runtime, notice no veneer call!
<o.&parent_class = 0>                                   ! <--- evaluates to zero at runtime, notice no veneer call!
Setting [ PrintShortName(obj = 7) ]
[ Meta__class(obj = 7) ]
[ Z__Region(addr = 7) ]
[ Unsigned__Compare(x = 7, y = 1662) ]
Pebble_1.parent_class to [ PrintShortName(obj = 6) ]
[ Meta__class(obj = 6) ]
[ Z__Region(addr = 6) ]
[ Unsigned__Compare(x = 6, y = 1662) ]
class Pebble.
[ RT__ChPS(obj = 7, prop = 72, val = 6) ]               ! <--- attempt to write to undeclared parent_class invokes RT__ChPS()
[ RT__Err(crime = 1636, obj = 7, id = 72, size = 0) ]   ! <--- RT__ChPS() invokes error condition because .&parent_class == 0

[** Programming error: Pebble_1 (object number 7)  has no property [ Print__PName(prop = 72) ]

The assembly debug output for line 8 (accessing .parent_class):

8  +00074 <*> print        "<o.parent_class = "
                           b2 14 c1 72 85 4a a6 5d 53 64 b6 22 26 63 00 14 
                           c1 f4 05 
8  +00087     call_vs      long_33 o long_36 -> TEMP1 ! <--- RT__ChPR(o, parent_class);
                           e0 23 00 21 01 00 24 ff    !      $05c3 = RT_ChPR (long const), $48 = parent_class (short const)
                   post-bp e0 23 05 c3 01 00 48 ff    !      $01 = local 1 = o (short var) // CRASHES SOME INTERPRETERS
8  +0008f     push         TEMP1 
                           e8 bf ff 
8  +00092     print_num    sp 
                           e6 bf 00 
8  +00095     print        ">^"
                           b2 14 c1 f8 a7 

The assembly debug output for line 9 (accessing .#parent_class); it does not make use of veneer routines as much as the post-declaration equivalent:

9  +0009a <*> print        "<o.#parent_class = "
                           b2 14 c1 72 85 48 b7 54 d7 2a 79 16 c8 44 d8 60 
                           05 18 3d 80 a5 
9  +000af     jg           short_1 o to L9 if TRUE
                           23 01 01 80 09 
9  +000b4     jg           o long_12 to L8 if FALSE
                           c3 8f 01 00 0c 00 08 
9  +000bb    .L9
9  +000bb     call_vn      long_22 short_22 o        ! call to RT__Err (skipped in actual operation)
                           f9 1b 00 16 16 01 
9  +000c1     store        TEMP2 short_2 
                           0d fe 02 
9  +000c4     jump         L10 
                           8c 00 0a 
9  +000c7    .L8
9  +000c7     store        TEMP2 o                   ! set TEMP2 to o
                           2d fe 01 
9  +000ca    .L10
9  +000ca     get_prop_addr TEMP2 long_36 -> TEMP1   ! non-strict call to get prop addr for o.parent_class
                           d2 8f fe 00 24 ff
                   post-bp d2 8f f3 00 48 ff
9  +000d0     jz           TEMP1 to L11 if TRUE      ! jump to L11 if retrieved prop addr is zero (branches here on some interpreters)
                           a0 ff 80 0b 
9  +000d4     get_prop_len TEMP1 -> TEMP1            ! non-strict call to get prop len for o.parent_class (no bp change)
                           a4 ff ff 
9  +000d7    .L11
9  +000d7     push         TEMP1                     ! push default zero or retrieved length to stack
                           e8 bf ff 
9  +000da     print_num    sp                        ! print default zero or retrieved length
                           e6 bf 00 
9  +000dd     print        ">^"
                           b2 14 c1 f8 a7 

Also of interest is the actual attempt to set .parent_class pre-declaration in lines 11 and 12:

11  +00123 <*> print        "Setting "
                           b2 13 0a 67 2e cd 80 
11  +0012a     call_2n      long_6 o 
                           da 2f 00 06 01 
11  +0012f     print        ".parent_class to "
                           b2 16 55 1a ea 4f 25 59 11 1b 18 03 34 80 a5 
11  +0013e     call_2n      long_6 c 
                           da 2f 00 06 02 
11  +00143     print        ".^"
                           b2 16 45 9c a5 
12  +00148 <*> call_vn      long_32 o long_36 c         ! <--- call to RT__ChPS(1 [local 1=o], 72 [parent_class], 2 [local 2=c])
                           f9 22 00 20 01 00 24 02     !      $059c = RT_ChPS (long const), $48 = parent_class (short const)
                   post-bp f9 22 05 9c 01 00 48 02     !      $01 = local 1 = o (short var), $02 = local 2 = c (short var)

This may boil down to a bug in the check_property_operator() routine in the compiler.

(Note to myself, mostly.)

Curious: in Glulx, some of these forward-declared property cases do cause an error message. (Although your original test case does not.)

"<constructing output>", line 0: Error:  *** Illegal backpatch marker in forward-declared symbol

This is fixable in a straightforward way. I probably just never tested that case, because no working Inform code was relying on forward-declared properties, because they didn’t work right in Z-code.

That’s great news!

Was the issue with check_property_operator(), as you expected? If so, will you provide a thumbnail sketch of the cause? If not, where was it? (It’s not an idle question; I’ve been trying to better understand the workings of the compiler.)

See the changes in this pull request:

Thank you, and thanks for the fix!