Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Compiler/Delphi/Generics    [ Add a report in this area ]  
Report #:  101272   Status: Closed
Internal Linker Error when implementing generic Y combinator
Project:  Delphi Build #:  15.0.3953.35171
Version:    15.0 Submitted By:   Moritz Beutel
Report Type:  Crash / Data loss / Total failure Date Reported:  11/23/2011 3:31:04 PM
Severity:    Infrequently encountered problem Last Updated: 4/15/2014 6:48:52 PM
Platform:    All platforms Internal Tracking #:   25212
Resolution: Fixed (Resolution Comments) Resolved in Build: : XE6
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
Description
See steps.

The problem occurred while I was trying to implement the Y combinator in Delphi. I eventually succeeded by applying a workaround, but the straightforward way (which works fine in C#) causes a linker error in Delphi, probably related to the self-referencing of TRecursiveFunc combined with the use of generics.

Removing the genericity of YCombinator.Fix() (by substituting T with Integer everywhere) makes the problem go away.

The working implementation can be found here:
http://rosettacode.org/wiki/Y_combinator#Delphi
Steps to Reproduce:
Try to compile the following code:
// -----
program Y;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  YCombinator = class sealed
    class function Fix<T> (F: TFunc<TFunc<T, T>, TFunc<T, T>>): TFunc<T, T>; static;
  end;

class function YCombinator.Fix<T> (F: TFunc<TFunc<T, T>, TFunc<T, T>>): TFunc<T, T>;
type
  TRecursiveFunc = reference to function (R: TRecursiveFunc): TFunc<T, T>;
var
  R: TRecursiveFunc;
begin
  R := function (W: TRecursiveFunc): TFunc<T, T>
    begin
      Result := F (function (I: T): T
        begin
          Result := W (W) (I);
        end);
    end;
  Result := R (R);
end;

type
  IntFunc = TFunc<Integer, Integer>;

function AlmostFac (F: IntFunc): IntFunc;
begin
  Result := function (N: Integer): Integer
    begin
      if N <= 1 then
        Result := 1
      else
        Result := N * F (N - 1);
    end;
end;

var
  Fac: IntFunc;
begin
  Fac := YCombinator.Fix<Integer> (AlmostFac);
  Writeln ('Fac(10) = ', Fac (10));
end.
// -----

Exp.: works, prints "Fac(10) = 3628800" at runtime
Act.: [DCC32 Error] Fatal: F2084 Internal Error: L2060

//USc: for XE2 U#2 it is SY6315
Workarounds
Use an additional wrapper record and rewrite YCombinator.Fix<>() as follows:

// -----
  TRecursiveFuncWrapper<T> = record
    type
      TRecursiveFunc = reference to function (R: TRecursiveFuncWrapper<T>): TFunc<T, T>;
    var
      O: TRecursiveFunc;
  end;

class function YCombinator.Fix<T> (F: TFunc<TFunc<T, T>, TFunc<T, T>>): TFunc<T, T>;
var
  R: TRecursiveFuncWrapper<T>;
begin
  R.O := function (W: TRecursiveFuncWrapper<T>): TFunc<T, T>
    begin
      Result := F (function (I: T): T
        begin
          Result := W.O (W) (I);
        end);
    end;
  Result := R.O (R);
end;
// -----
Attachment
None
Comments

None

Server Response from: ETNACODE01