Skip to content

Linking error with generics: undefined reference to `SYSTEM$_$TOBJECT_$__$$_GETHASHCODE$$<unknown type>'

Summary

Compiling and linking a generic procedure where the type has a count causes a linker error, even though the generic procedure is unused.

System Information

  • Operating system: Linux, x86_64, OpenSuse Leap 15.6 (also on AlmaLinux 9.4)
  • Processor architecture: x86-64, 64-bit
  • Compiler version: trunk, 3.3.1, 3.3.1-16505-gfdae200281
  • Device: Desktop Personal Computer

Steps to reproduce

Given these three source files:

unit_a.pas

unit unit_a;

interface

type
  tLoopAll     = reference to procedure (const i: integer);
  tMemberSeqId = reference to procedure (const member: pointer; const seqId: integer);

procedure loop (const start: integer; const final_value: integer; const loopPr: tLoopAll);
generic procedure loop<T> (const list: T; const doItem: tMemberSeqId);

implementation


procedure loop (const start: integer; const final_value: integer; const loopPr: tLoopAll);
  procedure forward_iterate (i: integer = 0);
    begin
      for i := start to final_value do loopPr(i);
    end;

  procedure reverse_iterate (i: integer = 0);
    begin
      for i := start downto final_value do loopPr(i);
    end;

  begin
    if final_value < start then reverse_iterate
    else
      forward_iterate;
  end;

generic procedure loop<T> (const list: T; const doItem: tMemberSeqId);
  begin
    loop(0, list.count - 1, procedure (const i: integer)
    begin
      doItem(list.items[i], i);
    end);
  end;

end.
// CudaText: lexer_file=Pascal; tab_size=2; tab_spaces=Yes; newline=LF;

unit_b.pas

unit unit_b;

interface

type
  tProc  = reference to procedure;
  tProcs = array of tProc;

procedure pick_all (const procs: tProcs);

implementation

uses unit_a;

procedure pick_all (const procs: tProcs);
  begin
    if length(procs) > 0 then loop(low(procs), high(procs), procedure (const i: integer)
      begin
        procs[i]();
      end);
  end;

end.
// CudaText: lexer_file=Pascal; tab_size=2; tab_spaces=Yes; newline=LF

appmain.pas

program appmain;

uses unit_b;

begin
end.
// CudaText: lexer_file=Pascal; tab_size=2; tab_spaces=Yes; newline=LF;

When compiling it the following occurs:

$ fpc -Mobjfpc -Sh -Fcutf8 -MadvancedRecords -MtypeHelpers -MfunctionReferences -ManonymousFunctions -MimplicitFunctionSpecialization -gh -gl -gp -gw3 -Xi -Sm -Sg -Os -Si appmain.pas
Free Pascal Compiler version 3.3.1 [2024/09/26] for x86_64
Copyright (c) 1993-2024 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling appmain.pas
Compiling unit_b.pas
Compiling unit_a.pas
Linking appmain
/usr/bin/ld.bfd: unit_b.o:(.data.n_VMT_$UNIT_B$_$PICK_ALL$TPROCS_$$_$CAPTURERCLASS_9_1_15_11+0xb8): undefined reference to `SYSTEM$_$TOBJECT_$__$$_GETHASHCODE$$<unknown type>'
appmain.pas(6,1) Error: Error while linking
appmain.pas(6,1) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: /home/dev/fpc_usr/lib/fpc/3.3.1/ppcx64 returned an error exitcode

Example Project

See Steps to reproduce

What is the current bug behavior?

See Steps to reproduce, compiler gives a Fatal: Internal error 2021010301 error.

What is the expected (correct) behavior?

That the code compile with no errors. Something to note is if the generic procedure is commented out, the code compiles and links. See #40925

unit_a.pas

unit unit_a;

interface

type
  tLoopAll     = reference to procedure (const i: integer);
  tMemberSeqId = reference to procedure (const member: pointer; const seqId: integer);

procedure loop (const start: integer; const final_value: integer; const loopPr: tLoopAll);
//generic procedure loop<T> (const list: T; const doItem: tMemberSeqId);

implementation


procedure loop (const start: integer; const final_value: integer; const loopPr: tLoopAll);
  procedure forward_iterate (i: integer = 0);
    begin
      for i := start to final_value do loopPr(i);
    end;

  procedure reverse_iterate (i: integer = 0);
    begin
      for i := start downto final_value do loopPr(i);
    end;

  begin
    if final_value < start then reverse_iterate
    else
      forward_iterate;
  end;
{
generic procedure loop<T> (const list: T; const doItem: tMemberSeqId);
  begin
    loop(0, list.count - 1, procedure (const i: integer)
    begin
      doItem(list.items[i], i);
    end);
  end;
}
end.
// CudaText: lexer_file=Pascal; tab_size=2; tab_spaces=Yes; newline=LF;

Code compiles without error:

$ fpc -Mobjfpc -Sh -Fcutf8 -MadvancedRecords -MtypeHelpers -MfunctionReferences -ManonymousFunctions -MimplicitFunctionSpecialization -gh -gl -gp -gw3 -Xi -Sm -Sg -Os -Si appmain.pas
Free Pascal Compiler version 3.3.1 [2024/09/26] for x86_64
Copyright (c) 1993-2024 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling appmain.pas
Compiling unit_b.pas
Compiling unit_a.pas
Linking appmain
72 lines compiled, 0.1 sec, 174496 bytes code, 1152456 bytes data

Relevant logs and/or screenshots

See Steps to reproduce

Possible fixes

Not sure, I the code that I had to isolate this repro example from was building and running last year. This is related to #40925

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information