Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Die Delphi-IDE (https://www.delphipraxis.net/62-die-delphi-ide/)
-   -   Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt (https://www.delphipraxis.net/214224-delphi-12-enum-value-record-per-implicit-operator-ist-kaputt.html)

TiGü 7. Dez 2023 16:16

Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Hallo zusammen,

ich bin gerade dabei Delphi 12 zu installieren und einzurichten und bin über ein Stück Code in unserer Software gestolpert, welches Probleme bereitet.
Anscheinend hat der Compiler eine Regression erfahren.
Der Original-Code mappt einen Enumerationstyp (im Beispiel unten
Delphi-Quellcode:
TMyEnum
) mit weit über 256 Elementen in ein Hilfs-Record, welches ein
Delphi-Quellcode:
Set of TMyEnum
ersetzt (weil eine Menge nicht mehr als 256 Elemente aufnehmen kann).
Im Beispiel sind es weniger Enum-Elemente, weil es für das Problem egal ist.

Dieses Stückchen Quelltext hat im Laufe der Jahre (eher Jahrzehnte) gut funktioniert.
Ein leeres Array mit [] wird dem Record zugewiesen und per Implicit-Operator wird es intern umgewandelt.
Siehe
Delphi-Quellcode:
procedure Main
im unteren Beispiel.
In Delphi 11.3 funktioniert das zuweisen per Implicit-Operator sehr schön ohne Probleme.
In Delphi 12 bekomme ich im
Delphi-Quellcode:
procedure TMyEnumWrapper.Create(const a: array of TMyEnum);
einen
Delphi-Quellcode:
ERangeError
bei Auswertung des Low()/High().

Wie seht ihr das, sollte ich einen Bug im Emba Jira anlegen?

Delphi-Quellcode:
program EnumToRecordImplicitBroken;

{$APPTYPE CONSOLE}
{$R *.res}

uses
    System.SysUtils;

type
    TMyEnum = (entry_1, entry_2, entry_3, entry_4, entry_5, entry_6, entry_7, entry_8, entry_9, entry_10, entry_11, entry_12,
      entry_13, entry_14, entry_15, entry_16, entry_17, entry_18, entry_19, entry_20, entry_21, entry_22, entry_23, entry_24,
      entry_25, entry_26, entry_27, entry_28, entry_29, entry_30, entry_31, entry_32, entry_33, entry_34, entry_35, entry_36,
      entry_37, entry_38, entry_39, entry_40, entry_41, entry_42, entry_43, entry_44, entry_45, entry_46, entry_47, entry_48,
      entry_49, entry_50, entry_51, entry_52, entry_53, entry_54, entry_55, entry_56, entry_57, entry_58, entry_59, entry_60,
      entry_61, entry_62, entry_63, entry_64, entry_65, entry_66, entry_67, entry_68, entry_69, entry_70, entry_71, entry_72,
      entry_73, entry_74, entry_75, entry_76, entry_77, entry_78, entry_79, entry_80, entry_81, entry_82, entry_83, entry_84,
      entry_85, entry_86, entry_87, entry_88, entry_89, entry_90, entry_91, entry_92, entry_93, entry_94, entry_95, entry_96,
      entry_97, entry_98, entry_99, entry_100, entry_101, entry_102, entry_103, entry_104, entry_105, entry_106, entry_107,
      entry_108, entry_109, entry_110, entry_111, entry_112, entry_113, entry_114, entry_115, entry_116, entry_117, entry_118,
      entry_119, entry_120, entry_121, entry_122, entry_123, entry_124, entry_125, entry_126, entry_127, entry_128, entry_129,
      entry_130, entry_131, entry_132, entry_133, entry_134, entry_135, entry_136, entry_137, entry_138, entry_139, entry_140,
      entry_141, entry_142, entry_143, entry_144, entry_145, entry_146, entry_147, entry_148, entry_149, entry_150, entry_151,
      entry_152, entry_153, entry_154, entry_155, entry_156, entry_157, entry_158, entry_159, entry_160, entry_161, entry_162,
      entry_163, entry_164, entry_165, entry_166, entry_167, entry_168, entry_169, entry_170, entry_171, entry_172, entry_173,
      entry_174, entry_175, entry_176, entry_177, entry_178, entry_179, entry_180, entry_181, entry_182, entry_183, entry_184,
      entry_185, entry_186, entry_187, entry_188, entry_189, entry_190, entry_191, entry_192, entry_193, entry_194, entry_195,
      entry_196, entry_197, entry_198, entry_199, entry_200);

    TMyEnumWrapper = record
        FMyEnumSet: array [0 .. (Ord(High(TMyEnum)) div 32) + 1] of Cardinal;

        function ToString: string;
        procedure IndexOf(const a: TMyEnum; out Index: Integer; out Mask: Cardinal); inline;
        procedure SetItem(const a: TMyEnum); inline;

        procedure Create(); overload;
        procedure Create(const a: TMyEnum); overload;
        procedure Create(const a: array of TMyEnum); overload;

        class operator Implicit(const a: array of TMyEnum): TMyEnumWrapper; overload;
        class operator Implicit(const a: TMyEnum): TMyEnumWrapper; overload;

        class operator In (const a: TMyEnum; const b: TMyEnumWrapper): Boolean;
    end;

class operator TMyEnumWrapper.Implicit(const a: array of TMyEnum): TMyEnumWrapper;
begin
    // in Delphi 12 ist das im Local Variables Window ein "Inaccessible value" -> da fängt der Fehler schon an
    // in Delphi 11.3 steht im Local Variables Window ein "()" -> richtig und schön
    Result.Create(a);
end;

procedure TMyEnumWrapper.Create;
begin
    FillChar(Self, sizeof(TMyEnumWrapper), 0);
end;

procedure TMyEnumWrapper.Create(const a: array of TMyEnum);
var
    i: Integer;
begin
    Create();
    for i := Low(a) to High(a) do
    begin
        SetItem(a[i]);
    end;
end;

procedure TMyEnumWrapper.Create(const a: TMyEnum);
begin
    Create();
    SetItem(a);
end;

class operator TMyEnumWrapper.Implicit(const a: TMyEnum): TMyEnumWrapper;
begin
    Result.Create();
end;

class operator TMyEnumWrapper.In(const a: TMyEnum; const b: TMyEnumWrapper): Boolean;
var
    i: Integer;
    m: Cardinal;
begin
    b.IndexOf(a, i, m);
    Result := (b.FMyEnumSet[i] and (m)) <> 0;
end;

procedure TMyEnumWrapper.IndexOf(const a: TMyEnum; out Index: Integer; out Mask: Cardinal);
var
    i: Integer;
begin
    i := Ord(a);
    Index := (i div 32);
    Mask := $01 shl (i - (Index * 32)); // Maske (Bit)
end;

procedure TMyEnumWrapper.SetItem(const a: TMyEnum);
var
    i: Integer;
    m: Cardinal;
begin
    IndexOf(a, i, m);
    FMyEnumSet[i] := (FMyEnumSet[i] or m);
end;

function TMyEnumWrapper.ToString: string;
var
    i: Integer;
begin
    Result := '';

    for i := 0 to Length(self.FMyEnumSet) - 1 do
    begin
        if (Result <> '') then
            Result := Result + ', ';
        Result := Result + Ord(FMyEnumSet[i]).ToString;
    end;
end;

procedure Main;
var
    MyEnumWrapperVar: TMyEnumWrapper;

begin
    MyEnumWrapperVar := [];
    Writeln(MyEnumWrapperVar.ToString);
end;

begin
    try
        Main;
    except
        on E: Exception do
            Writeln(E.ClassName, ': ', E.Message);
    end;
    readln;

end.

Uwe Raabe 7. Dez 2023 16:49

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Wichtiger Hinweis fehlt: Passiert nur bei Target = Win64!

TiGü 8. Dez 2023 09:24

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Ja, stimmt! Guter Hinweis.
Ich debugge meistens in der Zielplattform Win64.

Das Problem muss irgendwie mit dem Implicit-Operator zu tun haben, denn eine reine Funktion geht ohne Probleme in 64-Bit.

Delphi-Quellcode:
function Convert(const a: array of TMyEnum): TMyEnumWrapper;
begin
    Result.Create(a);
end;

Stevie 10. Dez 2023 05:17

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Da haben sie wohl beim open array 64-bit machen ne Stelle im Compiler vergessen.
Reported: https://quality.embarcadero.com/browse/RSP-43656

TiGü 12. Dez 2023 14:28

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Zitat:

Zitat von Stevie (Beitrag 1530622)
Da haben sie wohl beim open array 64-bit machen ne Stelle im Compiler vergessen.
Reported: https://quality.embarcadero.com/browse/RSP-43656

Vielen lieben Dank für den vereinfachten Testfall und das reporten!

@Alle: Bitte für den RSP voten! :thumb:

Sherlock 12. Dez 2023 15:18

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Abgestimmt!

Ich bin ja sowas von beunruhigt, daß die bei Embarcadero Unit Tests nicht besser machen als ich.

TiGü 13. Dez 2023 15:47

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Hier die generierten Debug Win64 Disassembler-Views zwischen begin und end des TRec.Implicit-Operators.
Sieht schon blöd anders aus unterhalb der for-Schleife.

Delphi-Quellcode:
// Delphi 11.3
SteviesExample.dpr.20: begin
000000000042BC50 55               push rbp
000000000042BC51 4883EC10         sub rsp,$10
000000000042BC55 488BEC          mov rbp,rsp
000000000042BC58 895528           mov [rbp+$28],edx
SteviesExample.dpr.24: for i := Low(values) to High(values) do
000000000042BC5B 33C0             xor eax,eax
000000000042BC5D 8B4D28           mov ecx,[rbp+$28]
000000000042BC60 894508           mov [rbp+$08],eax
000000000042BC63 394D08           cmp [rbp+$08],ecx
000000000042BC66 7F10             jnle TRec.&op_Implicit + $28
000000000042BC68 2BC8             sub ecx,eax
000000000042BC6A 83C101           add ecx,$01
SteviesExample.dpr.28: end;
000000000042BC6D 83450801         add dword ptr [rbp+$08],$01
SteviesExample.dpr.24: for i := Low(values) to High(values) do
000000000042BC71 83E901           sub ecx,$01
000000000042BC74 85C9             test ecx,ecx
000000000042BC76 75F5             jnz TRec.&op_Implicit + $1D
000000000042BC78 90               nop
SteviesExample.dpr.31: end;
000000000042BC79 8B450C          mov eax,[rbp+$0c]
000000000042BC7C 488D6510         lea rsp,[rbp+$10]
000000000042BC80 5D              pop rbp
000000000042BC81 C3               ret


// Delphi 12
SteviesExample.dpr.20: begin
000000000042C4C0 55               push rbp
000000000042C4C1 4883EC30         sub rsp,$30
000000000042C4C5 488BEC          mov rbp,rsp
000000000042C4C8 48895548         mov [rbp+$48],rdx
SteviesExample.dpr.24: for i := Low(values) to High(values) do
000000000042C4CC 488B4548         mov rax,[rbp+$48]
000000000042C4D0 B900000080       mov ecx,$80000000
000000000042C4D5 488D0C01         lea rcx,[rcx+rax]
000000000042C4D9 BAFFFFFFFF      mov edx,$ffffffff
000000000042C4DE 483BCA          cmp rcx,rdx
000000000042C4E1 7605             jbe TRec.&op_Implicit + $28
000000000042C4E3 E8E8B8FDFF      call @BoundErr
000000000042C4E8 33C9             xor ecx,ecx
000000000042C4EA 894D28           mov [rbp+$28],ecx
000000000042C4ED 394528           cmp [rbp+$28],eax
000000000042C4F0 7F10             jnle TRec.&op_Implicit + $42
000000000042C4F2 2BC1             sub eax,ecx
000000000042C4F4 83C001           add eax,$01
SteviesExample.dpr.28: end;
000000000042C4F7 83452801         add dword ptr [rbp+$28],$01
SteviesExample.dpr.24: for i := Low(values) to High(values) do
000000000042C4FB 83E801           sub eax,$01
000000000042C4FE 85C0             test eax,eax
000000000042C500 75F5             jnz TRec.&op_Implicit + $37
000000000042C502 90               nop
SteviesExample.dpr.31: end;
000000000042C503 8B452C          mov eax,[rbp+$2c]
000000000042C506 488D6530         lea rsp,[rbp+$30]
000000000042C50A 5D              pop rbp
000000000042C50B C3               ret

TiGü 14. Dez 2023 07:36

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Könnte das damit zusammenhängen?
Die Stichwörter sind alle da:

https://docwiki.embarcadero.com/RADS...y_Improvements
Zitat:

The hidden "high bound" parameter type for an open array parameter was changed from Integer to NativeInt, an alias of Integer or Int64, depending on the target CPU. This means that Length, High, and Low for open array parameters in 64-bit compilers now return 64-bit value.

Stevie 7. Feb 2024 13:42

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
RSP-43656 taucht zumindest in der Liste der in Patch 1 gefixten Issues auf

TiGü 13. Feb 2024 07:49

AW: Delphi 12: Enum Value to Record per Implicit-Operator ist kaputt
 
Zitat:

Zitat von Stevie (Beitrag 1533150)
RSP-43656 taucht zumindest in der Liste der in Patch 1 gefixten Issues auf

Kann ich als gefixt bestätigen. :thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz