Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2014-02

About typecasts and the documentation

5 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin Frb

http://www.freepascal.org/docs-html/ref/refse67.html#x124-13400012.4

In general, the type size of the expression and the size of the type cast must be the same. However, for ordinal types (byte, char, word, boolean, enumerates) this is not so, they can be used interchangeably. That is, the following will work, although the sizes do not match.

http://www.freepascal.org/docs-html/ref/refse68.html#x125-13500012.5

A variable can be considered a single factor in an expression. It can therefore be typecast as well. A variable can be typecast to any type, provided the type has the same size as the original variable.

In the below (first) example "longint(1)" and TFoo have the same size.

The below 2nd example shows that by printing
4
4
1
Surprisingly "SizeOf(1)" is 1, yet the error from the 1st example specified that 1 (inside the typecast) was of type longint which should give a size of 4.

Yet in the first example they fail. This is not covered by the documentation.
The doc even gives examples involving typecasting constant values.

Byte(’A’)

given that the doc then goes on, and gives

Integer(’A’);

that worked via the ordinal value (size does not match), it gives the impression that the first example was working under the rule of "same size".

But then the doc does not give any clue why same size can not be cast to a record.

program project1;
type
TFoo = record b:longint; end;
var
i: longint;
foo: TFoo;
begin
foo := TFoo(i);
foo := TFoo(longint(1)); // project1.lpr(9,8) Error: Illegal type
conversion: "LongInt" to "TFoo"
foo := TFoo(1); // project1.lpr(10,8) Error: Illegal type conversion:
"LongInt" to "TFoo"
end.
-------------------------------------
program project1;
type
TFoo = record b:longint; end;
var
i: longint;
foo: TFoo;
begin
foo := TFoo(i);
writeln(SizeOf(TFoo));
writeln(SizeOf(longint(1)));
writeln(SizeOf(1));
readln;
end.

Reply by email

Martin Frb
to Martin Frb
Martin Frb wrote
In the below (first) example "longint(1)" and TFoo have the same size.

....

Show quoted text 12 lines
Yet in the first example they fail. This is not covered by the documentation. The doc even gives examples involving typecasting constant values. > Byte(’A’) given that the doc then goes on, and gives > Integer(’A’); that worked via the ordinal value (size does not match), it gives the impression that the first example was working under the rule of "same size". But then the doc does not give any clue why same size can not be cast to a record.
Even stranger, changing the example and the error adapts.
    project1.lpr(10,8) Error: Illegal type conversion: "ShortInt" to "TFoo"

So fpc does actually resize the constant (even ignoring that it is unsigned), and then complains....
(and yes SizeOf(TFoo) is 1)

{$A1}
program project1;
type
TFoo = record b:byte; end;
var
i: byte;
foo: TFoo;
begin
foo := TFoo(i);
foo := TFoo(byte(1));
foo := TFoo(1);
end.
Show quoted text 35 lines
program project1; type TFoo = record b:longint; end; var i: longint; foo: TFoo; begin foo := TFoo(i); foo := TFoo(longint(1)); // project1.lpr(9,8) Error: Illegal type conversion: "LongInt" to "TFoo" foo := TFoo(1); // project1.lpr(10,8) Error: Illegal type conversion: "LongInt" to "TFoo" end. ------------------------------------- program project1; type TFoo = record b:longint; end; var i: longint; foo: TFoo; begin foo := TFoo(i); writeln(SizeOf(TFoo)); writeln(SizeOf(longint(1))); writeln(SizeOf(1)); readln; end. _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

Hans-Peter Diettrich
to Martin Frb: http://www.freepascal.org/docs-html/ref/refse67.html#x124-13400012.4…
Show quoted text 10 lines
http://www.freepascal.org/docs-html/ref/refse67.html#x124-13400012.4 > In general, the type size of the expression and the size of the type > cast must be the same. However, for ordinal types (byte, char, word, > boolean, enumerates) this is not so, they can be used interchangeably. > That is, the following will work, although the sizes do not match. http://www.freepascal.org/docs-html/ref/refse68.html#x125-13500012.5 > A variable can be considered a single factor in an expression. It can > therefore be typecast as well. A variable can be typecast to any type, > provided the type has the same size as the original variable.

IMO type*cast* and type*conversion* should be kept separate. A cast then requires that the *size* is the same, while in a conversion the *value* stays the same. The compiler messages should reflect this difference (see your example below).

Usually "typecast" can have both meanings, in detail for value typecasts. Further terms are "type coercion", "type promotion".

Typecasts can be further restricted to *compatible* types. Here numeric types seem to be compatible with other numeric types, but not with structured types (records...). With classes sometimes a distinction between upcast and downcast is made (type *inclusion*), where up and down reflect more basic (ancestors) and more derived classes. Eventually this also applies to conversions between Char and numeric types, for which standard conversions Ord(c) and Chr(i) are defined.

It's not clear to me why a TStrings.Objects[i]:TObject can be compatible with e.g. integer:

   MyStringList.AddObject('1',TObject(1));

This may be due to some underlying implementation detail, where the list (array) contains pointers instead of objects, and these pointers then are compatible with numbers.

Often also multiple casts can be accepted, in something (untested) like
   MyObject := TObject(pointer(1));
or
   pointer(MyObject) := pointer(1);

IMO the detailed rules, as implemented in the compiler, are too complex for a simple description. That's why the docs only explain the syntax, not the full semantics behind the syntax.

Show quoted text 5 lines
foo := TFoo(longint(1)); // project1.lpr(9,8) Error: Illegal type conversion: "LongInt" to "TFoo" foo := TFoo(1); // project1.lpr(10,8) Error: Illegal type conversion: "LongInt" to "TFoo" end.

Obviously the types (record and numeric) are considered incompatible by the compiler.

DoDi

Reply by email

Martin Frb
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote
Typecasts can be further restricted to *compatible* types. Here numeric types seem to be compatible with other numeric types, but not with structured types (records...).

Well than that needs to be added to the docs. I have no problems with it ont working, I do not need it. What I do is, I do need to know what works, and how. And I need to know the rule behind.

Besides record and number are not incompatible:
TRecordType(IntVariableOfSameSize) does work.

Show quoted text 6 lines
It's not clear to me why a TStrings.Objects[i]:TObject can be compatible with e.g. integer: MyStringList.AddObject('1',TObject(1)); This may be due to some underlying implementation detail, where the list (array) contains pointers instead of objects, and these pointers then are compatible with numbers.

Because internally the TObject "is a pointer" . (well that is not correctly worded..., but a variable of type TObject internally holds a pointer)

Show quoted text 9 lines
> foo := TFoo(longint(1)); // project1.lpr(9,8) Error: Illegal type > conversion: "LongInt" to "TFoo" > foo := TFoo(1); // project1.lpr(10,8) Error: Illegal type conversion: > "LongInt" to "TFoo" > end. Obviously the types (record and numeric) are considered incompatible by the compiler.

Only if the number is a constant.

But a practical example:

type TFoo = packed record hi,lo: Byte; end;
var a: word;
if TFoo(a).hi = 1 then ; // that works
if TFoo($0101).hi = 1 then; // that does not

Reply by email

Michael Van Canneyt
to Martin Frb
Martin Frb wrote 8 lines
On 08/02/2014 21:59, Hans-Peter Diettrich wrote: > > Typecasts can be further restricted to *compatible* types. Here numeric > types seem to be compatible with other numeric types, but not with > structured types (records...). Well than that needs to be added to the docs. I have no problems with it ont working, I do not need it. What I do is, I do need to know what works, and how. And I need to know the rule behind.

The rules behind is quite complex, so much so, that I don't care to explain it. I doubt the compiler devels could explain it correctly. I also suspect it changes regularly.
Check the compiler sources if you want current behaviour.

The equal size rule comes closest. There may be some strange side-effects in cases of ordinal types, but that is it.

Michael.

Reply by email