In Relations.i6t
there is a routine RelationEmptyOtoO()
. Its function is to either test whether a 1-to-1 relation is empty (i.e. return true if no assertions exist and false if at least one exists) or, if the clear
flag is set, to negate all assertions. Here is the routine as seen in 6M62 with additional comments to label the four included loops:
unmodified version
[ Relation_EmptyOtoO relation sym clear relation_property obj1 obj2 t1 t2 N1 N2;
relation_property = RlnGetF(relation, RR_STORAGE);
t1 = KindBaseTerm(RlnGetF(relation, RR_KIND), 0); ! Kind of left term
t2 = KindBaseTerm(RlnGetF(relation, RR_KIND), 1); ! Kind of right term
if (t2 == OBJECT_TY) {
objectloop (obj2 provides relation_property) { ! loop 1
obj1 = obj2.relation_property;
if (obj1) {
if (clear) obj2.relation_property = nothing;
else rfalse;
}
}
} else {
for (obj2=1: obj2<=N2: obj2++) { ! loop 2
obj1 = GProperty(t2, obj2, relation_property);
if (obj1) {
if (clear) WriteGProperty(t2, obj2, relation_property, 0);
else rfalse;
}
}
}
if (t1 ~= t2) {
if (t1 == OBJECT_TY) {
objectloop (obj1 provides relation_property) { ! loop 3
obj2 = obj1.relation_property;
if (obj2) {
if (clear) obj1.relation_property = nothing;
else rfalse;
}
}
} else {
for (obj1=1: obj1<=N2: obj1++) { ! loop 4
obj2 = GProperty(t1, obj1, relation_property);
if (obj2) {
if (clear) WriteGProperty(t1, obj1, relation_property, 0);
else rfalse;
}
}
}
}
rtrue;
];
Note that the above is from 6M62, but the 10.1 version seems logically identical.
The I7 code
if <relation> is empty...
and
now <relation> is empty;
will not work correctly when the right side of the relation is an enumerated kind of value (KOV) and the left side is either an object
-descended type or a KOV. The type of failure will differ depending on the type of the left side.
Here is a demonstration of the effect on KOV <-> KOV relations:
"Bug Demonstration 1"
Place is a room.
A color is a kind of value. The colors are red, orange, yellow, green, blue, indigo and violet.
Counterpart relates one color to one color. The verb to counter means the counterpart relation.
When play begins:
show relation the counterpart relation; [looks empty]
if the counterpart relation is empty, say "Test 1: EMPTY."; [evals true -- OK]
now red counters blue; [OK]
now orange counters green; [OK]
show relation the counterpart relation; [looks correct]
if the counterpart relation is empty, say "Test 2: EMPTY."; [evals true-- incorrect]
now the counterpart relation is empty; [no effect]
show relation the counterpart relation; [previous assertions present]
if the counterpart relation is empty, say "Test 3: EMPTY."; [evals true - incorrect]
now yellow counters indigo; [OK]
show relation the counterpart relation; [now three assertions]
if the counterpart relation is empty, say "Test 4: EMPTY." [evals false - incorrect]
The failure to detect a non-empty relation and the failure to clear all assertions when the relation is made empty appear to be because the code is lacking initialization of local variable N2
. This means that neither loop 2 nor loop 4 are ever entered. Based on the pattern seen in other routines, N2
should be set using the KOVDomainSize()
routine before use as the gate condition for any loop. For example:
[ Relation_EmptyOtoO relation sym clear relation_property obj1 obj2 t1 t2 N1 N2;
relation_property = RlnGetF(relation, RR_STORAGE);
t1 = KindBaseTerm(RlnGetF(relation, RR_KIND), 0); ! Kind of left term
t2 = KindBaseTerm(RlnGetF(relation, RR_KIND), 1); ! Kind of right term
N2 = KOVDomainSize(t2); ! ADDED
if (t2 == OBJECT_TY) {
...
} else {
for (obj2=1: obj2<=N2: obj2++) { ! loop 2
...
}
}
if (t1 ~= t2) {
if (t1 == OBJECT_TY) {
...
} else {
for (obj1=1: obj1<=N2: obj1++) { ! loop 4
...
}
}
}
rtrue;
];
If these changes are made, then the “Bug Demonstration 1” scenario will work as expected.