Overzealous implicit conversions (possible type checking bug)? lazarus / 64-FPC development ---------------------------------------------------------------------- H₂SO₄ · 2025-09-18 06:06 Yesterday I was caught off-guard when code containing a typo did not fail to compile, but instead silently led to a nonsensical chain of implicit conversions. Here's a minimal reproducible example (record/class fields and return values omitted for brevity; the bug still happens regardless): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Test; {$mode objfpc}{$H+} type  TEnum = (_);  TRecord = record end;  TClazz = class end; operator :=(Self: TEnum): String;begin  WriteLn(' enum -> String');end; operator :=(Self: Pointer): TRecord;begin  WriteLn(' Pointer -> record');end; operator =(const Self: TRecord; const Other: String): Boolean;begin  WriteLn(' (record = String)');end; function Main(): Boolean;var  Clazz: TClazz;  Enum: TEnum;begin  Result := (Clazz = Enum); // In my actual code I intended to write `Clazz.Enum = Enum` but forgot to access the fieldend; begin  Main();end. In my opinion, this code should fail to compile due to the comparison at line 30.  TClazz  defines no methods or operators, so it should be a dead-end. The compiler (trunk) disagrees, though: Quote$ fpc test.pas && ./test enum -> String Pointer -> record (record = String) Apparently  TClazz = Pointer  (???)  so that  Clazz  can be converted to  TRecord, which in turn can be compared to  String  (leading to  Enum  being stringified). Of course, it's so obvious...  The implicit conversion from a class type to  Pointer  here seems like a bug to me. On its own it makes sense (class instances are references after all), but I think it shouldn't apply in this situation. Normally at most one implicit conversion may be inserted by the compiler, but here it's two  (TClazz  →  Pointer  →  TRecord). Multiple implicit conversions in general are disallowed in most (all?) languages (and for good reason - imagine nonsense like  Boolean  →  PtrInt  →  Pointer  →  TForm). What do you think? ---------------------------------------------------------------------- ALLIGATOR · 2025-09-18 08:16 UPD: Although, of course, it's not quite the same thing. You have two transformations, I have one. Yes, it looks creepy But, by the way, if you try to rewrite it in Delphi style, you'll get roughly the same behavior, and Delphi itself (12.1CE) will compile it Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Test;{$ifdef FPC}{$mode delphi}{$endif} type  TR2 = record    class operator Implicit(A: TR2): String;    class operator Equal(A: TR2; B: String): Boolean;  end;  TR1 = record    class operator Implicit(A: TR1): TR2;  end;  class operator TR1.Implicit(A: TR1): TR2;begin  WriteLn(' TR1 -> TR2');end;class operator TR2.Implicit(A: TR2): String;begin  WriteLn(' TR2 -> String');end; class operator TR2.Equal(A: TR2; B: String): Boolean;begin  WriteLn(' (TR2 = String)');end; var  R1: TR1;  R2: TR2; begin  WriteLn (R1 = R2);  ReadLn;end. FPC & D12.1CE: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TR1 -> TR2 TR2 -> String (TR2 = String)