Browse Source

fcl-passrc: store generic procedure templates

git-svn-id: trunk@42451 -
Mattias Gaertner 6 years ago
parent
commit
5d4ae23df8

+ 112 - 4
packages/fcl-passrc/src/pastree.pp

@@ -1038,6 +1038,14 @@ type
                         pmNoReturn, pmFar, pmFinal);
                         pmNoReturn, pmFar, pmFinal);
   TProcedureModifiers = Set of TProcedureModifier;
   TProcedureModifiers = Set of TProcedureModifier;
   TProcedureMessageType = (pmtNone,pmtInteger,pmtString);
   TProcedureMessageType = (pmtNone,pmtInteger,pmtString);
+
+  { TProcedureNamePart }
+
+  TProcedureNamePart = record
+    Name: string;
+    Templates: TFPList; // optional list of TPasGenericTemplateType, can nil!
+  end;
+  TProcedureNameParts = array of TProcedureNamePart;
                         
                         
   TProcedureBody = class;
   TProcedureBody = class;
 
 
@@ -1067,6 +1075,7 @@ type
     AliasName : String;
     AliasName : String;
     ProcType : TPasProcedureType;
     ProcType : TPasProcedureType;
     Body : TProcedureBody;
     Body : TProcedureBody;
+    NameParts: TProcedureNameParts; // only used for generic functions
     Procedure AddModifier(AModifier : TProcedureModifier);
     Procedure AddModifier(AModifier : TProcedureModifier);
     Function IsVirtual : Boolean;
     Function IsVirtual : Boolean;
     Function IsDynamic : Boolean;
     Function IsDynamic : Boolean;
@@ -1080,6 +1089,7 @@ type
     Function IsStatic : Boolean;
     Function IsStatic : Boolean;
     Function IsForward: Boolean;
     Function IsForward: Boolean;
     Function GetProcTypeEnum: TProcType; virtual;
     Function GetProcTypeEnum: TProcType; virtual;
+    procedure SetNameParts(var Parts: TProcedureNameParts);
     Property Modifiers : TProcedureModifiers Read FModifiers Write FModifiers;
     Property Modifiers : TProcedureModifiers Read FModifiers Write FModifiers;
     Property CallingConvention : TCallingConvention Read GetCallingConvention Write SetCallingConvention;
     Property CallingConvention : TCallingConvention Read GetCallingConvention Write SetCallingConvention;
     Property MessageName : String Read FMessageName Write FMessageName;
     Property MessageName : String Read FMessageName Write FMessageName;
@@ -1724,12 +1734,15 @@ const
      = ('cvar', 'external', 'public', 'export', 'class', 'static');
      = ('cvar', 'external', 'public', 'export', 'class', 'static');
 
 
 procedure ReleaseAndNil(var El: TPasElement {$IFDEF CheckPasTreeRefCount}; const Id: string{$ENDIF}); overload;
 procedure ReleaseAndNil(var El: TPasElement {$IFDEF CheckPasTreeRefCount}; const Id: string{$ENDIF}); overload;
+function GenericTemplateTypesAsString(List: TFPList): string;
 
 
 {$IFDEF HasPTDumpStack}
 {$IFDEF HasPTDumpStack}
 procedure PTDumpStack;
 procedure PTDumpStack;
 function GetPTDumpStack: string;
 function GetPTDumpStack: string;
 {$ENDIF}
 {$ENDIF}
 
 
+procedure ReleaseProcNameParts(var NameParts: TProcedureNameParts);
+
 implementation
 implementation
 
 
 uses SysUtils;
 uses SysUtils;
@@ -1742,6 +1755,54 @@ begin
   El:=nil;
   El:=nil;
 end;
 end;
 
 
+function GenericTemplateTypesAsString(List: TFPList): string;
+var
+  i, j: Integer;
+  T: TPasGenericTemplateType;
+begin
+  Result:='';
+  for i:=0 to List.Count-1 do
+    begin
+    if i>0 then
+      Result:=Result+',';
+    T:=TPasGenericTemplateType(List[i]);
+    Result:=Result+T.Name;
+    if length(T.Constraints)>0 then
+      begin
+      Result:=Result+':';
+      for j:=0 to length(T.Constraints)-1 do
+        begin
+        if j>0 then
+          Result:=Result+',';
+        Result:=Result+T.GetDeclaration(false);
+        end;
+      end;
+    end;
+  Result:='<'+Result+'>';
+end;
+
+procedure ReleaseProcNameParts(var NameParts: TProcedureNameParts);
+var
+  El: TPasElement;
+  i, j: Integer;
+begin
+  for i := 0 to length(NameParts)-1 do
+    begin
+    with NameParts[i] do
+      if Templates<>nil then
+        begin
+        for j:=0 to Templates.Count-1 do
+          begin
+          El:=TPasGenericTemplateType(Templates[j]);
+          El.Parent:=nil;
+          El.Release{$IFDEF CheckPasTreeRefCount}('TPasProcedure.NameParts'){$ENDIF};
+          end;
+        Templates.Free;
+        end;
+    end;
+  NameParts:=nil;
+end;
+
 Function IndentStrings(S : TStrings; indent : Integer) : string;
 Function IndentStrings(S : TStrings; indent : Integer) : string;
 Var
 Var
   I,CurrLen,CurrPos : Integer;
   I,CurrLen,CurrPos : Integer;
@@ -3496,6 +3557,7 @@ begin
   ReleaseAndNil(TPasElement(MessageExpr){$IFDEF CheckPasTreeRefCount},'TPasProcedure.MessageExpr'{$ENDIF});
   ReleaseAndNil(TPasElement(MessageExpr){$IFDEF CheckPasTreeRefCount},'TPasProcedure.MessageExpr'{$ENDIF});
   ReleaseAndNil(TPasElement(ProcType){$IFDEF CheckPasTreeRefCount},'TPasProcedure.ProcType'{$ENDIF});
   ReleaseAndNil(TPasElement(ProcType){$IFDEF CheckPasTreeRefCount},'TPasProcedure.ProcType'{$ENDIF});
   ReleaseAndNil(TPasElement(Body){$IFDEF CheckPasTreeRefCount},'TPasProcedure.Body'{$ENDIF});
   ReleaseAndNil(TPasElement(Body){$IFDEF CheckPasTreeRefCount},'TPasProcedure.Body'{$ENDIF});
+  ReleaseProcNameParts(NameParts);
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -4164,7 +4226,7 @@ var
 begin
 begin
   inherited ForEachCall(aMethodCall, Arg);
   inherited ForEachCall(aMethodCall, Arg);
   for i:=0 to GenericTemplateTypes.Count-1 do
   for i:=0 to GenericTemplateTypes.Count-1 do
-    ForEachChildCall(aMethodCall,Arg,TPasElement(GenericTemplateTypes[i]),true);
+    ForEachChildCall(aMethodCall,Arg,TPasElement(GenericTemplateTypes[i]),false);
   for i:=0 to Members.Count-1 do
   for i:=0 to Members.Count-1 do
     ForEachChildCall(aMethodCall,Arg,TPasElement(Members[i]),false);
     ForEachChildCall(aMethodCall,Arg,TPasElement(Members[i]),false);
 end;
 end;
@@ -4256,7 +4318,12 @@ begin
       else
       else
         Temp:='packed '+Temp;
         Temp:='packed '+Temp;
     If Full and (Name<>'') then
     If Full and (Name<>'') then
-      Temp:=Name+' = '+Temp;
+      begin
+      if GenericTemplateTypes.Count>0 then
+        Temp:=Name+GenericTemplateTypesAsString(GenericTemplateTypes)+' = '+Temp
+      else
+        Temp:=Name+' = '+Temp;
+      end;
     S.Add(Temp);
     S.Add(Temp);
     GetMembers(S);
     GetMembers(S);
     S.Add('end');
     S.Add('end');
@@ -4562,8 +4629,15 @@ end;
 
 
 procedure TPasProcedure.ForEachCall(const aMethodCall: TOnForEachPasElement;
 procedure TPasProcedure.ForEachCall(const aMethodCall: TOnForEachPasElement;
   const Arg: Pointer);
   const Arg: Pointer);
+var
+  i, j: Integer;
 begin
 begin
   inherited ForEachCall(aMethodCall, Arg);
   inherited ForEachCall(aMethodCall, Arg);
+  for i:=0 to length(NameParts)-1 do
+    with NameParts[i] do
+      if Templates<>nil then
+        for j:=0 to Templates.Count-1 do
+          ForEachChildCall(aMethodCall,Arg,TPasElement(Templates[i]),false);
   ForEachChildCall(aMethodCall,Arg,ProcType,false);
   ForEachChildCall(aMethodCall,Arg,ProcType,false);
   ForEachChildCall(aMethodCall,Arg,PublicName,false);
   ForEachChildCall(aMethodCall,Arg,PublicName,false);
   ForEachChildCall(aMethodCall,Arg,LibraryExpr,false);
   ForEachChildCall(aMethodCall,Arg,LibraryExpr,false);
@@ -4573,7 +4647,6 @@ begin
 end;
 end;
 
 
 procedure TPasProcedure.AddModifier(AModifier: TProcedureModifier);
 procedure TPasProcedure.AddModifier(AModifier: TProcedureModifier);
-
 begin
 begin
   Include(FModifiers,AModifier);
   Include(FModifiers,AModifier);
 end;
 end;
@@ -4639,17 +4712,52 @@ begin
   Result:=ptProcedure;
   Result:=ptProcedure;
 end;
 end;
 
 
+procedure TPasProcedure.SetNameParts(var Parts: TProcedureNameParts);
+var
+  i, j: Integer;
+  El: TPasElement;
+begin
+  if length(NameParts)>0 then
+    ReleaseProcNameParts(NameParts);
+  NameParts:=Parts;
+  Parts:=nil;
+  for i:=0 to length(NameParts)-1 do
+    with NameParts[i] do
+      if Templates<>nil then
+        for j:=0 to Templates.Count-1 do
+          begin
+          El:=TPasElement(Templates[j]);
+          El.Parent:=Self;
+          end;
+end;
+
 function TPasProcedure.GetDeclaration(full: Boolean): string;
 function TPasProcedure.GetDeclaration(full: Boolean): string;
 Var
 Var
   S : TStringList;
   S : TStringList;
   T: String;
   T: String;
+  i: Integer;
 begin
 begin
   S:=TStringList.Create;
   S:=TStringList.Create;
   try
   try
     If Full then
     If Full then
       begin
       begin
       T:=TypeName;
       T:=TypeName;
-      if Name<>'' then
+      if length(NameParts)>0 then
+        begin
+        T:=T+' ';
+        for i:=0 to length(NameParts)-1 do
+          begin
+          if i>0 then
+            T:=T+'.';
+          with NameParts[i] do
+            begin
+            T:=T+Name;
+            if Templates<>nil then
+              T:=T+GenericTemplateTypesAsString(Templates);
+            end;
+          end;
+        end
+      else if Name<>'' then
         T:=T+' '+Name;
         T:=T+' '+Name;
       S.Add(T);
       S.Add(T);
       end;
       end;

+ 107 - 56
packages/fcl-passrc/src/pparser.pp

@@ -6318,42 +6318,86 @@ end;
 function TPasParser.ParseProcedureOrFunctionDecl(Parent: TPasElement;
 function TPasParser.ParseProcedureOrFunctionDecl(Parent: TPasElement;
   ProcType: TProcType; MustBeGeneric: boolean; AVisibility: TPasMemberVisibility
   ProcType: TProcType; MustBeGeneric: boolean; AVisibility: TPasMemberVisibility
   ): TPasProcedure;
   ): TPasProcedure;
+var
+  NameParts: TProcedureNameParts;
 
 
   function ExpectProcName: string;
   function ExpectProcName: string;
-
+  { Simple procedure:
+      Name
+    Method implementation of non generic class:
+      aClass.SubClass.Name
+    ObjFPC generic procedure or method declaration:
+      MustBeGeneric=true, Name<Templates>
+    Delphi generic Method Declaration:
+      MustBeGeneric=false, Name<Templates>
+    ObjFPC Method implementation of generic class:
+      aClass.SubClass.Name
+    Delphi Method implementation of generic class:
+      aClass<Templates>.SubClass<Templates>.Name
+      aClass.SubClass<Templates>.Name<Templates>
+  }
   Var
   Var
     L : TFPList;
     L : TFPList;
-    I : Integer;
-
+    I , Cnt, p: Integer;
+    CurName: String;
   begin
   begin
     Result:=ExpectIdentifier;
     Result:=ExpectIdentifier;
-    //writeln('ExpectProcName ',Parent.Classname);
-    if Parent is TImplementationSection then
-      begin
+    Cnt:=1;
+    repeat
       NextToken;
       NextToken;
-      repeat
-        if CurToken=tkDot then
-          Result:=Result+'.'+ExpectIdentifier
-        else if CurToken=tkLessThan then
+      if CurToken=tkDot then
+        begin
+          if Parent is TImplementationSection then
+            begin
+            inc(Cnt);
+            CurName:=ExpectIdentifier;
+            Result:=Result+'.'+CurName;
+            if length(NameParts)>0 then
+              begin
+              SetLength(NameParts,Cnt);
+              NameParts[Cnt-1].Name:=CurName;
+              end;
+            end
+          else
+            ParseExcSyntaxError;
+        end
+      else if CurToken=tkLessThan then
+        begin
+        if (not MustBeGeneric) and not (msDelphi in CurrentModeswitches) then
+          ParseExc(nParserGenericFunctionNeedsGenericKeyword,SParserGenericFunctionNeedsGenericKeyword);
+        // generic templates
+        if length(NameParts)=0 then
           begin
           begin
-          if (not MustBeGeneric) and not (msDelphi in CurrentModeswitches) then
-            ParseExc(nParserGenericFunctionNeedsGenericKeyword,SParserGenericFunctionNeedsGenericKeyword);
-          UnGetToken;
-          L:=TFPList.Create;
-          Try
-            ReadGenericArguments(L,Parent);
-          finally
-            For I:=0 to L.Count-1 do
-              TPasElement(L[i]).Release{$IFDEF CheckPasTreeRefCount}('CreateElement'){$ENDIF};
-            L.Free;
-          end;
+          // initialize NameParts
+          SetLength(NameParts,Cnt);
+          i:=0;
+          CurName:=Result;
+          repeat
+            p:=Pos('.',CurName);
+            if p>0 then
+              begin
+              NameParts[i].Name:=LeftStr(CurName,p-1);
+              System.Delete(CurName,1,p);
+              end
+            else
+              begin
+              NameParts[i].Name:=CurName;
+              break;
+              end;
+            inc(i);
+          until false;
           end
           end
-        else
-          break;
-        NextToken;
-      until false;
-      UngetToken;
-      end;
+        else if NameParts[Cnt-1].Templates<>nil then
+          ParseExcSyntaxError;
+        UnGetToken;
+        L:=TFPList.Create;
+        NameParts[Cnt-1].Templates:=L;
+        ReadGenericArguments(L,Parent);
+        end
+      else
+        break;
+    until false;
+    UngetToken;
   end;
   end;
 
 
 var
 var
@@ -6362,36 +6406,41 @@ var
   Ot : TOperatorType;
   Ot : TOperatorType;
   IsTokenBased , ok: Boolean;
   IsTokenBased , ok: Boolean;
 begin
 begin
-  case ProcType of
-  ptOperator,ptClassOperator:
-    begin
-    if MustBeGeneric then
-      ParseExcTokenError('procedure');
-    NextToken;
-    IsTokenBased:=CurToken<>tkIdentifier;
-    if IsTokenBased then
-      OT:=TPasOperator.TokenToOperatorType(CurTokenText)
-    else
-      OT:=TPasOperator.NameToOperatorType(CurTokenString);
-    if (ot=otUnknown) then
-      ParseExc(nErrUnknownOperatorType,SErrUnknownOperatorType,[CurTokenString]);
-    Name:=OperatorNames[Ot];
-    end;
-  ptAnonymousProcedure,ptAnonymousFunction:
-    begin
-    Name:='';
-    if MustBeGeneric then
-      ParseExcTokenError('generic'); // inconsistency
-    end
-  else
-    Name:=ExpectProcName;
-  end;
-  PC:=GetProcedureClass(ProcType);
-  if Name<>'' then
-    Parent:=CheckIfOverLoaded(Parent,Name);
-  Result:=TPasProcedure(CreateElement(PC,Name,Parent,AVisibility));
+  NameParts:=nil;
+  Result:=nil;
   ok:=false;
   ok:=false;
   try
   try
+    case ProcType of
+    ptOperator,ptClassOperator:
+      begin
+      if MustBeGeneric then
+        ParseExcTokenError('procedure');
+      NextToken;
+      IsTokenBased:=CurToken<>tkIdentifier;
+      if IsTokenBased then
+        OT:=TPasOperator.TokenToOperatorType(CurTokenText)
+      else
+        OT:=TPasOperator.NameToOperatorType(CurTokenString);
+      if (ot=otUnknown) then
+        ParseExc(nErrUnknownOperatorType,SErrUnknownOperatorType,[CurTokenString]);
+      Name:=OperatorNames[Ot];
+      end;
+    ptAnonymousProcedure,ptAnonymousFunction:
+      begin
+      Name:='';
+      if MustBeGeneric then
+        ParseExcTokenError('generic'); // inconsistency
+      end
+    else
+      Name:=ExpectProcName;
+    end;
+    PC:=GetProcedureClass(ProcType);
+    if Name<>'' then
+      Parent:=CheckIfOverLoaded(Parent,Name);
+    Result:=TPasProcedure(CreateElement(PC,Name,Parent,AVisibility));
+    if NameParts<>nil then
+      Result.SetNameParts(NameParts);
+
     case ProcType of
     case ProcType of
     ptFunction, ptClassFunction, ptOperator, ptClassOperator, ptAnonymousFunction:
     ptFunction, ptClassFunction, ptOperator, ptClassOperator, ptAnonymousFunction:
       begin
       begin
@@ -6428,7 +6477,9 @@ begin
         end;
         end;
     ok:=true;
     ok:=true;
   finally
   finally
-    if not ok then
+    if NameParts<>nil then;
+      ReleaseProcNameParts(NameParts);
+    if (not ok) and (Result<>nil) then
       Result.Release{$IFDEF CheckPasTreeRefCount}('CreateElement'){$ENDIF};
       Result.Release{$IFDEF CheckPasTreeRefCount}('CreateElement'){$ENDIF};
   end;
   end;
 end;
 end;

+ 30 - 2
packages/fcl-passrc/tests/tcgenerics.pp

@@ -28,7 +28,8 @@ Type
     Procedure TestSpecializeNested;
     Procedure TestSpecializeNested;
     Procedure TestInlineSpecializeInStatement;
     Procedure TestInlineSpecializeInStatement;
     Procedure TestInlineSpecializeInStatementDelphi;
     Procedure TestInlineSpecializeInStatementDelphi;
-    Procedure TestGenericFunction;
+    Procedure TestGenericFunction_Program;
+    Procedure TestGenericFunction_Unit;
   end;
   end;
 
 
 implementation
 implementation
@@ -200,11 +201,22 @@ begin
     Add('type');
     Add('type');
     Add('  TTest<T> =  object');
     Add('  TTest<T> =  object');
     Add('    procedure foo(v:T);');
     Add('    procedure foo(v:T);');
+    Add('    procedure bar<Y>(v:T);');
+    Add('  type');
+    Add('    TSub = class');
+    Add('      procedure DoIt<Y>(v:T);');
+    Add('    end;');
     Add('  end;');
     Add('  end;');
     Add('implementation');
     Add('implementation');
     Add('procedure TTest<T>.foo;');
     Add('procedure TTest<T>.foo;');
     Add('begin');
     Add('begin');
     Add('end;');
     Add('end;');
+    Add('procedure TTest<T>.bar<Y>;');
+    Add('begin');
+    Add('end;');
+    Add('procedure TTest<T>.TSub.DoIt<Y>;');
+    Add('begin');
+    Add('end;');
     end;
     end;
   ParseModule;
   ParseModule;
 end;
 end;
@@ -258,7 +270,7 @@ begin
   ParseModule;
   ParseModule;
 end;
 end;
 
 
-procedure TTestGenerics.TestGenericFunction;
+procedure TTestGenerics.TestGenericFunction_Program;
 begin
 begin
   Add([
   Add([
   'generic function IfThen<T>(val:boolean;const iftrue:T; const iffalse:T) :T; inline; overload;',
   'generic function IfThen<T>(val:boolean;const iftrue:T; const iffalse:T) :T; inline; overload;',
@@ -270,6 +282,22 @@ begin
   ParseModule;
   ParseModule;
 end;
 end;
 
 
+procedure TTestGenerics.TestGenericFunction_Unit;
+begin
+  Add([
+  'unit afile;',
+  'interface',
+  'generic function Get<T>(val: T) :T;',
+  'implementation',
+  'generic function Get<T>(val: T) :T;',
+  'begin',
+  'end;',
+  'initialization',
+  '  specialize GetIt<word>(2);',
+  '']);
+  ParseModule;
+end;
+
 initialization
 initialization
   RegisterTest(TTestGenerics);
   RegisterTest(TTestGenerics);
 end.
 end.