[fpc-devel] [Suggestion] Enumeration range-check intrinsic

Ondrej Pokorny lazarus at kluug.net
Wed Jul 3 01:38:38 CEST 2019


On 02.07.2019 23:34, Jonas Maebe wrote:
> On 02/07/2019 22:31, Ondrej Pokorny wrote:
>> This is similar to the object-is operator that gets evaluated as well
>> even if the type of the left-side value is the type at right side:
>>
>> var
>>    Value: TPersistent;
>> begin
>>    Value := TPersistent(TObject.Create);
>>    IsValid := Value is TPersistent; // IsValid gains false
> This is an invalid program. If you compile with -CR, the program will
> abort with an error when the typecast is performed, because it will get
> replaced by an "as" operation. In that sense, "integer as enum" would
> indeed be somewhat similar, and -CR might even be extended to perform
> the same replacement of explicit typecasts with "as" operators for these
> types.

No, this is a perfectly valid program with a perfectly defined behavior! 
It is a perfectly valid program even with -CR. When I use -CR I expect 
an exception and I will handle it - in this case I don't need the is-check:

var
   Value: TPersistent;
begin
   try
     Value := TPersistent(TObject.Create); // or: TObject.Create as 
TPersistent (without -CR)
   except
     Writeln('Wrong value supplied! Exit.');
     Exit;
   end;
   // do something
   Readln;
end.

A good real-word example is the cast from a pointer:

program Project1;
uses Classes;
var
   Value: TPersistent;
   P: Pointer;
begin
   P := TObject.Create;
   Value := TPersistent(P);
   if Value is TPersistent then
     Writeln('Value is TPersistent')
   else
     Writeln('Value is not TPersistent');
   Readln;
end.

> As an example of an operation on the resulting "Value" that is already
> undefined: if you would call a TPersistent virtual method on it, and
> whole-program optimization devirtualised that call, it may call the
> "correct" method of TPersistent instead of using the VMT of whatever
> other class instance type Value points to.

Yes, I agree that calling an (unavailable) method on an object casted to 
a wrong class is an invalid operation. But this is not what I did in my 
example. Preventing calls of unavailable methods or access of 
unavailable fields is exactly what I the IS-operator is for.


> Invalid data means undefined behaviour, always. "is" is not a special
> case that is immune to this.

We are again at the very fundamental question "what invalid data is". 
Storing a TObject in TPersistent is not an invalid operation IMO. Both 
are class objects and both can be checked for inheritance with the 
IS-operator. Calling methods, accessing fields etc. on the TObject-value 
in TPersistent-variable is invalid, indeed. But not a simple IS-operator.

What I understand as invalid for the IS-operator is e.g. trying to call 
it for an interface in TPersistent variable:

var
   Value: TPersistent;
   P: Pointer;
   I: IUnknown;
begin
   I := TInterfacedObject.Create;
   P := I;
   Value := TPersistent(P);
   if Value is TPersistent then

> And e.g. in the context of generics,
> simplifying/removing such checks where possible would probably be quite
> desirable.

Not really. The IS-operator returns false for NIL that is a valid value, 
so you cannot remove such checks:

var
   Value: TPersistent;
   IsValid: Boolean;
begin
   Value := nil;
   IsValid := Value is TPersistent; // IsValid gains false - you cannot 
simply replace this check with true
   Writeln(IsValid);
   Readln;
end.

Ondrej



More information about the fpc-devel mailing list