Подтвердить что ты не робот

Инициализация Delphi Record, содержащая динамический массив с оператором класса "Неявный"

Я пытаюсь выяснить, можно ли инициализировать запись, содержащую динамический массив, с помощью оператора "неявного" класса в Delphi (Berlin 10.1 upd 1)

Прилагаемая программа производит следующий вывод:

ci iA r1 r2 r3
1  1  1  1  49694764491115752
2  2  2  2  11570520
3  3  3  3  0
4  4  4  4  0
5  5  5  5  0
  • TRec - это тип записи, содержащий динамический массив, который я хочу инициализировать.
  • ci - это постоянный массив целого числа.
  • ia - это динамический массив целых чисел.
  • r1, r2, r3 - это записи типа TRec, которые инициализируются разными способами.

Как видно из вывода, первые два назначения (r1, r2), использующие константы, работают как ожидалось. Третье назначение r3 := iArray принимается компилятором, но результат не работает. Отладчик показывает, что значение v в TRec.Implicit уже неверно.

Что здесь происходит? Это вообще возможно?

program Project5;

{$APPTYPE CONSOLE}

{$R *.res}

type
  TRec = record
    iArray: array of UInt64;
    class operator Implicit(const v: array of UInt64): TRec;
  end;

{ TRec }

class operator TRec.Implicit(const v: array of UInt64): TRec;
var
  i: integer;
begin
  setlength(Result.iArray, Length(v));
  for i := 0 to High(v) do
    Result.iArray[i] := v[i];
end;

const
  ciArray: array [0 .. 4] of UInt64 = (1, 2, 3, 4, 5);

var
  i         : integer;
  iArray    : array of UInt64;
  r1, r2, r3: TRec;

begin
  iArray := [1, 2, 3, 4, 5];

  r1 := [1, 2, 3, 4, 5];
  r2 := ciArray;
  r3 := iArray;

  Writeln('ci iA r1 r1 r3');
  for I := 0 to High(ciArray) do
    Writeln(ciArray[i], '  ', iArray[i], '  ', r1.iArray[i], '  ', r2.iArray[i], '  ', r3.iArray[i]);

  readln;

end.
4b9b3361

Ответ 1

Похоже, вы обнаружили ошибку с кодегеном там (и она также существует в компиляторе Win64). Я просмотрел сгенерированный asm, и, похоже, компилятор производит неверную инструкцию для перегрузки оператора. Вот почему неправильные значения попадают в массив внутри перегрузки оператора. Сообщите об этом на Портале качества.

Сгенерированный код для плохого результата:

Project109.dpr.46: r3 := iArray;
0040B1F2 A1FC044100       mov eax,[$004104fc]
0040B1F7 8945E8           mov [ebp-$18],eax
0040B1FA 837DE800         cmp dword ptr [ebp-$18],$00
0040B1FE 740B             jz $0040b20b
0040B200 8B45E8           mov eax,[ebp-$18]
0040B203 83E804           sub eax,$04
0040B206 8B00             mov eax,[eax]
0040B208 8945E8           mov [ebp-$18],eax
0040B20B 8D4DD8           lea ecx,[ebp-$28]
0040B20E 8B55E8           mov edx,[ebp-$18]
0040B211 4A               dec edx
0040B212 B8FC044100       mov eax,$004104fc // <-- wrong one
0040B217 E87CF5FFFF       call TRec.&op_Implicit

Код для равного метода:

Project109.dpr.47: r3 := TRec.Implicit(iArray);
0040B22F A1FC044100       mov eax,[$004104fc]
0040B234 8945E4           mov [ebp-$1c],eax
0040B237 837DE400         cmp dword ptr [ebp-$1c],$00
0040B23B 740B             jz $0040b248
0040B23D 8B45E4           mov eax,[ebp-$1c]
0040B240 83E804           sub eax,$04
0040B243 8B00             mov eax,[eax]
0040B245 8945E4           mov [ebp-$1c],eax
0040B248 8D4DD4           lea ecx,[ebp-$2c]
0040B24B 8B55E4           mov edx,[ebp-$1c]
0040B24E 4A               dec edx
0040B24F A1FC044100       mov eax,[$004104fc] // <-- correct one
0040B254 E8CFF5FFFF       call TRec.Implicit

Однако вы можете избежать этого, добавив еще одну перегрузку для оператора Implicit с типом параметра TArray<UInt64>, а затем также объявите свою локальную переменную как этот тип, чтобы компилятор выбрал правильную перегрузку (тот, в котором он не генерирует неправильный код для в этом случае).

Но имейте в виду, что это будет работать только тогда, когда вы передадите переменные типа TArray<UInt64> и вызовите неправильный, когда у вас есть другой динамический array of UInt64 из-за правил строгих типов Delphis.

Обновление. Этот дефект был указан в RSP-16084 и исправлен в Delphi 10.2 Tokyo.