为什么记录构造函数在内联函数中行为异常?

时间:2014-08-30 08:33:02

标签: delphi record compiler-bug

在下面的代码中,记录构造函数做了一些奇怪的事情 除了下面标记的行外,它在所有情况下都可以正常工作:

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.

为什么这一行导致失败?

List1.PushInline(Pair.Create(2, 2)); <<- Failure: Dumps the data somewhere else.

这是编译器错误吗? 或者我错过了什么?

我正在使用Delphi XE6。

1 个答案:

答案 0 :(得分:2)

在我看来,内联与否,在实例上构造函数调用的表达式不会计算为值。因此不能作为论据传递。想想

Pair.Create(...)
好像是一个程序。它没有产生任何价值。

在我看来,代码应该被编译器拒绝,并且不被拒绝的事实就是bug。

在我看来,代码似乎无法内联,但在我看来,这可能是偶然的。


但这完全是猜测。记录构造函数没有正确记录。构造函数的documentation用于类,并且尚未更新以涵盖记录。类的文档说实例表单是一个表达式,它是对实例的引用。

  

使用对象引用(而不是类引用)调用构造函数时,它不会创建对象。相反,构造函数对指定的对象进行操作,仅执行构造函数实现中的语句,然后返回对该对象的引用。

但是这对记录没有多大意义,因为有值类型而不是引用类型。

我最好的猜测是解析器将此表达式视为与实例相同的类型,因为它是如何为类的构造函数创建的。但是,codegen实际上并没有产生价值。