Spurious RTE when calling child() of a variable holding a class object?

Here’s some code that appears to be considered problematic by compilers 6.31 through 6.35:

Constant Story "Child of variable holding class";
Constant Headline "^an example^";

Include "Parser";
Include "VerbLib";
Include "Grammar";

Class Room
    has light;

Class Battery(5)
    with    name 'battery',
            capacity 50;

Room Start "Starting Point"
    with    description
                "An uninteresting room.";

Verb    'squint'
    * 'at' noun -> Squint;

! Trace assembly 4;
[ SquintSub    prot cla ;
    cla = Battery;
    prot = child(cla);  ! RTE triggered by this line
! Trace assembly off;

[ Initialise ;

    location = Start;
    print "The child of Battery is: ", (name) child(Battery), ".^";


This is what the issue arising looks like, an RTE that does not appear to have a valid cause:

The child of Battery is: Battery_1.   ! <---- no problem when requesting child of class as constant
Child of variable holding class
an example
Release 1 / Serial number 210418 / Inform v6.34 Library 6/11 S

Starting Point
An uninteresting room.


[** Programming error: tried to find the "child" of Battery **]   ! <--- problem when requesting child of same class

Examination of the assembly trace shows that the RTE is being generated as part of what looks like hardcoded guards inserted by the compiler:

   26  +0d150  [ SquintSub prot cla 

   27  +0d151 <*> store        cla short_26 
                               0d 02 1a 
   28  +0d154 <*> jg           short_5 cla to L1 if TRUE    <--- ensures not checking child of Class, Object, String or Routine
                               23 05 02 80 01 
   28  +0d159     jg           cla long_12 to L1 if TRUE    <--- PROBLEM LINE - unknown purpose
                               c3 8f 02 00 0c 80 01 
   28  +0d160     jin          cla short_1 to L0 if FALSE
                               46 02 01 00 00 
   28  +0d165    .L1
   28  +0d165     call_vn      long_22 short_6 cla          <--- equiv to RT__Err(6); "tried to find a child of..." (?) 
                               f9 1b 00 16 06 02 
   28  +0d16b     store        TEMP2 short_2 
                               0d fe 02 
   28  +0d16e     jump         L2 
                               8c 00 02 
   28  +0d171    .L0
   28  +0d171     store        TEMP2 cla 
                               2d fe 02 
   28  +0d174    .L2
   28  +0d174     get_child    TEMP2 -> prot (no branch)
                               a2 fe 01 42 
   29  +0d178 <*> rtrue        

The RTE does not appear if the code is compiled with strict mode off.

Does anyone know what the intent of the line marked “PROBLEM LINE” is? The instruction is equivalent to “jump to L1 if cla > 12”, but the significance of 12 in this context isn’t clear.

This code is emitted by check_nonzero_at_runtime_z in expressc.c. The mysterious 12 is the index of system constant actual_largest_object_SC, and is replaced later in compilation with the actual number of the largest object (here 34). It’s actually the following line ( jin cla short_1 to L0 if FALSE) that’s triggering the runtime error - it’s specifically checking whether the variable points to a class object.

The discrepancy here is that for child(), check_nonzero_at_runtime_z allows references to classes when the value is a constant , but not when the value is a variable.

1 Like

Ah, then my diagnosis was incorrect. I didn’t check backpatching because the instruction code indicated long_12 was a constant, and I didn’t notice that the RTE could be reached by passing the following test. Another lesson learned, and some insight into the compiler’s code generation, too – thank you very much, @msww (Mysterious Stranger with Wisdom?).

Are you saying that the real problem is that there’s not a guard against a call to child() when a constant value is supplied? If so, what is being guarded against that would be considered a problem?

EDIT: For background, the reason that I encountered this was that I was trying to write a routine to differentiate between objects that are part of a class’s “pool” and those that are not. For example:

Class Battery(5)...

creates a pool of 5 battery objects, plus a sixth to be the class prototype. But it’s also possible to create another object inheriting from Battery, e.g.

Object fuel_cell "fuel cell"
    class Battery...

and this object would not be part of the pool.

The method I hit upon was to note the value of the “pool count” by using Class.remaining() within Initialise() and then use the range (class prototype ID - pool count) to (class prototype ID) as an indicator of being “within pool” for a given object.

I have been wracking my brain trying to deduce a reason for the strict mode guard to have been put in place. The only thing that I can think of is that it’s part of the game of pretend that I6 is engaging in regarding its class system – in other words, it wants to pretend that classes can never have children.

I know from experience that it’s certainly possible to iterate through objects to check whether their parent is a class object, which is functionally the same test at the cost of more CPU work. And, as seen, there is no comparable guard for checking a constant value. So what’s the point?

The I6 manual says:

It is incorrect to apply these functions [child(), parent(), etc] to the value nothing , since it is not an object.
You get a similar error message if you try to apply these tree functions to a routine, string or class.

1 Like

The Inform Technical manual documents all the object-tree-navigation system functions (including child(x)) as only being defined when metaclass(x) == Object, so it looks like the check not catching calling child on classes provided as a constant value is unintentional.

Well, then the RTE isn’t spurious and is fair behavior with respect to the rules as stated in DM4. Thank you for the reminder about that passage; I had forgotten about it after crawling through veneer code for a while (where this stricture is ignored because different rules apply).