Reputation: 76567
In the following code the record constructor does something strange.
It works OK in all instances, except in the line marked below:
program Project9;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.Generics.Collections;
type
TIntegerPair = TPair<Integer, Integer>;
type
TMiniStack<T> = record
public
Items: array[0..10] of T;
SP: integer;
procedure Init;
procedure PushInline(const Item: T); inline;
procedure PushNormal(const Item: T);
end;
procedure TMiniStack<T>.Init;
begin
FillChar(Items, SizeOf(Items), #0);
SP:= 0;
end;
procedure TMiniStack<T>.PushInline(const Item: T);
begin
Items[SP]:= Item;
Inc(SP);
end;
procedure TMiniStack<T>.PushNormal(const Item: T);
begin
Items[SP]:= Item;
Inc(SP);
end;
procedure RecordConstructorFail;
var
List1: TMiniStack<TIntegerPair>;
List2: array[0..2] of TIntegerPair;
Pair: TIntegerPair;
a: string;
begin
Writeln('start test...');
FillChar(List1, SizeOf(List1), #0);
List1.Init;
List1.PushInline(TIntegerPair.Create(1, 1));
List1.PushInline(Pair.Create(2, 2)); <<--- Failure
List2[0]:= TIntegerPair.Create(1, 1);
List2[1]:= Pair.Create(2, 2);
if (List1.Items[0].Key <> 1) or (List1.Items[1].Key <> 2) then Writeln('something is wrong with List1-Inline');
if (List2[0].Key <> 1) or (List2[1].Key <> 2) then Writeln('something is wrong with List1');
List1.Init;
List1.PushNormal(TIntegerPair.Create(1, 1));
List1.PushNormal(Pair.Create(2, 2));
if (List1.Items[0].Key <> 1) or (List1.Items[1].Key <> 2) then Writeln('something is wrong with List1-Normal');
Writeln('Done');
Readln(a);
Writeln(a);
end;
begin
RecordConstructorFail;
end.
Why does this line cause a failure?
List1.PushInline(Pair.Create(2, 2)); <<- Failure: Dumps the data somewhere else.
Is it a compiler bug?
Or am I missing something?
I'm using Delphi XE6.
Upvotes: 1
Views: 286
Reputation: 612954
In my view, inlined or not, an expression that is a constructor call on an instance does not evaluate to a value. And so cannot be passed as an argument. Think of
Pair.Create(...)
as though it is a procedure. It yields no value.
In my view the code should be rejected by the compiler and the fact that it is not rejected is the bug.
That the code appears to work when you don't inline it is, in my view, down to chance.
This is all guesswork though. Record constructors aren't properly documented. The documentation for constructors is for classes and has not been updated to cover records. The documentation for classes says that the instance form is an expression that is a reference to the instance.
When a constructor is called using an object reference (rather than a class reference), it does not create an object. Instead, the constructor operates on the specified object, executing only the statements in the constructor's implementation, and then returns a reference to the object.
But this doesn't make much sense for a record because there are value types rather than reference types.
My best guess is that the parser regards this expression as being the same type as the instance, because that's how it is for constructors of classes. But the codegen doesn't actually yield a value.
Upvotes: 2