瀏覽代碼

fcl-passrc: resolver: nested specialize

git-svn-id: trunk@42599 -
Mattias Gaertner 6 年之前
父節點
當前提交
c31774eac4

+ 3 - 3
packages/fcl-passrc/src/pasresolveeval.pas

@@ -332,9 +332,9 @@ resourcestring
   sIllegalExpressionAfterX = 'illegal expression after %s';
   sMethodHidesNonVirtualMethodExactly = 'method hides identifier at "%s". Use reintroduce';
   sDuplicatePublishedMethodXAtY = 'Duplicate published method "%s" at %s';
-  sConstraintXSpecifiedMoreThanOnce = 'Constraint ''%s'' specified more than once';
-  sConstraintXAndConstraintYCannotBeTogether = '''%s'' constraint and ''%s'' constraint cannot be specified together';
-  sXIsNotAValidConstraint = '''%s'' is not a valid constraint';
+  sConstraintXSpecifiedMoreThanOnce = 'Constraint "%s" specified more than once';
+  sConstraintXAndConstraintYCannotBeTogether = '"%s" constraint and "%s" constraint cannot be specified together';
+  sXIsNotAValidConstraint = '"%s" is not a valid constraint';
   sWrongNumberOfParametersForGenericType = 'wrong number of parameters for generic type %s';
   sGenericsWithoutSpecializationAsType = 'Generics without specialization cannot be used as a type for a %s';
 

+ 92 - 68
packages/fcl-passrc/src/pasresolver.pp

@@ -1883,7 +1883,7 @@ type
     function FindUnit(const AName, InFilename: String;
       NameExpr, InFileExpr: TPasExpr): TPasModule; virtual; abstract;
     function FindElement(const aName: String): TPasElement; override;  // used by TPasParser
-    function FindElementFor(const aName: String; AParent: TPasElement): TPasElement; override; // used by TPasParser
+    function FindElementFor(const aName: String; AParent: TPasElement; TypeParamCount: integer): TPasElement; override; // used by TPasParser
     function FindElementWithoutParams(const AName: String; ErrorPosEl: TPasElement;
       NoProcsWithArgs: boolean): TPasElement;
     function FindElementWithoutParams(const AName: String; out Data: TPRFindData;
@@ -1990,6 +1990,7 @@ type
     procedure RaiseInvalidScopeForElement(id: TMaxPrecInt; El: TPasElement; const Msg: string = '');
     procedure RaiseIdentifierNotFound(id: TMaxPrecInt; Identifier: string; El: TPasElement);
     procedure RaiseXExpectedButYFound(id: TMaxPrecInt; const X,Y: string; El: TPasElement);
+    procedure RaiseXExpectedButTypeYFound(id: TMaxPrecInt; const X: string; Y: TPasType; El: TPasElement);
     procedure RaiseContextXExpectedButYFound(id: TMaxPrecInt; const C,X,Y: string; El: TPasElement);
     procedure RaiseContextXInvalidY(id: TMaxPrecInt; const X,Y: string; El: TPasElement);
     procedure RaiseConstantExprExp(id: TMaxPrecInt; ErrorEl: TPasElement);
@@ -2011,6 +2012,7 @@ type
     procedure RaiseInvalidProcModifier(id: TMaxPrecInt; Proc: TPasProcedure;
       pm: TProcedureModifier; ErrorEl: TPasElement);
     procedure WriteScopes;
+    procedure WriteScopesShort(Title: string);
     // find value and type of an element
     procedure ComputeElement(El: TPasElement; out ResolvedEl: TPasResolverResult;
       Flags: TPasResolverComputeFlags; StartEl: TPasElement = nil);
@@ -5160,24 +5162,30 @@ begin
 
   // check duplicate in current scope
   OlderIdentifier:=Identifier.NextSameIdentifier;
-  if (OlderIdentifier<>nil) then
-    if (Identifier.Kind=pikSimple)
-        or (OlderIdentifier.Kind=pikSimple)
-        or (El.Visibility=visPublished) then
-      begin
-      if (OlderIdentifier.Element.ClassType=TPasEnumValue)
-          and (OlderIdentifier.Element.Parent.Parent<>Scope.Element) then
-        // this enum was propagated from a sub type -> remove enum
-        Scope.RemoveLocalIdentifier(OlderIdentifier.Element);
-      if (El.Visibility=visPublished) and (El is TPasProcedure)
-          and (OlderIdentifier.Element is TPasProcedure) then
-        RaiseMsg(20190626175432,nDuplicatePublishedMethodXAtY,
-                 sDuplicatePublishedMethodXAtY,
-                 [aName,GetElementSourcePosStr(OlderIdentifier.Element)],El)
-      else
-        RaiseMsg(20170216151530,nDuplicateIdentifier,sDuplicateIdentifier,
+  if OlderIdentifier<>nil then
+    begin
+    if (OlderIdentifier.Element.ClassType=TPasEnumValue)
+        and (OlderIdentifier.Element.Parent.Parent<>Scope.Element) then
+      begin
+      // this enum was propagated from a sub type -> remove enum from this scope
+      if OlderIdentifier.NextSameIdentifier<>nil then
+        RaiseNotYetImplemented(20190807114726,El,GetElementSourcePosStr(OlderIdentifier.Element));
+      Scope.RemoveLocalIdentifier(OlderIdentifier.Element);
+      OlderIdentifier:=nil;
+      end;
+    if (El.Visibility=visPublished) and (El is TPasProcedure)
+        and (OlderIdentifier.Element is TPasProcedure) then
+      // published method bites method in same scope
+      RaiseMsg(20190626175432,nDuplicatePublishedMethodXAtY,
+               sDuplicatePublishedMethodXAtY,
                [aName,GetElementSourcePosStr(OlderIdentifier.Element)],El);
-      end;
+    if (Identifier.Kind=pikSimple)
+        or (OlderIdentifier.Kind=pikSimple) then
+      // duplicate identifier
+      RaiseMsg(20170216151530,nDuplicateIdentifier,sDuplicateIdentifier,
+              [aName,GetElementSourcePosStr(OlderIdentifier.Element)],El);
+
+    end;
 
   Result:=Identifier;
 end;
@@ -5914,23 +5922,17 @@ begin
         if SpecializedItem.Step<>psssNone then continue;
         OldStashCount:=InitSpecializeScopes(El);
         {$IFDEF VerbosePasResolver}
-        writeln('TPasResolver.FinishClassType Finishing specialize interface: ',GetObjName(SpecializedItem.SpecializedType),' ',ScopeCount,' FStashScopeCount=',FStashScopeCount);
-        for j:=0 to FScopeCount-1 do
-          writeln('  ',i,'/',FScopeCount,' ',GetObjName(FScopes[i]));
+        WriteScopesShort('TPasResolver.FinishClassType Finishing specialize interface: '+GetObjName(SpecializedItem.SpecializedType));
         {$ENDIF}
         SpecializeGenTypeIntf(El,SpecializedItem);
 
         {$IFDEF VerbosePasResolver}
-        writeln('TPasResolver.FinishClassType Finished specialize interface: ',GetObjName(SpecializedItem.SpecializedType),' ',ScopeCount,' FStashScopeCount=',FStashScopeCount);
-        for j:=0 to FScopeCount-1 do
-          writeln('  ',i,'/',FScopeCount,' ',GetObjName(FScopes[i]));
+        WriteScopesShort('TPasResolver.FinishClassType Finished specialize interface: '+GetObjName(SpecializedItem.SpecializedType));
         {$ENDIF}
 
         RestoreStashedScopes(OldStashCount);
         {$IFDEF VerbosePasResolver}
-        writeln('TPasResolver.FinishClassType RestoreStashedScopes ',GetObjName(SpecializedItem.SpecializedType),' ',ScopeCount,' FStashScopeCount=',FStashScopeCount);
-        for j:=0 to FScopeCount-1 do
-          writeln('  ',i,'/',FScopeCount,' ',GetObjName(FScopes[i]));
+        WriteScopesShort('TPasResolver.FinishClassType RestoreStashedScopes '+GetObjName(SpecializedItem.SpecializedType));
         {$ENDIF}
         end;
     end;
@@ -14329,17 +14331,10 @@ begin
   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
-    else
-      RaiseMsg(20190728114254,nXExpectedButYFound,sXExpectedButYFound,['type',GetResolverResultDescription(ResolvedEl)],P);
+    ComputeElement(P,ResolvedEl,[rcType]);
+    if not (ResolvedEl.IdentEl is TPasType) then
+      RaiseMsg(20190725195434,nXExpectedButYFound,sXExpectedButYFound,['type',GetResolverResultDescription(ResolvedEl)],P);
+    ParamType:=TPasType(ResolvedEl.IdentEl);
     if ParamType is TPasGenericTemplateType then
       begin
       // not fully specialized
@@ -14357,17 +14352,17 @@ begin
         if SameText(Value,'record') then
           begin
           if not (ParamType is TPasRecordType) then
-            RaiseMsg(20190725200015,nXExpectedButYFound,sXExpectedButYFound,['record type',ParamType.Name],P);
+            RaiseXExpectedButTypeYFound(20190725200015,'record type',ParamType,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);
+            RaiseXExpectedButTypeYFound(20190726133231,'class type',ParamType,P);
           if TPasClassType(ParamType).ObjKind<>okClass then
-            RaiseMsg(20190726133232,nXExpectedButYFound,sXExpectedButYFound,['class type',ParamType.Name],P);
+            RaiseXExpectedButTypeYFound(20190726133232,'class type',ParamType,P);
           if TPasClassType(ParamType).IsExternal then
-            RaiseMsg(20190726133233,nXExpectedButYFound,sXExpectedButYFound,['class type',ParamType.Name],P);
+            RaiseXExpectedButTypeYFound(20190726133233,'non external class type',ParamType,P);
           if SameText(Value,'constructor') then
             begin
             // check if ParamType has the default constructor
@@ -14424,9 +14419,6 @@ var
   SpecializedTypes: TObjectList;
   NewName: String;
   NewClass: TPTreeElement;
-  {$IFDEF VerbosePasResolver}
-  i: integer;
-  {$ENDIF}
   SrcModule: TPasModule;
   SrcModuleScope: TPasModuleScope;
   SrcResolver: TPasResolver;
@@ -14448,9 +14440,7 @@ begin
   //writeln('TPasResolver.CreateSpecializedType ',ScopeCount,' FStashScopeCount=',FStashScopeCount);
   OldStashCount:=InitSpecializeScopes(GenericType);
   {$IFDEF VerbosePasResolver}
-  writeln('TPasResolver.CreateSpecializedType InitSpecializeScopes: ',ScopeCount,' FStashScopeCount=',FStashScopeCount);
-  for i:=0 to FScopeCount-1 do
-    writeln('  ',i,'/',FScopeCount,' ',GetObjName(FScopes[i]));
+  WriteScopesShort('TPasResolver.CreateSpecializedType InitSpecializeScopes: ');
   {$ENDIF}
 
   Result:=TPSSpecializedItem.Create;
@@ -14480,16 +14470,12 @@ begin
     SpecializeGenTypeIntf(GenericType,Result);
 
   {$IFDEF VerbosePasResolver}
-  writeln('TPasResolver.CreateSpecializedType FinishTypeDef:');
-  for i:=0 to FScopeCount-1 do
-    writeln('  ',i,'/',FScopeCount,' ',GetObjName(FScopes[i]));
+  WriteScopesShort('TPasResolver.CreateSpecializedType FinishTypeDef:');
   {$ENDIF}
 
   RestoreStashedScopes(OldStashCount);
   {$IFDEF VerbosePasResolver}
-  writeln('TPasResolver.CreateSpecializedType RestoreStashedScopes:');
-  for i:=0 to FScopeCount-1 do
-    writeln('  ',i,'/',FScopeCount,' ',GetObjName(FScopes[i]));
+  WriteScopesShort('TPasResolver.CreateSpecializedType RestoreStashedScopes:');
   {$ENDIF}
 
   if GenScope.GenericStep>=psgsImplementationParsed then
@@ -17356,11 +17342,11 @@ end;
 
 function TPasResolver.FindElement(const aName: String): TPasElement;
 begin
-  Result:=FindElementFor(aName,nil);
+  Result:=FindElementFor(aName,nil,0);
 end;
 
-function TPasResolver.FindElementFor(const aName: String; AParent: TPasElement
-  ): TPasElement;
+function TPasResolver.FindElementFor(const aName: String; AParent: TPasElement;
+  TypeParamCount: integer): TPasElement;
 // called by TPasParser for direct types, e.g. type t = ns1.unit1.tobj.tsub
 var
   p: SizeInt;
@@ -17368,19 +17354,13 @@ var
   NeedPop: Boolean;
   CurScopeEl, NextEl, ErrorEl, BestEl: TPasElement;
   CurSection: TPasSection;
-  i, SpecArgCount: Integer;
+  i: Integer;
   UsesUnit: TPasUsesUnit;
   CurScope: TPasDotBaseScope;
 begin
   Result:=nil;
-  //writeln('TPasResolver.FindElement Name="',aName,'"');
   ErrorEl:=nil; // use nil to use scanner position as error position
 
-  if AParent is TPasSpecializeType then
-    SpecArgCount:=TPasSpecializeType(AParent).Params.Count
-  else
-    SpecArgCount:=0;
-
   RightPath:=aName;
   LeftPath:='';
   p:=1;
@@ -17430,8 +17410,8 @@ begin
     else
       NeedPop:=false;
 
-    if (SpecArgCount>0) and (RightPath='') then
-      NextEl:=FindGenericType(CurName,SpecArgCount,ErrorEl)
+    if (TypeParamCount>0) and (RightPath='') then
+      NextEl:=FindGenericType(CurName,TypeParamCount,ErrorEl)
     else
       NextEl:=FindElementWithoutParams(CurName,ErrorEl,true);
     {$IFDEF VerbosePasResolver}
@@ -17503,6 +17483,8 @@ begin
     if RightPath='' then
       exit(NextEl);
   until false;
+
+  if AParent=nil then ;;
 end;
 
 function TPasResolver.FindElementWithoutParams(const AName: String;
@@ -18599,6 +18581,17 @@ begin
   EmitElementHints(RefEl,DeclEl);
 end;
 
+procedure TPasResolver.WriteScopesShort(Title: string);
+var
+  i: Integer;
+begin
+  {AllowWriteln}
+  writeln(Title,' ScopeCount=',ScopeCount,' FStashScopeCount=',FStashScopeCount);
+  for i:=0 to FScopeCount-1 do
+    writeln('  ',i,'/',FScopeCount,' ',GetObjName(FScopes[i]));
+  {AllowWriteln-}
+end;
+
 function TPasResolver.GetLocalScope: TPasScope;
 begin
   Result:=TopScope;
@@ -19252,6 +19245,13 @@ begin
   RaiseMsg(id,nXExpectedButYFound,sXExpectedButYFound,[X,Y],El);
 end;
 
+procedure TPasResolver.RaiseXExpectedButTypeYFound(id: TMaxPrecInt;
+  const X: string; Y: TPasType; El: TPasElement);
+begin
+  RaiseMsg(id,nXExpectedButYFound,sXExpectedButYFound,
+    [x,GetTypeDescription(Y)],El);
+end;
+
 procedure TPasResolver.RaiseContextXExpectedButYFound(id: TMaxPrecInt; const C, X,
   Y: string; El: TPasElement);
 begin
@@ -21512,6 +21512,9 @@ function TPasResolver.GetTypeDescription(aType: TPasType; AddPath: boolean): str
   function GetName: string;
   var
     s: String;
+    Spec: TPasSpecializeType;
+    P: TPasElement;
+    i: Integer;
   begin
     Result:=aType.Name;
     if Result='' then
@@ -21527,6 +21530,22 @@ function TPasResolver.GetTypeDescription(aType: TPasType; AddPath: boolean): str
         else
           Result:='dynamic array';
         end
+      else if aType is TPasSpecializeType then
+        begin
+        Spec:=TPasSpecializeType(aType);
+        if Spec.CustomData is TPasSpecializeTypeData then
+          exit(GetTypeDescription(TPasSpecializeTypeData(Spec.CustomData).SpecializedType));
+        Result:=GetTypeDescription(Spec.DestType,true)+'<';
+        for i:=0 to Spec.Params.Count-1 do
+          begin
+          P:=TPasElement(Spec.Params[i]);
+          if P is TPasType then
+            Result:=Result+GetTypeDescription(TPasType(P));
+          if i>0 then
+            Result:=Result+',';
+          end;
+        Result:=Result+'>';
+        end
       else
         Result:=GetElementTypeName(aType);
       end;
@@ -23645,10 +23664,15 @@ begin
   else if ElClass=TPasSpecializeType then
     begin
     if El.CustomData is TPasSpecializeTypeData then
-      TypeEl:=TPasSpecializeTypeData(El.CustomData).SpecializedType
+      begin
+      TypeEl:=TPasSpecializeTypeData(El.CustomData).SpecializedType;
+      SetResolverIdentifier(ResolvedEl,btContext,TypeEl,TypeEl,TypeEl,[]);
+      end
     else
+      begin
       TypeEl:=TPasSpecializeType(El).DestType;
-    SetResolverIdentifier(ResolvedEl,btContext,El,TypeEl,TPasType(El),[]);
+      SetResolverIdentifier(ResolvedEl,btContext,El,TypeEl,TPasType(El),[]);
+      end;
     end
   else
     RaiseNotYetImplemented(20160922163705,El);

+ 17 - 15
packages/fcl-passrc/src/pparser.pp

@@ -215,7 +215,7 @@ type
     function CreateFunctionType(const AName, AResultName: String; AParent: TPasElement;
       UseParentAsResultParent: Boolean; const ASrcPos: TPasSourcePos; TypeParams: TFPList = nil): TPasFunctionType;
     function FindElement(const AName: String): TPasElement; virtual; abstract;
-    function FindElementFor(const AName: String; AParent: TPasElement): TPasElement; virtual;
+    function FindElementFor(const AName: String; AParent: TPasElement; TypeParamCount: integer): TPasElement; virtual;
     procedure BeginScope(ScopeType: TPasScopeType; El: TPasElement); virtual;
     procedure FinishScope(ScopeType: TPasScopeType; El: TPasElement); virtual;
     procedure FinishTypeAlias(var aType: TPasType); virtual;
@@ -407,7 +407,7 @@ type
     function ExprToText(Expr: TPasExpr): String;
     function ArrayExprToText(Expr: TPasExprArray): String;
     // Type declarations
-    function ResolveTypeReference(Name: string; Parent: TPasElement): TPasType;
+    function ResolveTypeReference(Name: string; Parent: TPasElement; ParamCnt: integer = 0): TPasType;
     function ParseComplexType(Parent : TPasElement = Nil): TPasType;
     function ParseTypeDecl(Parent: TPasElement): TPasType;
     function ParseGenericTypeDecl(Parent: TPasElement; AddToParent: boolean): TPasGenericType;
@@ -896,11 +896,12 @@ begin
     visDefault, ASrcPos, TypeParams));
 end;
 
-function TPasTreeContainer.FindElementFor(const AName: String; AParent: TPasElement
-  ): TPasElement;
+function TPasTreeContainer.FindElementFor(const AName: String;
+  AParent: TPasElement; TypeParamCount: integer): TPasElement;
 begin
   Result:=FindElement(AName);
   if AParent=nil then ;
+  if TypeParamCount=0 then ;
 end;
 
 procedure TPasTreeContainer.BeginScope(ScopeType: TPasScopeType; El: TPasElement
@@ -1561,8 +1562,6 @@ begin
     else if (CurToken = tkLessThan) then // A = B<t>;
       begin
       Result:=ParseSpecializeType(Parent,TypeName,Name,Expr);
-      if TypeName='' then
-        Engine.FinishScope(stTypeDef,Result); // finish anonymous type
       ok:=true;
       exit;
       end
@@ -1708,14 +1707,14 @@ begin
     // read nested specialize arguments
     ReadSpecializeArguments(ST);
     // Important: resolve type reference AFTER args, because arg count is needed
-    ST.DestType:=ResolveTypeReference(GenName,ST);
+    ST.DestType:=ResolveTypeReference(GenName,ST,ST.Params.Count);
 
     if CurToken<>tkGreaterThan then
       ParseExcTokenError('[20190801113005]');
     // ToDo: cascaded specialize A<B>.C<D>
 
     if TypeName='' then
-      Engine.FinishScope(stTypeDef,Result);
+      Engine.FinishScope(stTypeDef,ST);
     Result:=ST;
   finally
     if Result=nil then
@@ -1978,7 +1977,6 @@ end;
 function TPasParser.ParseArrayType(Parent: TPasElement;
   const NamePos: TPasSourcePos; const TypeName: String; PackMode: TPackMode
   ): TPasArrayType;
-
 Var
   ok: Boolean;
 begin
@@ -2075,7 +2073,8 @@ begin
     end;
 end;
 
-function TPasParser.ResolveTypeReference(Name: string; Parent: TPasElement): TPasType;
+function TPasParser.ResolveTypeReference(Name: string; Parent: TPasElement;
+  ParamCnt: integer): TPasType;
 var
   SS: Boolean;
   Ref: TPasElement;
@@ -2084,7 +2083,7 @@ begin
   SS:=(not (po_ResolveStandardTypes in FOptions)) and isSimpleTypeToken(Name);
   if not SS then
     begin
-    Ref:=Engine.FindElementFor(Name,Parent);
+    Ref:=Engine.FindElementFor(Name,Parent,ParamCnt);
     if Ref=nil then
       begin
       {$IFDEF VerbosePasResolver}
@@ -4028,6 +4027,7 @@ Var
   N : String;
   T : TPasGenericTemplateType;
   Expr: TPasExpr;
+  TypeEl: TPasType;
 begin
   ExpectToken(tkLessThan);
   repeat
@@ -4048,10 +4048,12 @@ begin
           end
         else if CurToken=tkIdentifier then
           begin
-          if T.TypeConstraint='' then
-            T.TypeConstraint:=ReadDottedIdentifier(T,Expr,true)
-          else
-            ReadDottedIdentifier(T,Expr,false);
+          TypeEl:=ParseTypeReference(Parent,true,Expr);
+          if TypeEl<>nil then
+            begin
+            T.TypeConstraint:=TypeEl.Name;
+            TypeEl.Release{$IFDEF CheckPasTreeRefCount}('CreateElement'){$ENDIF};
+            end;
           end
         else
           CheckToken(tkIdentifier);

+ 94 - 5
packages/fcl-passrc/tests/tcresolvegenerics.pas

@@ -19,19 +19,23 @@ type
     // generic types
     procedure TestGen_MissingTemplateFail;
     procedure TestGen_VarTypeWithoutSpecializeFail;
+    procedure TestGen_GenTypeWithWrongParamCountFail;
     procedure TestGen_ConstraintStringFail;
     procedure TestGen_ConstraintMultiClassFail;
     procedure TestGen_ConstraintRecordExpectedFail;
-    // ToDo: constraints mismatch: TAnt<T:record>; TBird<T:Class> = record v: TAnt<T> end   Fail
-    // ToDo: constraint keyword record
-    // ToDo: constraint keyword class, constructor, class+constructor
+    procedure TestGen_ConstraintClassRecordFail;
+    procedure TestGen_ConstraintRecordClassFail;
+    procedure TestGen_ConstraintArrayFail;
+    // ToDo: constraint constructor
     // ToDo: constraint T:Unit2.TBird
     // ToDo: constraint T:Unit2.TGen<word>
     procedure TestGen_TemplNameEqTypeNameFail;
     procedure TestGen_GenericNotFoundFail;
+    procedure TestGen_SameNameSameParamCountFail;
     procedure TestGen_RecordLocalNameDuplicateFail;
     procedure TestGen_Record;
     procedure TestGen_RecordDelphi;
+    procedure TestGen_RecordNestedSpecialized;
     // ToDo: enums within generic
     // ToDo: procedure TestGen_SpecializeArg_ArrayOf;  type TBird = specialize<array of word>
     // ToDo: unitname.specialize TBird<word>.specialize
@@ -98,6 +102,18 @@ begin
     nGenericsWithoutSpecializationAsType);
 end;
 
+procedure TTestResolveGenerics.TestGen_GenTypeWithWrongParamCountFail;
+begin
+  StartProgram(false);
+  Add([
+  'type generic TBird<T> = record end;',
+  'var b: TBird<word, byte>;',
+  'begin',
+  '']);
+  CheckResolverException('identifier not found "TBird<,>"',
+    nIdentifierNotFound);
+end;
+
 procedure TTestResolveGenerics.TestGen_ConstraintStringFail;
 begin
   StartProgram(false);
@@ -108,7 +124,7 @@ begin
   'end;',
   'begin',
   '']);
-  CheckResolverException('''string'' is not a valid constraint',
+  CheckResolverException('"string" is not a valid constraint',
     nXIsNotAValidConstraint);
 end;
 
@@ -127,7 +143,7 @@ begin
   'end;',
   'begin',
   '']);
-  CheckResolverException('''TBird'' constraint and ''TBear'' constraint cannot be specified together',
+  CheckResolverException('"TBird" constraint and "TBear" constraint cannot be specified together',
     nConstraintXAndConstraintYCannotBeTogether);
 end;
 
@@ -145,6 +161,50 @@ begin
     nXExpectedButYFound);
 end;
 
+procedure TTestResolveGenerics.TestGen_ConstraintClassRecordFail;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  TRec = record end;',
+  '  generic TBird<T:class> = record v: T; end;',
+  'var r: specialize TBird<TRec>;',
+  'begin',
+  '']);
+  CheckResolverException('class type expected, but TRec found',
+    nXExpectedButYFound);
+end;
+
+procedure TTestResolveGenerics.TestGen_ConstraintRecordClassFail;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  TObject = class end;',
+  '  generic TBird<T:record> = record v: T; end;',
+  'var r: specialize TBird<TObject>;',
+  'begin',
+  '']);
+  CheckResolverException('record type expected, but TObject found',
+    nXExpectedButYFound);
+end;
+
+procedure TTestResolveGenerics.TestGen_ConstraintArrayFail;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  TArr = array of word;',
+  '  generic TBird<T:TArr> = record v: T; end;',
+  'begin',
+  '']);
+  CheckResolverException('"TArr" is not a valid constraint',
+    nXIsNotAValidConstraint);
+end;
+
 procedure TTestResolveGenerics.TestGen_TemplNameEqTypeNameFail;
 begin
   StartProgram(false);
@@ -172,6 +232,20 @@ begin
     nIdentifierNotFound);
 end;
 
+procedure TTestResolveGenerics.TestGen_SameNameSameParamCountFail;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode delphi}',
+  'type',
+  '  TBird<S,T> = record w: T; end;',
+  '  TBird<X,Y> = record f: X; end;',
+  'begin',
+  '']);
+  CheckResolverException('Duplicate identifier "TBird" at afile.pp(4,8)',
+    nDuplicateIdentifier);
+end;
+
 procedure TTestResolveGenerics.TestGen_RecordLocalNameDuplicateFail;
 begin
   StartProgram(false);
@@ -223,6 +297,21 @@ begin
   ParseProgram;
 end;
 
+procedure TTestResolveGenerics.TestGen_RecordNestedSpecialized;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  TObject = class end;',
+  '  generic TBird<T> = class v: T; end;',
+  '  generic TFish<T:class> = record v: T; end;',
+  'var f: specialize TFish<specialize TBird<word>>;',
+  'begin',
+  '']);
+  ParseProgram;
+end;
+
 procedure TTestResolveGenerics.TestGen_Class;
 begin
   StartProgram(false);