Fix generic return type on interface free functions

A unit interface-section free function returning a generic instantiation
(e.g. function MakeList: TList<string>) failed with 'Unknown return type'
because the free-function return-type resolution used FTable.FindType, which
only matches already-registered types and never instantiates a generic on
demand. Class-method return types already resolved correctly via
FindTypeOrInstantiate; this aligns the free-function paths (interface and
implementation, in both AnalyseUnit and AnalyseUnitForExport) with that.
The change is additive — FindTypeOrInstantiate tries FindType first — so
non-generic returns are unaffected. Adds an e2e regression test.
This commit is contained in:
Graeme Geldenhuys 2026-06-23 18:15:52 +01:00
parent 5e42020faf
commit c81d65bb78
2 changed files with 49 additions and 4 deletions

View file

@ -1177,7 +1177,7 @@ begin
if MDecl.ReturnTypeName <> '' then
begin
ParType := FTable.FindType(MDecl.ReturnTypeName);
ParType := FindTypeOrInstantiate(MDecl.ReturnTypeName);
if ParType = nil then
SemanticError(
Format('Unknown return type ''%s'' for ''%s''',
@ -1268,7 +1268,7 @@ begin
if ImplDecl.ReturnTypeName <> '' then
begin
ParType := FTable.FindType(ImplDecl.ReturnTypeName);
ParType := FindTypeOrInstantiate(ImplDecl.ReturnTypeName);
if ParType = nil then
SemanticError(
Format('Unknown return type ''%s'' for ''%s''',
@ -1500,7 +1500,7 @@ begin
if MDecl.ReturnTypeName <> '' then
begin
ParType := FTable.FindType(MDecl.ReturnTypeName);
ParType := FindTypeOrInstantiate(MDecl.ReturnTypeName);
if ParType = nil then
SemanticError(
Format('Unknown return type ''%s'' for ''%s''',
@ -1585,7 +1585,7 @@ begin
if ImplDecl.ReturnTypeName <> '' then
begin
ParType := FTable.FindType(ImplDecl.ReturnTypeName);
ParType := FindTypeOrInstantiate(ImplDecl.ReturnTypeName);
if ParType = nil then
SemanticError(
Format('Unknown return type ''%s'' for ''%s''',

View file

@ -25,6 +25,7 @@ type
procedure TestRun_TObjectList_Delete;
procedure TestRun_Collections_Valgrind;
procedure TestRun_TwoDictInstancesInUnit_BothLink;
procedure TestRun_IntfFreeFunc_GenericReturn_Compiles;
end;
implementation
@ -319,6 +320,50 @@ begin
AssertEquals('Ints(2) + Bools(1) = 3', '3' + #10, Output);
end;
{ Regression: a unit's INTERFACE-section free function (not a class method)
whose return type is a generic instantiation such as TList<string> used to
fail semantic analysis with "Unknown return type 'TList<string>'". The
interface free-function path resolved the return type through a plain
FindType lookup, which never attempts generic instantiation, whereas the
class-method path goes through FindTypeOrInstantiate. Switching the
free-function path to FindTypeOrInstantiate fixes it. The same generic
return type as a class method, and as an impl-section free function, already
worked — those must keep working. }
procedure TE2ECollections2Tests.TestRun_IntfFreeFunc_GenericReturn_Compiles;
const
UnitSrc = '''
unit freefunc;
interface
uses Generics.Collections;
function MakeList: TList<string>;
implementation
function MakeList: TList<string>;
begin
Result := TList<string>.Create();
Result.Add('a');
Result.Add('b')
end;
end.
''';
DrvSrc = '''
program P;
uses Generics.Collections, freefunc;
var L: TList<string>;
begin
L := MakeList();
WriteLn(L.Count);
L.Free()
end.
''';
var Output: string; RCode: Integer;
begin
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
AssertTrue('compile+link+run',
CompileAndRunWithUnit('freefunc', UnitSrc, DrvSrc, Output, RCode));
AssertEquals('exit code 0', 0, RCode);
AssertEquals('list count', '2' + #10, Output);
end;
initialization
RegisterTest(TE2ECollections2Tests);