Browse Source

fcl-passrc: added TPasGenericType

git-svn-id: trunk@42502 -
Mattias Gaertner 6 years ago
parent
commit
52ef731f42

+ 2 - 0
packages/fcl-passrc/src/pasresolveeval.pas

@@ -193,6 +193,7 @@ const
   nConstraintXSpecifiedMoreThanOnce = 3127;
   nConstraintXSpecifiedMoreThanOnce = 3127;
   nConstraintXAndConstraintYCannotBeTogether = 3128;
   nConstraintXAndConstraintYCannotBeTogether = 3128;
   nXIsNotAValidConstraint = 3129;
   nXIsNotAValidConstraint = 3129;
+  nWrongNumberOfParametersForGenericType = 3130;
 
 
   // using same IDs as FPC
   // using same IDs as FPC
   nVirtualMethodXHasLowerVisibility = 3250; // was 3050
   nVirtualMethodXHasLowerVisibility = 3250; // was 3050
@@ -333,6 +334,7 @@ resourcestring
   sConstraintXSpecifiedMoreThanOnce = 'Constraint ''%s'' specified more than once';
   sConstraintXSpecifiedMoreThanOnce = 'Constraint ''%s'' specified more than once';
   sConstraintXAndConstraintYCannotBeTogether = '''%s'' constraint and ''%s'' constraint cannot be specified together';
   sConstraintXAndConstraintYCannotBeTogether = '''%s'' constraint and ''%s'' constraint cannot be specified together';
   sXIsNotAValidConstraint = '''%s'' is not a valid constraint';
   sXIsNotAValidConstraint = '''%s'' is not a valid constraint';
+  sWrongNumberOfParametersForGenericType = 'wrong number of parameters for generic type %s';
 
 
 type
 type
   { TResolveData - base class for data stored in TPasElement.CustomData }
   { TResolveData - base class for data stored in TPasElement.CustomData }

+ 327 - 5
packages/fcl-passrc/src/pasresolver.pp

@@ -661,6 +661,26 @@ type
     Element: TPasType; // TPasClassOfType or TPasPointerType
     Element: TPasType; // TPasClassOfType or TPasPointerType
   end;
   end;
 
 
+  { TPasSpecializeTypeData - CustomData of TPasSpecializeType
+    for the generic type see TPasSpecializeType(Element).DestType }
+
+  TPasSpecializeTypeData = Class(TResolveData)
+  public
+    SpecializedType: TPasType;
+  end;
+
+  { TPSSpecializedItem }
+
+  TPSSpecializedItem = class
+  private
+    FSpecializedType: TPasGenericType;
+    procedure SetSpecializedType(AValue: TPasGenericType);
+  public
+    Params: TPasTypeArray;
+    destructor Destroy; override;
+    property SpecializedType: TPasGenericType read FSpecializedType write SetSpecializedType;
+  end;
+
   TPSRefAccess = (
   TPSRefAccess = (
     psraNone,
     psraNone,
     psraRead,
     psraRead,
@@ -896,9 +916,17 @@ type
     destructor Destroy; override;
     destructor Destroy; override;
   end;
   end;
 
 
+  { TPasGenericScope }
+
+  TPasGenericScope = Class(TPasIdentifierScope)
+  public
+    SpecializedTypes: TObjectList; // list of TPSSpecializedItem
+    destructor Destroy; override;
+  end;
+
   { TPasClassOrRecordScope }
   { TPasClassOrRecordScope }
 
 
-  TPasClassOrRecordScope = Class(TPasIdentifierScope)
+  TPasClassOrRecordScope = Class(TPasGenericScope)
   public
   public
     DefaultProperty: TPasProperty;
     DefaultProperty: TPasProperty;
     ClassConstructor: TPasClassConstructor;
     ClassConstructor: TPasClassConstructor;
@@ -1239,6 +1267,7 @@ type
     Flags: TPasResolverResultFlags;
     Flags: TPasResolverResultFlags;
   end;
   end;
   PPasResolverResult = ^TPasResolverResult;
   PPasResolverResult = ^TPasResolverResult;
+  TPasResolverResultArray = array of TPasResolverResult;
 
 
 type
 type
   TPasResolverComputeFlag = (
   TPasResolverComputeFlag = (
@@ -1520,13 +1549,16 @@ type
     procedure FinishRangeType(El: TPasRangeType); virtual;
     procedure FinishRangeType(El: TPasRangeType); virtual;
     procedure FinishConstRangeExpr(RangeExpr: TBinaryExpr;
     procedure FinishConstRangeExpr(RangeExpr: TBinaryExpr;
       out LeftResolved, RightResolved: TPasResolverResult);
       out LeftResolved, RightResolved: TPasResolverResult);
+    procedure FinishGenericTemplateTypes(aType: TPasGenericType); virtual;
     procedure FinishRecordType(El: TPasRecordType); virtual;
     procedure FinishRecordType(El: TPasRecordType); virtual;
     procedure FinishClassType(El: TPasClassType); virtual;
     procedure FinishClassType(El: TPasClassType); virtual;
     procedure FinishClassOfType(El: TPasClassOfType); virtual;
     procedure FinishClassOfType(El: TPasClassOfType); virtual;
     procedure FinishPointerType(El: TPasPointerType); virtual;
     procedure FinishPointerType(El: TPasPointerType); virtual;
     procedure FinishArrayType(El: TPasArrayType); virtual;
     procedure FinishArrayType(El: TPasArrayType); virtual;
     procedure FinishGenericTemplateType(El: TPasGenericTemplateType); virtual;
     procedure FinishGenericTemplateType(El: TPasGenericTemplateType); virtual;
+    procedure FinishSpecializeType(El: TPasSpecializeType); virtual;
     procedure FinishResourcestring(El: TPasResString); virtual;
     procedure FinishResourcestring(El: TPasResString); virtual;
+    procedure FinishProcNameParts(aProc: TPasProcedure); virtual;
     procedure FinishProcedure(aProc: TPasProcedure); virtual;
     procedure FinishProcedure(aProc: TPasProcedure); virtual;
     procedure FinishProcedureType(El: TPasProcedureType); virtual;
     procedure FinishProcedureType(El: TPasProcedureType); virtual;
     procedure FinishMethodDeclHeader(Proc: TPasProcedure); virtual;
     procedure FinishMethodDeclHeader(Proc: TPasProcedure); virtual;
@@ -1622,6 +1654,7 @@ type
     function FindTVarRec(ErrorEl: TPasElement): TPasRecordType; virtual;
     function FindTVarRec(ErrorEl: TPasElement): TPasRecordType; virtual;
     function GetTVarRec(El: TPasArrayType): TPasRecordType; virtual;
     function GetTVarRec(El: TPasArrayType): TPasRecordType; virtual;
   protected
   protected
+    // constant evaluation
     fExprEvaluator: TResExprEvaluator;
     fExprEvaluator: TResExprEvaluator;
     procedure OnExprEvalLog(Sender: TResExprEvaluator; const id: TMaxPrecInt;
     procedure OnExprEvalLog(Sender: TResExprEvaluator; const id: TMaxPrecInt;
       MsgType: TMessageType; MsgNumber: integer; const Fmt: String;
       MsgType: TMessageType; MsgNumber: integer; const Fmt: String;
@@ -1633,6 +1666,10 @@ type
     procedure OnRangeCheckEl(Sender: TResExprEvaluator; El: TPasElement;
     procedure OnRangeCheckEl(Sender: TResExprEvaluator; El: TPasElement;
       var MsgType: TMessageType); virtual;
       var MsgType: TMessageType); virtual;
     function EvalBaseTypeCast(Params: TParamsExpr; bt: TResolverBaseType): TResEvalvalue;
     function EvalBaseTypeCast(Params: TParamsExpr; bt: TResolverBaseType): TResEvalvalue;
+  protected
+    // generic/specialize
+    function GetSpecializedType(El: TPasSpecializeType): TPasGenericType;
+    procedure CheckSpecializeConstraints(El : TPasSpecializeType);
   protected
   protected
     // custom types (added by descendant resolvers)
     // custom types (added by descendant resolvers)
     function CheckAssignCompatibilityCustom(
     function CheckAssignCompatibilityCustom(
@@ -2783,6 +2820,36 @@ begin
   str(a,Result);
   str(a,Result);
 end;
 end;
 
 
+{ TPasGenericScope }
+
+destructor TPasGenericScope.Destroy;
+begin
+  if SpecializedTypes<>nil then
+    begin
+    SpecializedTypes.Free;
+    SpecializedTypes:=nil;
+    end;
+  inherited Destroy;
+end;
+
+{ TPSSpecializedItem }
+
+procedure TPSSpecializedItem.SetSpecializedType(AValue: TPasGenericType);
+begin
+  if FSpecializedType=AValue then Exit;
+  if FSpecializedType<>nil then
+    FSpecializedType.Release{$IFDEF CheckPasTreeRefCount}('TPSSpecializedItem.SpecializedType'){$ENDIF};
+  FSpecializedType:=AValue;
+  if FSpecializedType<>nil then
+    FSpecializedType.AddRef{$IFDEF CheckPasTreeRefCount}('TPSSpecializedItem.SpecializedType'){$ENDIF};
+end;
+
+destructor TPSSpecializedItem.Destroy;
+begin
+  SpecializedType:=nil;
+  inherited Destroy;
+end;
+
 { TPasInheritedScope }
 { TPasInheritedScope }
 
 
 function TPasInheritedScope.FindIdentifier(const Identifier: String
 function TPasInheritedScope.FindIdentifier(const Identifier: String
@@ -5412,7 +5479,9 @@ begin
   else if (C=TPasPointerType) then
   else if (C=TPasPointerType) then
     EmitTypeHints(El,TPasPointerType(El).DestType)
     EmitTypeHints(El,TPasPointerType(El).DestType)
   else if C=TPasGenericTemplateType then
   else if C=TPasGenericTemplateType then
-    FinishGenericTemplateType(TPasGenericTemplateType(El));
+    FinishGenericTemplateType(TPasGenericTemplateType(El))
+  else if C=TPasSpecializeType then
+    FinishSpecializeType(TPasSpecializeType(El));
 end;
 end;
 
 
 procedure TPasResolver.FinishEnumType(El: TPasEnumType);
 procedure TPasResolver.FinishEnumType(El: TPasEnumType);
@@ -5538,6 +5607,32 @@ begin
   ReleaseEvalValue(RgValue);
   ReleaseEvalValue(RgValue);
 end;
 end;
 
 
+procedure TPasResolver.FinishGenericTemplateTypes(aType: TPasGenericType);
+var
+  C: TClass;
+  Scope: TPasIdentifierScope;
+  GenTemplates: TFPList;
+  i: Integer;
+  TemplType: TPasGenericTemplateType;
+begin
+  // add template names to scope
+  C:=aType.ClassType;
+  if C.InheritsFrom(TPasMembersType) then
+    Scope:=aType.CustomData as TPasClassOrRecordScope
+  // ToDo: TPasArrayType
+  // ToDo: TPasProcedureType
+  else
+    RaiseMsg(20190726150359,nNotYetImplemented,sNotYetImplemented,[GetObjName(aType)],aType);
+  GenTemplates:=aType.GenericTemplateTypes;
+  if (GenTemplates=nil) or (GenTemplates.Count=0) then
+    RaiseMsg(20190726184902,nNotYetImplemented,sNotYetImplemented,['emty generic template list'],aType);
+  for i:=0 to GenTemplates.Count-1 do
+    begin
+    TemplType:=TPasGenericTemplateType(GenTemplates[i]);
+    AddIdentifier(Scope,TemplType.Name,TemplType,pikSimple);
+    end;
+end;
+
 procedure TPasResolver.FinishRecordType(El: TPasRecordType);
 procedure TPasResolver.FinishRecordType(El: TPasRecordType);
 begin
 begin
   if TopScope.Element=El then
   if TopScope.Element=El then
@@ -5940,6 +6035,50 @@ begin
     end;
     end;
 end;
 end;
 
 
+procedure TPasResolver.FinishSpecializeType(El: TPasSpecializeType);
+var
+  Params, GenericTemplateList: TFPList;
+  P: TPasElement;
+  DestType: TPasType;
+  i: Integer;
+begin
+  // resolve Params
+  Params:=El.Params;
+  for i:=0 to Params.Count-1 do
+    begin
+    P:=TPasElement(Params[i]);
+    if P is TPasExpr then
+      ResolveExpr(TPasExpr(P),rraRead);
+    end;
+  if Params.Count=0 then
+    RaiseMsg(20190724114416,nMissingParameterX,sMissingParameterX,['type'],El);
+
+  // check DestType
+  GenericTemplateList:=nil;
+  DestType:=El.DestType;
+  if DestType=nil then
+    RaiseMsg(20190725184734,nIdentifierNotFound,sIdentifierNotFound,['specialize type'],El)
+  else if not (DestType is TPasGenericType) then
+    RaiseMsg(20190725193552,nXExpectedButYFound,sXExpectedButYFound,['generic type',DestType.Name],El);
+  GenericTemplateList:=TPasGenericType(DestType).GenericTemplateTypes;
+  if (GenericTemplateList<>nil)
+      and (GenericTemplateList.Count<>Params.Count) then
+    GenericTemplateList:=nil;
+
+  if GenericTemplateList=nil then
+    begin
+    // ToDO: resolve DestType using Params.Count
+    //FindElementWithoutParams();
+    //Data:=Default(TPRFindData);
+    //Data.ErrorPosEl:=El;
+    //Abort:=false;
+    //IterateElements(El.Name,@OnFindFirst_PreferNoParams,@Data,Abort);
+    RaiseMsg(20190725194222,nWrongNumberOfParametersForGenericType,sWrongNumberOfParametersForGenericType,['ToDo'],El);
+    end;
+
+  GetSpecializedType(El);
+end;
+
 procedure TPasResolver.FinishResourcestring(El: TPasResString);
 procedure TPasResolver.FinishResourcestring(El: TPasResString);
 var
 var
   ResolvedEl: TPasResolverResult;
   ResolvedEl: TPasResolverResult;
@@ -5950,6 +6089,19 @@ begin
     RaiseXExpectedButYFound(20171004135753,'string',GetTypeDescription(ResolvedEl),El.Expr);
     RaiseXExpectedButYFound(20171004135753,'string',GetTypeDescription(ResolvedEl),El.Expr);
 end;
 end;
 
 
+procedure TPasResolver.FinishProcNameParts(aProc: TPasProcedure);
+var
+  i, j: Integer;
+begin
+  for i:=0 to length(aProc.NameParts)-1 do
+    with aProc.NameParts[i] do
+      begin
+      if Templates<>nil then
+        for j:=0 to Templates.Count-1 do
+          AddType(TPasGenericTemplateType(Templates[j]));
+      end;
+end;
+
 procedure TPasResolver.FinishProcedure(aProc: TPasProcedure);
 procedure TPasResolver.FinishProcedure(aProc: TPasProcedure);
 var
 var
   i: Integer;
   i: Integer;
@@ -13755,6 +13907,169 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TPasResolver.GetSpecializedType(El: TPasSpecializeType
+  ): TPasGenericType;
+var
+  Data: TPasSpecializeTypeData;
+  GenericType: TPasGenericType;
+  GenScope: TPasGenericScope;
+  Params: TFPList;
+  i, j: Integer;
+  Param: TPasElement;
+  ParamsResolved: TPasTypeArray;
+  ResolvedEl: TPasResolverResult;
+  SpecializedTypes: TObjectList;
+  Item: TPSSpecializedItem;
+begin
+  Result:=nil;
+  if El.CustomData<>nil then
+    RaiseInternalError(20190726142522);
+
+  CheckSpecializeConstraints(El);
+
+  // spezialize: parsing implementation must be delayed until implementation section is complete
+  GenericType:=El.DestType as TPasGenericType;
+  if not (GenericType.CustomData is TPasGenericScope) then
+    RaiseMsg(20190726194316,nNotYetImplemented,sNotYetImplemented,[GetObjName(GenericType.CustomData)],El);
+  GenScope:=TPasGenericScope(GenericType.CustomData);
+  Params:=El.Params;
+  SetLength(ParamsResolved,Params.Count);
+  for i:=0 to Params.Count-1 do
+    begin
+    Param:=TPasElement(Params[i]);
+    ComputeElement(Param,ResolvedEl,[rcType]);
+    ParamsResolved[i]:=ResolvedEl.LoTypeEl;
+    end;
+  SpecializedTypes:=GenScope.SpecializedTypes;
+  if SpecializedTypes=nil then
+    begin
+    SpecializedTypes:=TObjectList.Create(true);
+    GenScope.SpecializedTypes:=SpecializedTypes;
+    end;
+  i:=SpecializedTypes.Count-1;
+  Item:=nil;
+  while i>=0 do
+    begin
+    Item:=TPSSpecializedItem(SpecializedTypes[i]);
+    j:=length(Item.Params);
+    while (j>=0) and (Item.Params[j]=ParamsResolved[j]) do dec(j);
+    if j<0 then
+      break;
+    Item:=nil;
+    dec(i);
+    end;
+  if Item<>nil then
+    begin
+    // already specialized
+    Result:=Item.SpecializedType;
+    end
+  else
+    begin
+    // new specialization
+    Item:=TPSSpecializedItem.Create;
+    Item.Params:=ParamsResolved;
+    SpecializedTypes.Add(Item);
+    // ToDo: create specilized type
+    RaiseMsg(20190726141738,nNotYetImplemented,sNotYetImplemented,['specialize'],El);
+    end;
+
+  Data:=TPasSpecializeTypeData.Create;
+  // add to free list
+  AddResolveData(El,Data,lkModule);
+  Data.SpecializedType:=Result;
+end;
+
+procedure TPasResolver.CheckSpecializeConstraints(El: TPasSpecializeType);
+var
+  Params, GenericTemplateList: TFPList;
+  i, j: Integer;
+  P: TPasElement;
+  ParamType, DestType: TPasType;
+  ResolvedEl, ResolvedConstraint: TPasResolverResult;
+  GenTempl: TPasGenericTemplateType;
+  ConExpr: TPasExpr;
+  Value: String;
+  ConstraintClass: TPasClassType;
+begin
+  Params:=El.Params;
+  DestType:=El.DestType;
+  if not (DestType is TPasGenericType) then
+    RaiseMsg(20190726193025,nXExpectedButYFound,sXExpectedButYFound,['generic type',DestType.Name],El);
+  GenericTemplateList:=TPasGenericType(DestType).GenericTemplateTypes;
+  if GenericTemplateList=nil then
+    RaiseMsg(20190726193048,nXExpectedButYFound,sXExpectedButYFound,['generic templates',DestType.Name],El);
+  if GenericTemplateList.Count<>Params.Count then
+    RaiseMsg(20190726193107,nXExpectedButYFound,sXExpectedButYFound,['type with '+IntToStr(Params.Count)+' generic templates',DestType.Name],El);
+
+  // check constraints
+  for i:=0 to Params.Count-1 do
+    begin
+    P:=TPasElement(Params[i]);
+    if P is TPasType then
+      ParamType:=TPasType(P)
+    else if P is TPasExpr then
+      begin
+      ComputeElement(P,ResolvedEl,[rcType]);
+      if not (ResolvedEl.IdentEl is TPasType) then
+        RaiseMsg(20190725195434,nXExpectedButYFound,sXExpectedButYFound,['type',GetResolverResultDescription(ResolvedEl)],P);
+      ParamType:=TPasType(ResolvedEl.IdentEl);
+      end;
+    GenTempl:=TPasGenericTemplateType(GenericTemplateList[i]);
+    for j:=0 to length(GenTempl.Constraints)-1 do
+      begin
+      ConExpr:=GenTempl.Constraints[j];
+      if (ConExpr.Kind=pekIdent) then
+        begin
+        Value:=TPrimitiveExpr(ConExpr).Value;
+        if SameText(Value,'record') then
+          begin
+          if not (ParamType is TPasRecordType) then
+            RaiseMsg(20190725200015,nXExpectedButYFound,sXExpectedButYFound,['record type',ParamType.Name],P);
+          continue;
+          end
+        else if SameText(Value,'class') or SameText(Value,'constructor') then
+          begin
+          if not (ParamType is TPasClassType) then
+            RaiseMsg(20190726133231,nXExpectedButYFound,sXExpectedButYFound,['class type',ParamType.Name],P);
+          if TPasClassType(ParamType).ObjKind<>okClass then
+            RaiseMsg(20190726133232,nXExpectedButYFound,sXExpectedButYFound,['class type',ParamType.Name],P);
+          if TPasClassType(ParamType).IsExternal then
+            RaiseMsg(20190726133233,nXExpectedButYFound,sXExpectedButYFound,['class type',ParamType.Name],P);
+          if SameText(Value,'constructor') then
+            begin
+            // check if ParamType has the default constructor
+            // ToDo
+            RaiseMsg(20190726133722,nXIsNotSupported,sXIsNotSupported,['constraint keyword construcor'],P);
+            end;
+          continue;
+          end;
+        end;
+      // constraint can be a class type or interface type
+      // Param must be a class
+      ComputeElement(ConExpr,ResolvedConstraint,[rcType]);
+      if ResolvedConstraint.IdentEl=nil then
+        RaiseMsg(20190726134037,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],P);
+      if not (ResolvedConstraint.LoTypeEl is TPasClassType) then
+        RaiseMsg(20190726134223,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],P);
+      ConstraintClass:=TPasClassType(ResolvedConstraint.LoTypeEl);
+      if not (ParamType is TPasClassType) then
+        RaiseIncompatibleType(20190726135859,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,P);
+      case ConstraintClass.ObjKind of
+      okClass:
+        // Param must be a ConstraintClass
+        if CheckClassIsClass(ParamType,ConstraintClass)=cIncompatible then
+          RaiseIncompatibleType(20190726135309,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,P);
+      okInterface:
+        // ParamType must implement ConstraintClass
+        if GetClassImplementsIntf(TPasClassType(ParamType),ConstraintClass)=nil then
+          RaiseIncompatibleType(20190726135458,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,P);
+      else
+        RaiseIncompatibleType(20190726135310,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,P);
+      end;
+      end;
+    end;
+end;
+
 function TPasResolver.CheckAssignCompatibilityCustom(const LHS,
 function TPasResolver.CheckAssignCompatibilityCustom(const LHS,
   RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean;
   RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean;
   var Handled: boolean): integer;
   var Handled: boolean): integer;
@@ -15955,8 +16270,12 @@ begin
         or (AClass=TPasProcedureType)
         or (AClass=TPasProcedureType)
         or (AClass=TPasFunctionType)
         or (AClass=TPasFunctionType)
         or (AClass=TPasSetType)
         or (AClass=TPasSetType)
-        or (AClass=TPasRangeType) then
+        or (AClass=TPasRangeType)
+        or (AClass=TPasSpecializeType) then
       AddType(TPasType(El))
       AddType(TPasType(El))
+    else if AClass=TPasGenericTemplateType then
+      // TPasParser first collects template types and later adds them as a list
+      // they are not real types
     else if AClass=TPasStringType then
     else if AClass=TPasStringType then
       begin
       begin
       AddType(TPasType(El));
       AddType(TPasType(El));
@@ -16003,8 +16322,6 @@ begin
       // resolved when finished
       // resolved when finished
     else if AClass=TPasImplCommand then
     else if AClass=TPasImplCommand then
     else if AClass=TPasAttributes then
     else if AClass=TPasAttributes then
-    else if AClass=TPasGenericTemplateType then
-      AddType(TPasType(El))
     else if AClass=TPasUnresolvedUnitRef then
     else if AClass=TPasUnresolvedUnitRef then
       RaiseMsg(20171018121900,nCantFindUnitX,sCantFindUnitX,[AName],El)
       RaiseMsg(20171018121900,nCantFindUnitX,sCantFindUnitX,[AName],El)
     else
     else
@@ -16687,6 +17004,11 @@ begin
   stTypeSection: FinishTypeSection(El);
   stTypeSection: FinishTypeSection(El);
   stTypeDef: FinishTypeDef(El as TPasType);
   stTypeDef: FinishTypeDef(El as TPasType);
   stResourceString: FinishResourcestring(El as TPasResString);
   stResourceString: FinishResourcestring(El as TPasResString);
+  stGenericTypeTemplates:
+    if El is TPasGenericType then
+      FinishGenericTemplateTypes(TPasGenericType(El))
+    else
+      FinishProcNameParts(El as TPasProcedure);
   stProcedure: FinishProcedure(El as TPasProcedure);
   stProcedure: FinishProcedure(El as TPasProcedure);
   stProcedureHeader: FinishProcedureType(El as TPasProcedureType);
   stProcedureHeader: FinishProcedureType(El as TPasProcedureType);
   stExceptOnExpr: FinishExceptOnExpr;
   stExceptOnExpr: FinishExceptOnExpr;

+ 90 - 115
packages/fcl-passrc/src/pastree.pp

@@ -508,6 +508,7 @@ type
   public
   public
     function ElementTypeName: string; override;
     function ElementTypeName: string; override;
   end;
   end;
+  TPasTypeArray = array of TPasType;
 
 
   { TPasAliasType }
   { TPasAliasType }
 
 
@@ -549,12 +550,33 @@ type
     function ElementTypeName: string; override;
     function ElementTypeName: string; override;
   end;
   end;
 
 
-  { TPasClassOfType }
+  { TPasGenericTemplateType }
 
 
-  TPasClassOfType = class(TPasAliasType)
+  TPasGenericTemplateType = Class(TPasType)
   public
   public
-    function ElementTypeName: string; override;
-    function GetDeclaration(full: boolean) : string; override;
+    destructor Destroy; override;
+    function GetDeclaration(full : boolean) : string; override;
+    procedure ForEachCall(const aMethodCall: TOnForEachPasElement;
+      const Arg: Pointer); override;
+    procedure AddConstraint(Expr: TPasExpr);
+  Public
+    TypeConstraint: String deprecated; // deprecated in fpc 3.3.1
+    Constraints: TPasExprArray;
+  end;
+
+  { TPasGenericType }
+
+  TPasGenericType = class(TPasType)
+  private
+    procedure ClearChildReferences(El: TPasElement; arg: pointer);
+  protected
+    procedure SetParent(const AValue: TPasElement); override;
+  public
+    GenericTemplateTypes: TFPList; // list of TPasGenericTemplateType, can be nil
+    destructor Destroy; override;
+    procedure ForEachCall(const aMethodCall: TOnForEachPasElement;
+      const Arg: Pointer); override;
+    procedure SetGenericTemplates(AList: TFPList); virtual;
   end;
   end;
 
 
   { TPasSpecializeType DestType<Params> }
   { TPasSpecializeType DestType<Params> }
@@ -588,6 +610,14 @@ type
     Params: TFPList; // list of TPasType or TPasExpr
     Params: TFPList; // list of TPasType or TPasExpr
   end;
   end;
 
 
+  { TPasClassOfType }
+
+  TPasClassOfType = class(TPasAliasType)
+  public
+    function ElementTypeName: string; override;
+    function GetDeclaration(full: boolean) : string; override;
+  end;
+
   { TPasRangeType }
   { TPasRangeType }
 
 
   TPasRangeType = class(TPasType)
   TPasRangeType = class(TPasType)
@@ -605,27 +635,19 @@ type
 
 
   { TPasArrayType }
   { TPasArrayType }
 
 
-  TPasArrayType = class(TPasType)
-  private
-    procedure ClearChildReferences(El: TPasElement; arg: pointer);
-  protected
-    procedure SetParent(const AValue: TPasElement); override;
+  TPasArrayType = class(TPasGenericType)
   public
   public
     destructor Destroy; override;
     destructor Destroy; override;
     function ElementTypeName: string; override;
     function ElementTypeName: string; override;
     function GetDeclaration(full : boolean) : string; override;
     function GetDeclaration(full : boolean) : string; override;
-    procedure ForEachCall(const aMethodCall: TOnForEachPasElement;
-      const Arg: Pointer); override;
   public
   public
     IndexRange : string; // only valid if Parser po_arrayrangeexpr disabled
     IndexRange : string; // only valid if Parser po_arrayrangeexpr disabled
     Ranges: TPasExprArray; // only valid if Parser po_arrayrangeexpr enabled
     Ranges: TPasExprArray; // only valid if Parser po_arrayrangeexpr enabled
     PackMode : TPackMode;
     PackMode : TPackMode;
     ElType: TPasType;
     ElType: TPasType;
-    GenericTemplateTypes: TFPList; // list of TPasGenericTemplateType, can be nil
     function IsGenericArray : Boolean;
     function IsGenericArray : Boolean;
     function IsPacked : Boolean;
     function IsPacked : Boolean;
     procedure AddRange(Range: TPasExpr);
     procedure AddRange(Range: TPasExpr);
-    procedure SetGenericTemplates(AList: TFPList); virtual;
   end;
   end;
 
 
   { TPasFileType }
   { TPasFileType }
@@ -701,22 +723,16 @@ type
 
 
   { TPasMembersType - base type for TPasRecordType and TPasClassType }
   { TPasMembersType - base type for TPasRecordType and TPasClassType }
 
 
-  TPasMembersType = class(TPasType)
-  private
-    procedure ClearChildReferences(El: TPasElement; arg: pointer);
-  protected
-    procedure SetParent(const AValue: TPasElement); override;
+  TPasMembersType = class(TPasGenericType)
   public
   public
     PackMode: TPackMode;
     PackMode: TPackMode;
     Members: TFPList;
     Members: TFPList;
-    GenericTemplateTypes: TFPList; // list of TPasGenericTemplateType
     Constructor Create(const AName: string; AParent: TPasElement); override;
     Constructor Create(const AName: string; AParent: TPasElement); override;
     Destructor Destroy; override;
     Destructor Destroy; override;
     Function IsPacked: Boolean;
     Function IsPacked: Boolean;
     Function IsBitPacked : Boolean;
     Function IsBitPacked : Boolean;
     Procedure ForEachCall(const aMethodCall: TOnForEachPasElement;
     Procedure ForEachCall(const aMethodCall: TOnForEachPasElement;
       const Arg: Pointer); override;
       const Arg: Pointer); override;
-    Procedure SetGenericTemplates(AList: TFPList); virtual;
   end;
   end;
 
 
   { TPasRecordType }
   { TPasRecordType }
@@ -737,23 +753,9 @@ type
     Function IsAdvancedRecord : Boolean;
     Function IsAdvancedRecord : Boolean;
   end;
   end;
 
 
-  { TPasGenericTemplateType }
-
-  TPasGenericTemplateType = Class(TPasType)
-  public
-    destructor Destroy; override;
-    function GetDeclaration(full : boolean) : string; override;
-    procedure ForEachCall(const aMethodCall: TOnForEachPasElement;
-      const Arg: Pointer); override;
-    procedure AddConstraint(Expr: TPasExpr);
-  Public
-    TypeConstraint: String deprecated; // deprecated in fpc 3.3.1
-    Constraints: TPasExprArray;
-  end;
-
   TPasObjKind = (
   TPasObjKind = (
     okObject, okClass, okInterface,
     okObject, okClass, okInterface,
-    // okGeneric  removed in FPC 3.3.1  check instead GenericTemplateTypes.Count>0
+    // okGeneric  removed in FPC 3.3.1  check instead GenericTemplateTypes<>nil
     // okSpecialize removed in FPC 3.1.1
     // okSpecialize removed in FPC 3.1.1
     okClassHelper,okRecordHelper,okTypeHelper,
     okClassHelper,okRecordHelper,okTypeHelper,
     okDispInterface);
     okDispInterface);
@@ -823,7 +825,7 @@ type
 
 
   { TPasProcedureType }
   { TPasProcedureType }
 
 
-  TPasProcedureType = class(TPasType)
+  TPasProcedureType = class(TPasGenericType)
   private
   private
     function GetIsNested: Boolean;
     function GetIsNested: Boolean;
     function GetIsOfObject: Boolean;
     function GetIsOfObject: Boolean;
@@ -1845,6 +1847,58 @@ begin
     end;
     end;
 end;
 end;
 
 
+{ TPasGenericType }
+
+procedure TPasGenericType.ClearChildReferences(El: TPasElement; arg: pointer);
+begin
+  El.ClearTypeReferences(Self);
+  if arg=nil then ;
+end;
+
+procedure TPasGenericType.SetParent(const AValue: TPasElement);
+begin
+  if (AValue=nil) and (Parent<>nil) then
+    begin
+    // parent is cleared
+    // -> clear all child references to this array (releasing loops)
+    ForEachCall(@ClearChildReferences,nil);
+    end;
+  inherited SetParent(AValue);
+end;
+
+destructor TPasGenericType.Destroy;
+begin
+  ReleaseGenericTemplateTypes(GenericTemplateTypes{$IFDEF CheckPasTreeRefCount},'TPasGenericType'{$ENDIF});
+  inherited Destroy;
+end;
+
+procedure TPasGenericType.ForEachCall(const aMethodCall: TOnForEachPasElement;
+  const Arg: Pointer);
+var
+  i: Integer;
+begin
+  inherited ForEachCall(aMethodCall, Arg);
+  if GenericTemplateTypes<>nil then
+    for i:=0 to GenericTemplateTypes.Count-1 do
+      ForEachChildCall(aMethodCall,Arg,TPasElement(GenericTemplateTypes[i]),false);
+end;
+
+procedure TPasGenericType.SetGenericTemplates(AList: TFPList);
+var
+  I: Integer;
+  El: TPasElement;
+begin
+  if GenericTemplateTypes=nil then
+    GenericTemplateTypes:=TFPList.Create;
+  For I:=0 to AList.Count-1 do
+    begin
+    El:=TPasElement(AList[i]);
+    El.Parent:=Self;
+    GenericTemplateTypes.Add(El);
+    end;
+  AList.Clear;
+end;
+
 { TPasGenericTemplateType }
 { TPasGenericTemplateType }
 
 
 destructor TPasGenericTemplateType.Destroy;
 destructor TPasGenericTemplateType.Destroy;
@@ -3078,28 +3132,10 @@ begin
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
-procedure TPasArrayType.ClearChildReferences(El: TPasElement; arg: pointer);
-begin
-  El.ClearTypeReferences(Self);
-  if arg=nil then ;
-end;
-
-procedure TPasArrayType.SetParent(const AValue: TPasElement);
-begin
-  if (AValue=nil) and (Parent<>nil) then
-    begin
-    // parent is cleared
-    // -> clear all child references to this array (releasing loops)
-    ForEachCall(@ClearChildReferences,nil);
-    end;
-  inherited SetParent(AValue);
-end;
-
 destructor TPasArrayType.Destroy;
 destructor TPasArrayType.Destroy;
 var
 var
   i: Integer;
   i: Integer;
 begin
 begin
-  ReleaseGenericTemplateTypes(GenericTemplateTypes{$IFDEF CheckPasTreeRefCount},'TPasArrayType'{$ENDIF});
   for i:=0 to length(Ranges)-1 do
   for i:=0 to length(Ranges)-1 do
     Ranges[i].Release{$IFDEF CheckPasTreeRefCount}('TPasArrayType.Ranges'){$ENDIF};
     Ranges[i].Release{$IFDEF CheckPasTreeRefCount}('TPasArrayType.Ranges'){$ENDIF};
   ReleaseAndNil(TPasElement(ElType){$IFDEF CheckPasTreeRefCount},'TPasArrayType.ElType'{$ENDIF});
   ReleaseAndNil(TPasElement(ElType){$IFDEF CheckPasTreeRefCount},'TPasArrayType.ElType'{$ENDIF});
@@ -4088,18 +4124,6 @@ begin
     Result:=Result+'const';
     Result:=Result+'const';
 end;
 end;
 
 
-procedure TPasArrayType.ForEachCall(const aMethodCall: TOnForEachPasElement;
-  const Arg: Pointer);
-var
-  i: Integer;
-begin
-  inherited ForEachCall(aMethodCall, Arg);
-  if GenericTemplateTypes<>nil then
-    for i:=0 to GenericTemplateTypes.Count-1 do
-      ForEachChildCall(aMethodCall,Arg,TPasElement(GenericTemplateTypes[i]),false);
-  ForEachChildCall(aMethodCall,Arg,ElType,true);
-end;
-
 function TPasArrayType.IsGenericArray: Boolean;
 function TPasArrayType.IsGenericArray: Boolean;
 begin
 begin
   Result:=GenericTemplateTypes<>nil;
   Result:=GenericTemplateTypes<>nil;
@@ -4119,22 +4143,6 @@ begin
   Ranges[i]:=Range;
   Ranges[i]:=Range;
 end;
 end;
 
 
-procedure TPasArrayType.SetGenericTemplates(AList: TFPList);
-var
-  I: Integer;
-  El: TPasElement;
-begin
-  if GenericTemplateTypes=nil then
-    GenericTemplateTypes:=TFPList.Create;
-  For I:=0 to AList.Count-1 do
-    begin
-    El:=TPasElement(AList[i]);
-    El.Parent:=Self;
-    GenericTemplateTypes.Add(El);
-    end;
-  AList.Clear;
-end;
-
 function TPasFileType.GetDeclaration (full : boolean) : string;
 function TPasFileType.GetDeclaration (full : boolean) : string;
 begin
 begin
   Result:='File';
   Result:='File';
@@ -4224,23 +4232,6 @@ end;
 
 
 { TPasMembersType }
 { TPasMembersType }
 
 
-procedure TPasMembersType.ClearChildReferences(El: TPasElement; arg: pointer);
-begin
-  El.ClearTypeReferences(Self);
-  if arg=nil then ;
-end;
-
-procedure TPasMembersType.SetParent(const AValue: TPasElement);
-begin
-  if (AValue=nil) and (Parent<>nil) then
-    begin
-    // parent is cleared
-    // -> clear all child references to this class/record (releasing loops)
-    ForEachCall(@ClearChildReferences,nil);
-    end;
-  inherited SetParent(AValue);
-end;
-
 constructor TPasMembersType.Create(const AName: string; AParent: TPasElement);
 constructor TPasMembersType.Create(const AName: string; AParent: TPasElement);
 begin
 begin
   inherited Create(AName, AParent);
   inherited Create(AName, AParent);
@@ -4284,26 +4275,10 @@ var
   i: Integer;
   i: Integer;
 begin
 begin
   inherited ForEachCall(aMethodCall, Arg);
   inherited ForEachCall(aMethodCall, Arg);
-  for i:=0 to GenericTemplateTypes.Count-1 do
-    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;
 
 
-procedure TPasMembersType.SetGenericTemplates(AList: TFPList);
-var
-  I: Integer;
-  El: TPasElement;
-begin
-  For I:=0 to AList.Count-1 do
-    begin
-    El:=TPasElement(AList[i]);
-    El.Parent:=Self;
-    GenericTemplateTypes.Add(El);
-    end;
-  AList.Clear;
-end;
-
 { TPasRecordType }
 { TPasRecordType }
 
 
 procedure TPasRecordType.GetMembers(S: TStrings);
 procedure TPasRecordType.GetMembers(S: TStrings);

+ 32 - 7
packages/fcl-passrc/src/pparser.pp

@@ -169,6 +169,7 @@ type
     stTypeSection,
     stTypeSection,
     stTypeDef, // e.g. a TPasType
     stTypeDef, // e.g. a TPasType
     stResourceString, // e.g. TPasResString
     stResourceString, // e.g. TPasResString
+    stGenericTypeTemplates, // called after TPasGenericType.SetGenericTemplates or TPasProcedure.setNameParts
     stProcedure, // also method, procedure, constructor, destructor, ...
     stProcedure, // also method, procedure, constructor, destructor, ...
     stProcedureHeader,
     stProcedureHeader,
     stWithExpr, // calls BeginScope after parsing every WITH-expression
     stWithExpr, // calls BeginScope after parsing every WITH-expression
@@ -3354,6 +3355,13 @@ var
     Scanner.SetForceCaret(NewBlock=declType);
     Scanner.SetForceCaret(NewBlock=declType);
   end;
   end;
 
 
+  procedure InitGenericType(NewEl: TPasGenericType; GenericTemplateTypes: TFPList);
+  begin
+    Declarations.Declarations.Add(NewEl);
+    NewEl.SetGenericTemplates(GenericTemplateTypes);
+    Engine.FinishScope(stGenericTypeTemplates,NewEl);
+  end;
+
 var
 var
   ConstEl: TPasConst;
   ConstEl: TPasConst;
   ResStrEl: TPasResString;
   ResStrEl: TPasResString;
@@ -3365,13 +3373,14 @@ var
   ExpEl: TPasExportSymbol;
   ExpEl: TPasExportSymbol;
   PropEl : TPasProperty;
   PropEl : TPasProperty;
   TypeName: String;
   TypeName: String;
-  PT : TProcType;
+  PT , ProcType: TProcType;
   NamePos: TPasSourcePos;
   NamePos: TPasSourcePos;
   ok: Boolean;
   ok: Boolean;
   Proc: TPasProcedure;
   Proc: TPasProcedure;
   RecordEl: TPasRecordType;
   RecordEl: TPasRecordType;
   Attr: TPasAttributes;
   Attr: TPasAttributes;
   CurEl: TPasElement;
   CurEl: TPasElement;
+  ProcTypeEl: TPasProcedureType;
 begin
 begin
   CurBlock := declNone;
   CurBlock := declNone;
   HadTypeSection:=false;
   HadTypeSection:=false;
@@ -3600,9 +3609,8 @@ begin
               begin
               begin
               ClassEl := TPasClassType(CreateElement(TPasClassType,
               ClassEl := TPasClassType(CreateElement(TPasClassType,
                 TypeName, Declarations, NamePos));
                 TypeName, Declarations, NamePos));
-              Declarations.Declarations.Add(ClassEl);
               Declarations.Classes.Add(ClassEl);
               Declarations.Classes.Add(ClassEl);
-              ClassEl.SetGenericTemplates(List);
+              InitGenericType(ClassEl,List);
               NextToken;
               NextToken;
               DoParseClassType(ClassEl);
               DoParseClassType(ClassEl);
               CheckHint(ClassEl,True);
               CheckHint(ClassEl,True);
@@ -3612,9 +3620,8 @@ begin
              begin
              begin
              RecordEl := TPasRecordType(CreateElement(TPasRecordType,
              RecordEl := TPasRecordType(CreateElement(TPasRecordType,
                TypeName, Declarations, NamePos));
                TypeName, Declarations, NamePos));
-             Declarations.Declarations.Add(RecordEl);
              Declarations.Classes.Add(RecordEl);
              Declarations.Classes.Add(RecordEl);
-             RecordEl.SetGenericTemplates(List);
+             InitGenericType(RecordEl,List);
              NextToken;
              NextToken;
              ParseRecordMembers(RecordEl,tkend,
              ParseRecordMembers(RecordEl,tkend,
                               (msAdvancedRecords in Scanner.CurrentModeSwitches)
                               (msAdvancedRecords in Scanner.CurrentModeSwitches)
@@ -3626,13 +3633,28 @@ begin
            tkArray:
            tkArray:
              begin
              begin
              ArrEl := TPasArrayType(CreateElement(TPasArrayType, TypeName, Declarations, NamePos));
              ArrEl := TPasArrayType(CreateElement(TPasArrayType, TypeName, Declarations, NamePos));
-             Declarations.Declarations.Add(ArrEl);
              Declarations.Types.Add(ArrEl);
              Declarations.Types.Add(ArrEl);
-             ArrEl.SetGenericTemplates(List);
+             InitGenericType(ArrEl,List);
              DoParseArrayType(ArrEl);
              DoParseArrayType(ArrEl);
              CheckHint(ArrEl,True);
              CheckHint(ArrEl,True);
              Engine.FinishScope(stTypeDef,ArrEl);
              Engine.FinishScope(stTypeDef,ArrEl);
              end;
              end;
+          tkprocedure,tkfunction:
+            begin
+            if CurToken=tkFunction then
+              begin
+              ProcTypeEl := CreateFunctionType(TypeName, 'Result', Declarations, False, NamePos);
+              ProcType:=ptFunction;
+              end
+            else
+              begin
+              ProcTypeEl := TPasProcedureType(CreateElement(TPasProcedureType, TypeName, Declarations, NamePos));
+              ProcType:=ptProcedure;
+              end;
+            Declarations.Functions.Add(ProcTypeEl);
+            InitGenericType(ProcTypeEl,List);
+            ParseProcedureOrFunction(ProcTypeEl, ProcTypeEl, ProcType, True);
+            end;
           else
           else
             ParseExc(nParserGenericClassOrArray,SParserGenericClassOrArray);
             ParseExc(nParserGenericClassOrArray,SParserGenericClassOrArray);
           end;
           end;
@@ -6389,7 +6411,10 @@ begin
       Parent:=CheckIfOverLoaded(Parent,Name);
       Parent:=CheckIfOverLoaded(Parent,Name);
     Result:=TPasProcedure(CreateElement(PC,Name,Parent,AVisibility));
     Result:=TPasProcedure(CreateElement(PC,Name,Parent,AVisibility));
     if NameParts<>nil then
     if NameParts<>nil then
+      begin
       Result.SetNameParts(NameParts);
       Result.SetNameParts(NameParts);
+      Engine.FinishScope(stGenericTypeTemplates,Result);
+      end;
 
 
     case ProcType of
     case ProcType of
     ptFunction, ptClassFunction, ptOperator, ptClassOperator, ptAnonymousFunction:
     ptFunction, ptClassFunction, ptOperator, ptClassOperator, ptAnonymousFunction:

+ 13 - 1
packages/fcl-passrc/tests/tcgenerics.pp

@@ -16,8 +16,9 @@ Type
     Procedure TestObjectGenerics;
     Procedure TestObjectGenerics;
     Procedure TestRecordGenerics;
     Procedure TestRecordGenerics;
     Procedure TestArrayGenerics;
     Procedure TestArrayGenerics;
+    Procedure TestProcTypeGenerics;
     Procedure TestGenericConstraint;
     Procedure TestGenericConstraint;
-    Procedure TestGenericInterfaceConstraint; // ToDo
+    Procedure TestGenericInterfaceConstraint;
     Procedure TestDeclarationConstraint;
     Procedure TestDeclarationConstraint;
     Procedure TestSpecializationDelphi;
     Procedure TestSpecializationDelphi;
     Procedure TestDeclarationDelphi;
     Procedure TestDeclarationDelphi;
@@ -61,6 +62,17 @@ begin
   Add([
   Add([
     'Type',
     'Type',
     '  Generic TSome<T> = array of T;',
     '  Generic TSome<T> = array of T;',
+    '  Generic TStatic<R,T> = array[R] of T;',
+    '']);
+  ParseDeclarations;
+end;
+
+procedure TTestGenerics.TestProcTypeGenerics;
+begin
+  Add([
+    'Type',
+    '  Generic TSome<T> = procedure(v: T);',
+    '  Generic TFunc<R,T> = function(b: R): T;',
     '']);
     '']);
   ParseDeclarations;
   ParseDeclarations;
 end;
 end;

+ 79 - 1
packages/fcl-passrc/tests/tcresolvegenerics.pas

@@ -5,7 +5,7 @@ unit tcresolvegenerics;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, testregistry, tcresolver, PasResolveEval;
+  Classes, SysUtils, testregistry, tcresolver, PasResolveEval, PParser;
 
 
 type
 type
 
 
@@ -14,13 +14,21 @@ type
   TTestResolveGenerics = Class(TCustomTestResolver)
   TTestResolveGenerics = Class(TCustomTestResolver)
   Published
   Published
     procedure TestGen_GenericFunction; // ToDo
     procedure TestGen_GenericFunction; // ToDo
+    procedure TestGen_MissingTemplateFail;
     procedure TestGen_ConstraintStringFail;
     procedure TestGen_ConstraintStringFail;
     procedure TestGen_ConstraintMultiClassFail;
     procedure TestGen_ConstraintMultiClassFail;
+    procedure TestGen_ConstraintRecordExpectedFail;
     // ToDo: constraint keyword record
     // ToDo: constraint keyword record
     // ToDo: constraint keyword class, constructor, class+constructor
     // ToDo: constraint keyword class, constructor, class+constructor
     // ToDo: constraint Unit2.TBird
     // ToDo: constraint Unit2.TBird
     // ToDo: constraint Unit2.TGen<word>
     // ToDo: constraint Unit2.TGen<word>
+    procedure TestGen_GenericNotFoundFail;
+    procedure TestGen_RecordLocalNameDuplicateFail;
+    procedure TestGen_Record;
+    // ToDo: generic class
+    // ToDo: generic interface
     // ToDo: generic array
     // ToDo: generic array
+    // ToDo: generic procedure type
   end;
   end;
 
 
 implementation
 implementation
@@ -44,6 +52,16 @@ begin
   ParseProgram;
   ParseProgram;
 end;
 end;
 
 
+procedure TTestResolveGenerics.TestGen_MissingTemplateFail;
+begin
+  StartProgram(false);
+  Add([
+  'type generic g< > = array of word;',
+  'begin',
+  '']);
+  CheckParserException('Expected "Identifier"',nParserExpectTokenError);
+end;
+
 procedure TTestResolveGenerics.TestGen_ConstraintStringFail;
 procedure TTestResolveGenerics.TestGen_ConstraintStringFail;
 begin
 begin
   StartProgram(false);
   StartProgram(false);
@@ -77,6 +95,66 @@ begin
     nConstraintXAndConstraintYCannotBeTogether);
     nConstraintXAndConstraintYCannotBeTogether);
 end;
 end;
 
 
+procedure TTestResolveGenerics.TestGen_ConstraintRecordExpectedFail;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  generic TBird<T:record> = record v: T; end;',
+  'var r: specialize TBird<word>;',
+  'begin',
+  '']);
+  CheckResolverException('record type expected, but Word found',
+    nXExpectedButYFound);
+end;
+
+procedure TTestResolveGenerics.TestGen_GenericNotFoundFail;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  TBird = specialize TAnimal<word>;',
+  'begin',
+  '']);
+  CheckResolverException('identifier not found "TAnimal"',
+    nIdentifierNotFound);
+end;
+
+procedure TTestResolveGenerics.TestGen_RecordLocalNameDuplicateFail;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  generic TBird<T> = record T: word; end;',
+  'begin',
+  '']);
+  CheckResolverException('Duplicate identifier "T" at afile.pp(4,18)',
+    nDuplicateIdentifier);
+end;
+
+procedure TTestResolveGenerics.TestGen_Record;
+begin
+  exit; // ToDo
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  {#Typ}T = word;',
+  '  generic TRec<{#Templ}T> = record',
+  '    {=Templ}v: T;',
+  '  end;',
+  'var',
+  '  r: specialize TRec<word>;',
+  '  {=Typ}w: T;',
+  'begin',
+  '  r.v:=w;',
+  '']);
+  ParseProgram;
+end;
+
 initialization
 initialization
   RegisterTests([TTestResolveGenerics]);
   RegisterTests([TTestResolveGenerics]);