Inform 6/7 interop - mismatch w.r.t properties?

Hi! I’ve noticed a rather strange behavior with respect to how included code in Inform 7 (compiled from stable 10.1.2) translates down to Inform 6 - in particular, how properties get treated by the compiler.

So here’s what I’ve been experimenting with:

There is a room.

Include (-
[ access_prop_by_id obj id ;
	return obj.id;
]
-).

To access property (N - a number) on (O - an object): (- access_prop_by_id({O}, {N}); -).

After jumping: access property 1 on the player.

(Yes, I am aware this is quite pathological! This is just a minimum working example; I’m doing ostensibly safer stuff in my actual experiments (re: dynamic allocation).)

When invoking the bare I7 compiler on this, it produces a large amount of I6 code, but the two pertinent snippets are

! included code
[ access_prop_by_id obj id;
    return _final_propertyvalue(OBJECT_TY, obj, id);
];

! included as part of I7 boilerplate
[ _final_propertyvalue K o p t;
    if (K == OBJECT_TY) {
        if (metaclass(o) == Object) {
            t = p-->0; p = p-->1;
            if (t == 2) { if (o has p) rtrue; rfalse; }
            if (o provides p) return o.p;
        }
        rfalse;
    } else {
        t = value_property_holders-->K;
        return (t.(p-->1))-->(o+COL_HSIZE);
    }
];

I’m rather new to this rodeo, so I may be misinterpreting here… but if I’m not mistaken, this seems to be compiling Inform-6-in-Inform-7’s notion of “obj.id” into bare-metal-Inform-6’s “obj.(id–>1)”, plus or minus a few type checks.

This seems bizarre to me! Why does this happen?

1 Like

I’m also relatively new to this, but here’s my understanding:

The I6 language’s . operator takes as operands an object (roughly, a pointer into the array of simworld objects) and a property name. The property name is, in C terminology, an enumerator. That is, it’s a serial number, unique among all property names. It’s used as something sort of like an index into an array of fields that is stored in the object (I know that’s actually only true for “common” property, but we can pretend.)

If you’d written an I7 call to get a specific named property, the I7 code would be converted to find the I6 name of that property and the I6 . operator would be used directly. But the type you gave in the I7 is number, so I7 converts the number to an I6 thing that represents a property at runtime and then decides what operation to apply. I7 only has “properties”, making no distinction between yes/no and valued properties, but I6 does, so that thing looks to be an array with one field for the operation to use, and the other field being the serial number that the property name corresponds to, or in the case of a yes/no I7 property, the serial number of the I6 “attribute” serial number, which is an index into a bitmask, similar to but distinct from the index into an object’s field array.

I don’t know where you’d find the code that does the conversion from number to “property thing” array.

1 Like

The conceptual basis of properties in I7 is different from that of I6. In I6 only objects can have properties. In I7 even kinds of value (internally represented as integers) can have properties! Also, I7 tries to use I6 attributes (which, as sadiedemight notes, take one bit of storage) to track either-or properties.

The code that you’re looking at abstracts away the differences in representation of properties:

[ _final_propertyvalue K o p t;
	if (K == OBJECT_TY) {									! <-- is K supposed to be an object?
	    if (metaclass(o) == Object) {						! <-- double-check
	        t = p-->0; p = p-->1;							! <-- get stored property type indicator (?)
	        if (t == 2) { if (o has p) rtrue; rfalse; }		! <-- if type indicator was 2, check I6 attributes of o...
	        if (o provides p) return o.p;					! <-- ... otherwise check I6 properties of o
	    }
	    rfalse;												! <-- default to 0/false
	} else {												! <-- K is not an object, it's a kind of value or similar...
	    t = value_property_holders-->K;						! <-- ... so look for associated VPH, then ...
	    return (t.(p-->1))-->(o+COL_HSIZE);					! <-- ... return property value from VPH
	}
];

A VPH is a “value property holder,” which is an object constructed by the I7 compiler to hold properties on behalf of non-objects. (As an example, if you say Color is a kind of value. Some colors are red, green and blue. A color can be bright. Red is bright., then a VPH for red is constructed to hold the bright property. (EDIT: Actually, I’m pretty sure that the VPH holds property data for all values of color in that example, since it needs to know that green and blue are not bright. The value of o in the array access would be the integer representing a particular color, used as an index with offset.)

I haven’t spent a lot of time in 10.1.2, so I’m not sure about the details of representation here, but it looks like whatever is being passed originally as p (to represent the property) is not an I6 property ID – apparently, it is a structure storing the I6 property ID at -->1 and some sort of type indicator at -->0.

I know that @drpeterbatesuk has been looking into this subsystem in 10.1.2, so he may have more to offer.

2 Likes

That’s correct. In Ver 10, the I7 compiler compiles an array of metadata regarding each object property/attribute type and _final_propertyvalue() is called to retrieve the value of a given object’s property/attribute as _final_propertyvalue(OBJECT_TY, some_object, some_property/attribute_metadata_array).

_final_propertyvalue() then examines the property metadata array to see if the given I7 object property has been implemented as an I6 attribute or as an I6 property and accordingly returns either the property value using the I6 object.property idiom or the attribute value using the I6 object has attribute idiom.