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^";
with name 'battery',
Room Start "Starting Point"
"An uninteresting room.";
* '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
Release 1 / Serial number 210418 / Inform v6.34 Library 6/11 S
An uninteresting room.
>SQUINT AT ME
[** 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.
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:
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
Object fuel_cell "fuel cell"
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
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 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).