On 20 Nov 2002 at 14:03, CBFalconer wrote:
There is still the problem of untangling nested variants.
True, but this is a matter of adding a range check on each of the variant selectors. One could, for example, declare a type with ten nested variants, and then an access to a field of the most deeply nested variant would involve ten sequential range checks before permitting the access, but that is what is necessary to detect the potential error. It would be slow, but it's still straightforward. (As a matter of practicality, I don't recall ever using more than two nested variants, and I would guess that the typical case is one variant, so the typical speed penalty would be nothing like the unusual case offered above.)
And the multiple ranges per variant, as pointed out by Emil, complicates that variant selector check, unless I am again confused.
No, it does indeed complicate the issue, as multiple (disjoint) checks need to be made.
But consider a "regular" (executable) CASE statement appearing in the body of a program. To decide whether a specific contained statement is to be executed, the same sequence of possibly disjoint checks must be made. The only difference between this and the variant access situation is the action taken after the check: a transfer of execution or an allowed field access. So the checking code already exists in GPC; it "simply" :-) needs to be emitted in association with a variant field access.
This means the variant check has to be something like (variation on your code, snipped)....
Essentially, yes, although I would imagine that the loop would be unrolled to perform sequential checks to improve speed. The GNAT (Ada) compiler actually puts the selector-checking code for a specific variant into a compiler-generated function, so that accesses to any fields contained within that variant may be checked by calling the common routine (i.e., avoiding the replication of checking code at each field reference within the program).
It is still a lot of code to emit to store a single char in a variant field! :-)
It certainly does involve extra code, to be sure. How much code is involved depends on the checks required and the processor instruction set. For example, the Motorola 68020 (a GPC target) can do an interval range check with a single instruction, so using your original example, the 68020 code for:
rcd.twobyte := something;
...might be (where D0 is a machine data register, "#" designates a numeric literal, and "Bxx" are "branch if tested condition is true" instructions):
MOVE a,D0 \ CMP #1,D0 > variant-checking code BNE variant_error /
MOVE something,D0 \ CMP2 bounds,D0 > range-checking code BCS range_error > ("bounds" are "-5,512" in this case) MOVE D0,twobyte /
Contrast this with the non-range-checking case:
MOVE something,twobyte
Certainly, more code is required, but not horrendously more code.
But consider:
r = RECORD CASE i : integer OF 0..1000, 10000: (c : char); -9999, 3000..4000: (j : integer); END; (* r *)
OK, now the equivalent 68020 variant-checking code becomes:
MOVE i,D0 CMP #-9999,D0 BEQ ok CMP2 bounds,D0 ; bounds are "3000,4000" BCS variant_error ok:
More complicated ranges would require correspondingly more code, but I don't see that the problem is intractable. As you have said, providing range-checking is of significant benefit. It appears to me that the benefit can be extended to variant-checking with little additional complication.
-- Dave