Browse Source

fcl-passrc: specialize generic constraints

git-svn-id: trunk@42948 -
Mattias Gaertner 6 years ago
parent
commit
f2a8e646b7

+ 2 - 10
packages/fcl-json/src/jsonscanner.pp

@@ -257,14 +257,6 @@ begin
       FCurToken := Result;
       FCurToken := Result;
       exit;
       exit;
       end;
       end;
-  // Empty line
-  if (FTokenStr=FEOL) then
-    begin
-    Result := tkWhiteSpace;
-    FCurToken := Result;
-    exit;
-    end;
-
 
 
   FCurTokenString := '';
   FCurTokenString := '';
   case FTokenStr^ of
   case FTokenStr^ of
@@ -278,13 +270,13 @@ begin
       Result := tkWhitespace;
       Result := tkWhitespace;
       repeat
       repeat
         Inc(FTokenStr);
         Inc(FTokenStr);
-        if (FTokenStr[0] = #0) or (FTokenStr=FEOL) then
+        if FTokenStr[0] = #0 then
           if not FetchLine then
           if not FetchLine then
           begin
           begin
             FCurToken := Result;
             FCurToken := Result;
             exit;
             exit;
           end;
           end;
-      until not (FTokenStr[0] in [#9, ' ',#10, #13]);
+      until not (FTokenStr[0] in [#9, ' ']);
       end;
       end;
     '"','''':
     '"','''':
       begin
       begin

+ 13 - 51
packages/fcl-json/tests/testjsonparser.pp

@@ -70,7 +70,6 @@ type
     Procedure TestHandlerResult;
     Procedure TestHandlerResult;
     Procedure TestHandlerResultStream;
     Procedure TestHandlerResultStream;
     Procedure TestEmptyLine;
     Procedure TestEmptyLine;
-    procedure TestBug36037Part2;
   end;
   end;
 
 
 implementation
 implementation
@@ -541,23 +540,20 @@ begin
 end;
 end;
 
 
 procedure TTestParser.TestEmptyLine;
 procedure TTestParser.TestEmptyLine;
-
 // Bug report 36037
 // Bug report 36037
-
-Const
-  MyJSON =
-    '  {'+sLineBreak+
-    '  "pylib__linux" : "libpython3.7m.so.1.0",'+sLineBreak+
-    '  "ui_toolbar_theme": "default_24x24",'+sLineBreak+
-    '  "ui_toolbar_show" : true,'+sLineBreak+
-    '  "font_name__linux" : "DejaVu Sans Mono",'+sLineBreak+
-    '  "font_size__linux" : 10,'+sLineBreak+
-    '    "ui_listbox_fuzzy": false,'+sLineBreak+
-    '    "ui_max_size_lexer": 5,'+sLineBreak+
-    '    "find_separate_form": false,'+sLineBreak+sLineBreak+
-    '}';
-var
-  J : TJSONData;
+Const MyJSON =
+'  {'+sLineBreak+
+'  "pylib__linux" : "libpython3.7m.so.1.0",'+sLineBreak+
+'  "ui_toolbar_theme": "default_24x24",'+sLineBreak+
+'  "ui_toolbar_show" : true,'+sLineBreak+
+'  "font_name__linux" : "DejaVu Sans Mono",'+sLineBreak+
+'  "font_size__linux" : 10,'+sLineBreak+
+'    "ui_listbox_fuzzy": false,'+sLineBreak+
+'    "ui_max_size_lexer": 5,'+sLineBreak+
+'    "find_separate_form": false,'+sLineBreak+sLineBreak+
+'}';
+  var
+    J : TJSONData;
 begin
 begin
   With TJSONParser.Create(MyJSON,[joUTF8,joIgnoreTrailingComma]) do
   With TJSONParser.Create(MyJSON,[joUTF8,joIgnoreTrailingComma]) do
   Try
   Try
@@ -568,40 +564,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TTestParser.TestBug36037Part2;
-
-Const
-  MyJSON =
-
-'{'+sLineBreak+
-'  "tab_spaces": true,'+sLineBreak+
-'  //auto-indent kind:'+sLineBreak+
-'  //  0: indent like in prev line'+sLineBreak+
-'  //  1: by spaces'+sLineBreak+
-'  //  2: by tabs+spaces'+sLineBreak+
-'  //  3: by tabs only'+sLineBreak+
-'  "indent_kind": 1,'+sLineBreak+
-'  "indent_size": 4,'+sLineBreak+
-''+sLineBreak+
-'  "saving_trim_spaces": true,'+sLineBreak+
-''+sLineBreak+
-'//  "config_menus_from": "kv-menu JSON.json",'+sLineBreak+
-'    "find_hotkey_replace": "Alt+Enter",'+sLineBreak+
-'    "fold_style": 4,'+sLineBreak+
-'}'+sLineBreak;
-
-var
-  J : TJSONData;
-begin
-  With TJSONParser.Create(MyJSON,[joUTF8,joIgnoreTrailingComma,joComments]) do
-  Try
-    J:=Parse;
-    J.Free;
-  Finally
-    Free;
-  end;
-end;
-
 procedure TTestParser.DoTestError(S : String; Options : TJSONOptions = DefaultOpts);
 procedure TTestParser.DoTestError(S : String; Options : TJSONOptions = DefaultOpts);
 
 
 Var
 Var

+ 294 - 205
packages/fcl-passrc/src/pasresolver.pp

@@ -690,6 +690,7 @@ type
     Params: TPasTypeArray;
     Params: TPasTypeArray;
     ImplProcs: TFPList;
     ImplProcs: TFPList;
     HeaderScope: TObject;
     HeaderScope: TObject;
+    SpecializedConstraints: TPasExprArray;
     destructor Destroy; override;
     destructor Destroy; override;
     property SpecializedType: TPasGenericType read FSpecializedType write SetSpecializedType;
     property SpecializedType: TPasGenericType read FSpecializedType write SetSpecializedType;
   end;
   end;
@@ -1727,9 +1728,16 @@ type
     procedure AddGenericTemplateIdentifiers(GenericTemplateTypes: TFPList;
     procedure AddGenericTemplateIdentifiers(GenericTemplateTypes: TFPList;
       Scope: TPasIdentifierScope);
       Scope: TPasIdentifierScope);
     procedure AddSpecializedTemplateIdentifiers(GenericTemplateTypes: TFPList;
     procedure AddSpecializedTemplateIdentifiers(GenericTemplateTypes: TFPList;
-      ParamTypes: TPasTypeArray; Scope: TPasIdentifierScope);
+      SpecializedItem: TPSSpecializedItem; Scope: TPasIdentifierScope;
+      CheckConstraints: boolean);
     function GetSpecializedType(El: TPasSpecializeType): TPasGenericType;
     function GetSpecializedType(El: TPasSpecializeType): TPasGenericType;
-    function CheckSpecializeConstraints(El : TPasSpecializeType): boolean; virtual; // false = not fully specialized
+    procedure CheckSpecializedParamFitsConstraintExpr(ParamType: TPasType;
+      SpecializedItem: TPSSpecializedItem; ConExpr: TPasExpr; ErrorPos: TPasElement);
+    procedure CheckSpecializedParamFitsTemplate(ParamType: TPasType;
+      GenTempl: TPasGenericTemplateType; SpecializedItem: TPSSpecializedItem;
+      ErrorPos: TPasElement);
+    procedure CheckSpecializedTemplateFitsTemplate(ParamTemplType,
+      GenTempl: TPasGenericTemplateType; ErrorPos: TPasElement);
     function CreateSpecializedType(El: TPasSpecializeType;
     function CreateSpecializedType(El: TPasSpecializeType;
       const ParamsResolved: TPasTypeArray): TPSSpecializedItem; virtual;
       const ParamsResolved: TPasTypeArray): TPSSpecializedItem; virtual;
     function InitSpecializeScopes(El: TPasElement): integer; virtual;
     function InitSpecializeScopes(El: TPasElement): integer; virtual;
@@ -2979,9 +2987,12 @@ begin
     ImplProcs.Free;
     ImplProcs.Free;
     ImplProcs:=nil;
     ImplProcs:=nil;
     end;
     end;
-  SpecializedType:=nil;
+  for i:=0 to length(SpecializedConstraints)-1 do
+    TPasElement(SpecializedConstraints[i]).Release{$IFDEF CheckPasTreeRefCount}('CreateElement'){$ENDIF};
+  SetLength(SpecializedConstraints,0);
   HeaderScope.Free;
   HeaderScope.Free;
   HeaderScope:=nil;
   HeaderScope:=nil;
+  SpecializedType:=nil;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -14778,15 +14789,31 @@ begin
 end;
 end;
 
 
 procedure TPasResolver.AddSpecializedTemplateIdentifiers(
 procedure TPasResolver.AddSpecializedTemplateIdentifiers(
-  GenericTemplateTypes: TFPList; ParamTypes: TPasTypeArray;
-  Scope: TPasIdentifierScope);
+  GenericTemplateTypes: TFPList; SpecializedItem: TPSSpecializedItem;
+  Scope: TPasIdentifierScope; CheckConstraints: boolean);
 var
 var
   i: Integer;
   i: Integer;
   TemplType: TPasGenericTemplateType;
   TemplType: TPasGenericTemplateType;
+  ParamTypes: TPasTypeArray;
+  ParamType: TPasType;
+  ErrorPos: TPasElement;
 begin
 begin
+  ParamTypes:=SpecializedItem.Params;
+  ErrorPos:=SpecializedItem.FirstSpecialize;
   for i:=0 to length(ParamTypes)-1 do
   for i:=0 to length(ParamTypes)-1 do
     begin
     begin
     TemplType:=TPasGenericTemplateType(GenericTemplateTypes[i]);
     TemplType:=TPasGenericTemplateType(GenericTemplateTypes[i]);
+    ParamType:=ParamTypes[i];
+
+    if CheckConstraints then
+      begin
+      if ParamType is TPasGenericTemplateType then
+        CheckSpecializedTemplateFitsTemplate(TPasGenericTemplateType(ParamType),
+                                             TemplType,ErrorPos)
+      else
+        CheckSpecializedParamFitsTemplate(ParamType,TemplType,SpecializedItem,ErrorPos);
+      end;
+
     AddIdentifier(Scope,TemplType.Name,ParamTypes[i],pikSimple);
     AddIdentifier(Scope,TemplType.Name,ParamTypes[i],pikSimple);
     end;
     end;
 end;
 end;
@@ -14797,7 +14824,7 @@ var
   Data: TPasSpecializeTypeData;
   Data: TPasSpecializeTypeData;
   GenericType: TPasGenericType;
   GenericType: TPasGenericType;
   GenScope: TPasGenericScope;
   GenScope: TPasGenericScope;
-  Params: TFPList;
+  Params, GenericTemplateList: TFPList;
   i, j: Integer;
   i, j: Integer;
   Param: TPasElement;
   Param: TPasElement;
   ParamsResolved: TPasTypeArray;
   ParamsResolved: TPasTypeArray;
@@ -14807,6 +14834,7 @@ var
   SrcModule: TPasModule;
   SrcModule: TPasModule;
   SrcModuleScope: TPasModuleScope;
   SrcModuleScope: TPasModuleScope;
   SrcResolver: TPasResolver;
   SrcResolver: TPasResolver;
+  IsSelf: Boolean;
 begin
 begin
   Result:=nil;
   Result:=nil;
   if El.CustomData<>nil then
   if El.CustomData<>nil then
@@ -14824,21 +14852,28 @@ begin
     RaiseMsg(20190807205038,nTypeXIsNotYetCompletelyDefined,sTypeXIsNotYetCompletelyDefined,
     RaiseMsg(20190807205038,nTypeXIsNotYetCompletelyDefined,sTypeXIsNotYetCompletelyDefined,
       [GetTypeDescription(GenericType)],El);
       [GetTypeDescription(GenericType)],El);
 
 
-  if not CheckSpecializeConstraints(El) then
-    begin
-    // El is actually the GenericType
-    // e.g. "type A<T> = class v: A<T> end;"
-    exit(GenericType);
-    end;
-
+  GenericTemplateList:=GenericType.GenericTemplateTypes;
   Params:=El.Params;
   Params:=El.Params;
+  if GenericTemplateList=nil then
+    RaiseMsg(20190905111703,nXExpectedButYFound,sXExpectedButYFound,['generic templates',GenericType.Name],El);
+  if GenericTemplateList.Count<>Params.Count then
+    RaiseMsg(20190905111704,nXExpectedButYFound,sXExpectedButYFound,
+      ['type with '+IntToStr(Params.Count)+' generic templates',
+       GenericType.Name+GetTypeParamCommas(GenericTemplateList.Count)],El);
+
   SetLength(ParamsResolved,Params.Count);
   SetLength(ParamsResolved,Params.Count);
+  IsSelf:=true;
   for i:=0 to Params.Count-1 do
   for i:=0 to Params.Count-1 do
     begin
     begin
     Param:=TPasElement(Params[i]);
     Param:=TPasElement(Params[i]);
     ComputeElement(Param,ResolvedEl,[rcType]);
     ComputeElement(Param,ResolvedEl,[rcType]);
     ParamsResolved[i]:=ResolvedEl.LoTypeEl;
     ParamsResolved[i]:=ResolvedEl.LoTypeEl;
+    if ResolvedEl.LoTypeEl<>TPasType(GenericTemplateList[i]) then
+      IsSelf:=false;
     end;
     end;
+  if IsSelf then
+    exit(GenericType);
+
   SpecializedTypes:=GenScope.SpecializedTypes;
   SpecializedTypes:=GenScope.SpecializedTypes;
   if SpecializedTypes=nil then
   if SpecializedTypes=nil then
     begin
     begin
@@ -14881,216 +14916,266 @@ begin
   Data.SpecializedType:=Result;
   Data.SpecializedType:=Result;
 end;
 end;
 
 
-function TPasResolver.CheckSpecializeConstraints(El: TPasSpecializeType
-  ): boolean;
+procedure TPasResolver.CheckSpecializedParamFitsConstraintExpr(
+  ParamType: TPasType; SpecializedItem: TPSSpecializedItem; ConExpr: TPasExpr;
+  ErrorPos: TPasElement);
+var
+  GenType: TPasGenericType;
 
 
-  procedure CheckTemplateFitsTemplate(ParamTemplType,
-    GenTempl: TPasGenericTemplateType; ErrorPos: TPasElement);
-  var
-    ParamConstraints: TPasExprArray;
-    j, k: Integer;
-    ConExpr, ParamConstraintExpr: TPasExpr;
-    ConToken: TToken;
-    ResolvedConstraint, ResolvedParamCon: TPasResolverResult;
-    ConstraintClass, ParamClassType: TPasClassType;
+  procedure RaiseNotValidConstraint(Id: TMaxPrecInt; ConExpr: TPasExpr);
   begin
   begin
-    // specialize via template type (not fully specialized)
-    ParamConstraints:=ParamTemplType.Constraints;
-    for j:=0 to length(GenTempl.Constraints)-1 do
-      begin
-      ConExpr:=GenTempl.Constraints[j];
-      ConToken:=GetGenericConstraintKeyword(ConExpr);
-      if ConToken<>tkEOF then
-        begin
-        // constraint is keyword
-        // -> check if keyword is in ParamConstraints
-        k:=length(ParamConstraints)-1;
-        while (k>=0) and (GetGenericConstraintKeyword(ParamConstraints[k])<>ConToken) do
-          dec(k);
-        if k<0 then
-          RaiseMsg(20190816230021,nTypeParamXIsMissingConstraintY,
-            sTypeParamXIsMissingConstraintY,[ParamTemplType.Name,TokenInfos[ConToken]],ErrorPos);
-        end
-      else
-        begin
-        // constraint is identifier
-        ComputeElement(ConExpr,ResolvedConstraint,[rcType]);
-        if ResolvedConstraint.IdentEl=nil then
-          RaiseMsg(20190816231846,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],ConExpr);
-        if not (ResolvedConstraint.LoTypeEl is TPasClassType) then
-          RaiseMsg(20190816231849,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],ConExpr);
-        ConstraintClass:=TPasClassType(ResolvedConstraint.LoTypeEl);
-        // constraint is class/interface type
-        // -> check if one of ParamConstraints fits the constraint type
-        // i.e. ParamConstraints must be more strict than target constraints
-        k:=length(ParamConstraints)-1;
-        while k>=0 do
-          begin
-          ParamConstraintExpr:=ParamConstraints[k];
-          ConToken:=GetGenericConstraintKeyword(ParamConstraintExpr);
-          if ConToken=tkEOF then
-            begin
-            ComputeElement(ParamConstraintExpr,ResolvedParamCon,[rcType]);
-            if not (ResolvedParamCon.IdentEl is TPasClassType) then
-              RaiseMsg(20190816232459,nXExpectedButYFound,sXExpectedButYFound,['type',GetResolverResultDescription(ResolvedParamCon)],ParamConstraintExpr);
-            ParamClassType:=TPasClassType(ResolvedParamCon.IdentEl);
-            if (ConstraintClass.ObjKind=okInterface)
-                and (ParamClassType.ObjKind=okClass) then
-              begin
-              if GetClassImplementsIntf(ParamClassType,ConstraintClass)<>nil then
-                break;
-              end
-            else
-              begin
-              if CheckClassIsClass(ParamClassType,ConstraintClass)<cIncompatible then
-                break;
-              end;
-            end;
-          dec(k);
-          end;
-        if k<0 then
-          begin
-          if ConstraintClass.ObjKind=okInterface then
-            RaiseMsg(20190816233102,nTypeParamXMustSupportIntfY,
-              sTypeParamXMustSupportIntfY,[ParamTemplType.Name,GetTypeDescription(ConstraintClass)],ErrorPos)
-          else
-            RaiseMsg(20190816230021,nTypeParamXIsNotCompatibleWithY,
-              sTypeParamXIsNotCompatibleWithY,[ParamTemplType.Name,GetTypeDescription(ConstraintClass)],ErrorPos);
-          end;
-        end;
-      end;
+    RaiseMsg(Id,nXIsNotAValidConstraint,sXIsNotAValidConstraint,
+      [GetElementSourcePosStr(ConExpr)],ErrorPos);
   end;
   end;
 
 
-  procedure CheckTypeFitsConstraintExpr(ParamType: TPasType;
-    ConExpr: TPasExpr; ErrorPos: TPasElement);
+  function ElementReferencesTemplateTypes(El: TPasElement): boolean;
   var
   var
-    ConToken: TToken;
-    aClass, ConstraintClass: TPasClassType;
-    ResolvedConstraint: TPasResolverResult;
-    GenTempl: TPasGenericTemplateType;
-    j: Integer;
+    C: TClass;
+    Prim: TPrimitiveExpr;
+    Decl: TPasElement;
+    Bin: TBinaryExpr;
+    Spec: TPasSpecializeType;
+    Arr: TPasArrayType;
+    i: Integer;
   begin
   begin
-    ConToken:=GetGenericConstraintKeyword(ConExpr);
-    case ConToken of
-    tkrecord:
-      begin
-      if not (ParamType is TPasRecordType) then
-        RaiseXExpectedButTypeYFound(20190725200015,'record type',ParamType,ErrorPos);
-      end;
-    tkclass,tkconstructor:
+    Result:=false;
+    if El=nil then exit;
+    C:=El.ClassType;
+    if C=TPrimitiveExpr then
       begin
       begin
-      if not (ParamType is TPasClassType) then
-        RaiseXExpectedButTypeYFound(20190726133231,'class type',ParamType,ErrorPos);
-      aClass:=TPasClassType(ParamType);
-      if aClass.ObjKind<>okClass then
-        RaiseXExpectedButTypeYFound(20190726133232,'class type',ParamType,ErrorPos);
-      if aClass.IsExternal then
-        RaiseXExpectedButTypeYFound(20190726133233,'non external class type',ParamType,ErrorPos);
-      if ConToken=tkconstructor then
+      Prim:=TPrimitiveExpr(El);
+      if Prim.Kind=pekIdent then
         begin
         begin
-        if FindDefaultConstructor(aClass)=nil then
-          RaiseXExpectedButTypeYFound(20190831000225,'class type with constructor create()',ParamType,ErrorPos);
-        end;
-      end;
-    else
+        if Prim.CustomData is TResolvedReference then
+          begin
+          Decl:=TResolvedReference(Prim.CustomData).Declaration;
+          exit(ElementReferencesTemplateTypes(Decl));
+          end;
+        end
+      else
+        exit;
+      end
+    else if C=TBinaryExpr then
       begin
       begin
-      // constraint can be a class type, interface type or a gen param type
-      // Param must be a class
-      ComputeElement(ConExpr,ResolvedConstraint,[rcType]);
-      if ResolvedConstraint.BaseType<>btContext then
-        RaiseMsg(20190831214107,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],ConExpr);
-      if ResolvedConstraint.IdentEl=nil then
-        RaiseMsg(20190726134037,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],ConExpr);
-      if ResolvedConstraint.LoTypeEl is TPasGenericTemplateType then
-        begin
-        GenTempl:=TPasGenericTemplateType(ResolvedConstraint.LoTypeEl);
-        if GenTempl=ConExpr.Parent then
-          RaiseNotYetImplemented(20190831213359,GenTempl);
-        for j:=0 to length(GenTempl.Constraints)-1 do
-          CheckTypeFitsConstraintExpr(ParamType,GenTempl.Constraints[j],ErrorPos);
+      Bin:=TBinaryExpr(El);
+      Result:=ElementReferencesTemplateTypes(Bin.left)
+        or ElementReferencesTemplateTypes(Bin.right);
+      end
+    else if C=TInlineSpecializeExpr then
+      Result:=ElementReferencesTemplateTypes(TInlineSpecializeExpr(El).DestType)
+    else if C=TPasGenericTemplateType then
+      Result:=GenType.GenericTemplateTypes.IndexOf(El)>=0
+    else if C.InheritsFrom(TPasType) then
+      begin
+      if TPasType(El).Name<>'' then exit;
+      if C=TPasSpecializeType then
+        begin
+        Spec:=TPasSpecializeType(El);
+        if ElementReferencesTemplateTypes(Spec.DestType) then exit(true);
+        for i:=0 to Spec.Params.Count-1 do
+          if ElementReferencesTemplateTypes(TPasElement(Spec.Params[i])) then
+            exit(true);
         end
         end
-      else if ResolvedConstraint.LoTypeEl is TPasClassType then
-        begin
-        ConstraintClass:=TPasClassType(ResolvedConstraint.LoTypeEl);
-        if not (ParamType is TPasClassType) then
-          RaiseIncompatibleType(20190726135859,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,ErrorPos);
-        case ConstraintClass.ObjKind of
-        okClass:
-          // Param must be a ConstraintClass
-          if CheckClassIsClass(ParamType,ConstraintClass)=cIncompatible then
-            RaiseIncompatibleType(20190726135309,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,ErrorPos);
-        okInterface:
-          // ParamType must implement ConstraintClass
-          if GetClassImplementsIntf(TPasClassType(ParamType),ConstraintClass)=nil then
-            RaiseIncompatibleType(20190726135458,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,ErrorPos);
-        else
-          RaiseIncompatibleType(20190726135310,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,ErrorPos);
-        end;
+      else if C=TPasArrayType then
+        begin
+        Arr:=TPasArrayType(El);
+        for i:=0 to length(Arr.Ranges)-1 do
+          if ElementReferencesTemplateTypes(Arr.Ranges[i]) then exit(true);
+        Result:=ElementReferencesTemplateTypes(Arr.ElType);
         end
         end
+      else if C=TPasPointerType then
+        Result:=ElementReferencesTemplateTypes(TPasPointerType(El).DestType)
+      else if C=TPasSetType then
+        Result:=ElementReferencesTemplateTypes(TPasSetType(El).EnumType)
+      else if C=TPasEnumType then
       else
       else
-        RaiseMsg(20190726134223,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],ConExpr);
-      end;
-    end;// case-end
+        RaiseNotYetImplemented(20190905110152,El);
+      end
+    else
+      RaiseNotYetImplemented(20190905105648,El);
   end;
   end;
 
 
-  procedure CheckTypeFitsTemplate(ParamType: TPasType;
-    GenTempl: TPasGenericTemplateType; ErrorPos: TPasElement);
-  var
-    j: Integer;
-  begin
-    // check if the specialized ParamType fits the constraints
-    for j:=0 to length(GenTempl.Constraints)-1 do
-      CheckTypeFitsConstraintExpr(ParamType,GenTempl.Constraints[j],ErrorPos);
+var
+  ConToken: TToken;
+  aClass, ConstraintClass: TPasClassType;
+  ResolvedConstraint: TPasResolverResult;
+  GenTempl: TPasGenericTemplateType;
+  ConLoType: TPasType;
+  i: Integer;
+  NewClass: TPTreeElement;
+  SpecConExpr: TPasExpr;
+begin
+  ConToken:=GetGenericConstraintKeyword(ConExpr);
+  case ConToken of
+  tkrecord:
+    begin
+    if ParamType is TPasRecordType then exit;
+    RaiseXExpectedButTypeYFound(20190725200015,'record type',ParamType,ErrorPos);
+    end;
+  tkclass,tkconstructor:
+    begin
+    if not (ParamType is TPasClassType) then
+      RaiseXExpectedButTypeYFound(20190726133231,'class type',ParamType,ErrorPos);
+    aClass:=TPasClassType(ParamType);
+    if aClass.ObjKind<>okClass then
+      RaiseXExpectedButTypeYFound(20190726133232,'class type',ParamType,ErrorPos);
+    if aClass.IsExternal then
+      RaiseXExpectedButTypeYFound(20190726133233,'non external class type',ParamType,ErrorPos);
+    if ConToken=tkconstructor then
+      begin
+      if FindDefaultConstructor(aClass)=nil then
+        RaiseXExpectedButTypeYFound(20190831000225,'class type with constructor create()',ParamType,ErrorPos);
+      end;
+    exit;
+    end;
   end;
   end;
 
 
+  // constraint can be a class type, interface type or a gen param type
+  // Param must be a class
+  if SpecializedItem<>nil then
+    begin
+    GenType:=SpecializedItem.GenericType;
+    if ElementReferencesTemplateTypes(ConExpr) then
+      begin
+      // constraint contains templates -> specialize constraint
+      i:=length(SpecializedItem.SpecializedConstraints);
+      Setlength(SpecializedItem.SpecializedConstraints,i+1);
+      NewClass:=TPTreeElement(ConExpr.ClassType);
+      SpecConExpr:=TPasExpr(NewClass.Create(ConExpr.Name,SpecializedItem.SpecializedType));
+      SpecializedItem.SpecializedConstraints[i]:=SpecConExpr;
+      SpecializeElement(ConExpr,SpecConExpr);
+      ConExpr:=SpecConExpr;
+      ResolveExpr(ConExpr,rraNone);
+      end;
+    end;
+
+  ComputeElement(ConExpr,ResolvedConstraint,[rcType]);
+  if ResolvedConstraint.BaseType<>btContext then
+    RaiseNotValidConstraint(20190831214107,ConExpr);
+  if ResolvedConstraint.IdentEl=nil then
+    RaiseNotValidConstraint(20190726134037,ConExpr);
+  ConLoType:=ResolvedConstraint.LoTypeEl;
+  if ConLoType is TPasGenericTemplateType then
+    begin
+    GenTempl:=TPasGenericTemplateType(ConLoType);
+    if GenTempl=ConExpr.Parent then
+      RaiseNotYetImplemented(20190831213359,GenTempl);
+    CheckSpecializedParamFitsTemplate(ParamType,GenTempl,nil,ErrorPos);
+    end
+  else if ConLoType is TPasClassType then
+    begin
+    ConstraintClass:=TPasClassType(ConLoType);
+    if not (ParamType is TPasClassType) then
+      RaiseIncompatibleType(20190726135859,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,ErrorPos);
+    if TPasClassType(ParamType).ObjKind<>okClass then
+      RaiseMsg(20190904175144,nXExpectedButYFound,sXExpectedButYFound,['class',GetTypeDescription(ParamType)],ErrorPos);
+    case ConstraintClass.ObjKind of
+    okClass:
+      // Param must be a ConstraintClass
+      if CheckClassIsClass(ParamType,ConstraintClass)=cIncompatible then
+        RaiseIncompatibleType(20190726135309,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,ErrorPos);
+    okInterface:
+      // ParamType must implement ConstraintClass
+      if GetClassImplementsIntf(TPasClassType(ParamType),ConstraintClass)=nil then
+        RaiseIncompatibleType(20190726135458,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,ErrorPos);
+    else
+      RaiseIncompatibleType(20190726135310,nIncompatibleTypesGotExpected,[''],ParamType,ConstraintClass,ErrorPos);
+    end;
+    end
+  else
+    begin
+    {$IFDEF VerbosePasResolver}
+    writeln('TPasResolver.CheckSpecializedParamFitsConstraintExpr ResolvedConstraint=',GetResolverResultDbg(ResolvedConstraint));
+    {$ENDIF}
+    RaiseMsg(20190726134223,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],ConExpr);
+    end;
+end;
+
+procedure TPasResolver.CheckSpecializedParamFitsTemplate(ParamType: TPasType;
+  GenTempl: TPasGenericTemplateType; SpecializedItem: TPSSpecializedItem;
+  ErrorPos: TPasElement);
 var
 var
-  Params, GenericTemplateList: TFPList;
   i: Integer;
   i: Integer;
-  P, ErrorPos: TPasElement;
-  ParamType, DestType: TPasType;
-  ResolvedEl: TPasResolverResult;
-  GenTempl: TPasGenericTemplateType;
 begin
 begin
-  Result:=false;
-  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 if the specialized ParamType fits the constraints
+  for i:=0 to length(GenTempl.Constraints)-1 do
+    CheckSpecializedParamFitsConstraintExpr(ParamType,SpecializedItem,
+                                            GenTempl.Constraints[i],ErrorPos);
+end;
 
 
-  // check constraints
-  for i:=0 to Params.Count-1 do
+procedure TPasResolver.CheckSpecializedTemplateFitsTemplate(ParamTemplType,
+  GenTempl: TPasGenericTemplateType; ErrorPos: TPasElement);
+var
+  ParamConstraints: TPasExprArray;
+  j, k: Integer;
+  ConExpr, ParamConstraintExpr: TPasExpr;
+  ConToken: TToken;
+  ResolvedConstraint, ResolvedParamCon: TPasResolverResult;
+  ConstraintClass, ParamClassType: TPasClassType;
+begin
+  // specialize via template type (not fully specialized)
+  ParamConstraints:=ParamTemplType.Constraints;
+  for j:=0 to length(GenTempl.Constraints)-1 do
     begin
     begin
-    GenTempl:=TPasGenericTemplateType(GenericTemplateList[i]);
-    P:=TPasElement(Params[i]);
-    if P.Parent=El then
-      ErrorPos:=P
-    else
-      ErrorPos:=El;
-    // check if P fits into GenTempl
-    ComputeElement(P,ResolvedEl,[rcType]);
-    if not (ResolvedEl.IdentEl is TPasType) then
-      RaiseMsg(20190725195434,nXExpectedButYFound,sXExpectedButYFound,['type',GetResolverResultDescription(ResolvedEl)],P);
-    ParamType:=ResolvedEl.LoTypeEl;
-    if ParamType=GenTempl then
-      // circle
-      // e.g. type A<S,T> = class
-      //          v: A<S,T>; // circle, do not specialize
-      //          u: A<S,word>; // specialize
-      //        end;
-    else if ParamType is TPasGenericTemplateType then
-      begin
-      CheckTemplateFitsTemplate(TPasGenericTemplateType(ParamType),GenTempl,ErrorPos);
-      Result:=true;
+    ConExpr:=GenTempl.Constraints[j];
+    ConToken:=GetGenericConstraintKeyword(ConExpr);
+    if ConToken<>tkEOF then
+      begin
+      // constraint is keyword
+      // -> check if keyword is in ParamConstraints
+      k:=length(ParamConstraints)-1;
+      while (k>=0) and (GetGenericConstraintKeyword(ParamConstraints[k])<>ConToken) do
+        dec(k);
+      if k<0 then
+        RaiseMsg(20190816230021,nTypeParamXIsMissingConstraintY,
+          sTypeParamXIsMissingConstraintY,[ParamTemplType.Name,TokenInfos[ConToken]],ErrorPos);
       end
       end
     else
     else
       begin
       begin
-      CheckTypeFitsTemplate(ParamType,GenTempl,ErrorPos);
-      Result:=true;
+      // constraint is identifier
+      ComputeElement(ConExpr,ResolvedConstraint,[rcType]);
+      if ResolvedConstraint.IdentEl=nil then
+        RaiseMsg(20190816231846,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],ConExpr);
+      if not (ResolvedConstraint.LoTypeEl is TPasClassType) then
+        RaiseMsg(20190816231849,nXIsNotAValidConstraint,sXIsNotAValidConstraint,[GetElementSourcePosStr(ConExpr)],ConExpr);
+      ConstraintClass:=TPasClassType(ResolvedConstraint.LoTypeEl);
+      // constraint is class/interface type
+      // -> check if one of ParamConstraints fits the constraint type
+      // i.e. ParamConstraints must be more strict than target constraints
+      k:=length(ParamConstraints)-1;
+      while k>=0 do
+        begin
+        ParamConstraintExpr:=ParamConstraints[k];
+        ConToken:=GetGenericConstraintKeyword(ParamConstraintExpr);
+        if ConToken=tkEOF then
+          begin
+          ComputeElement(ParamConstraintExpr,ResolvedParamCon,[rcType]);
+          if not (ResolvedParamCon.IdentEl is TPasClassType) then
+            RaiseMsg(20190816232459,nXExpectedButYFound,sXExpectedButYFound,['type',GetResolverResultDescription(ResolvedParamCon)],ParamConstraintExpr);
+          ParamClassType:=TPasClassType(ResolvedParamCon.IdentEl);
+          if (ConstraintClass.ObjKind=okInterface)
+              and (ParamClassType.ObjKind=okClass) then
+            begin
+            if GetClassImplementsIntf(ParamClassType,ConstraintClass)<>nil then
+              break;
+            end
+          else
+            begin
+            if CheckClassIsClass(ParamClassType,ConstraintClass)<cIncompatible then
+              break;
+            end;
+          end;
+        dec(k);
+        end;
+      if k<0 then
+        begin
+        if ConstraintClass.ObjKind=okInterface then
+          RaiseMsg(20190816233102,nTypeParamXMustSupportIntfY,
+            sTypeParamXMustSupportIntfY,[ParamTemplType.Name,GetTypeDescription(ConstraintClass)],ErrorPos)
+        else
+          RaiseMsg(20190816230021,nTypeParamXIsNotCompatibleWithY,
+            sTypeParamXIsNotCompatibleWithY,[ParamTemplType.Name,GetTypeDescription(ConstraintClass)],ErrorPos);
+        end;
       end;
       end;
     end;
     end;
 end;
 end;
@@ -15947,7 +16032,7 @@ begin
       // specialized procedure type
       // specialized procedure type
       GenScope.SpecializedItem:=SpecializedItem;
       GenScope.SpecializedItem:=SpecializedItem;
       AddSpecializedTemplateIdentifiers(GenEl.GenericTemplateTypes,
       AddSpecializedTemplateIdentifiers(GenEl.GenericTemplateTypes,
-                                        SpecializedItem.Params,GenScope);
+        SpecializedItem,GenScope,true);
       end
       end
     else
     else
       begin
       begin
@@ -16413,7 +16498,7 @@ begin
       // specialized generic array
       // specialized generic array
       GenScope.SpecializedItem:=SpecializedItem;
       GenScope.SpecializedItem:=SpecializedItem;
       AddSpecializedTemplateIdentifiers(GenEl.GenericTemplateTypes,
       AddSpecializedTemplateIdentifiers(GenEl.GenericTemplateTypes,
-                                        SpecializedItem.Params,GenScope);
+                                        SpecializedItem,GenScope,true);
       end
       end
     else
     else
       begin
       begin
@@ -16442,7 +16527,7 @@ begin
     // specialized generic record
     // specialized generic record
     GenScope.SpecializedItem:=SpecializedItem;
     GenScope.SpecializedItem:=SpecializedItem;
     AddSpecializedTemplateIdentifiers(GenEl.GenericTemplateTypes,
     AddSpecializedTemplateIdentifiers(GenEl.GenericTemplateTypes,
-                                      SpecializedItem.Params,GenScope);
+                                      SpecializedItem,GenScope,true);
     end
     end
   else if GenEl.GenericTemplateTypes.Count>0 then
   else if GenEl.GenericTemplateTypes.Count>0 then
     begin
     begin
@@ -16489,9 +16574,9 @@ begin
     SpecializedItem.HeaderScope:=HeaderScope;
     SpecializedItem.HeaderScope:=HeaderScope;
     TemplType:=TPasGenericTemplateType(GenericTemplateTypes[0]);
     TemplType:=TPasGenericTemplateType(GenericTemplateTypes[0]);
     HeaderScope.Element:=TemplType;
     HeaderScope.Element:=TemplType;
-    AddSpecializedTemplateIdentifiers(GenericTemplateTypes,
-                                      SpecializedItem.Params,HeaderScope);
     PushScope(HeaderScope);
     PushScope(HeaderScope);
+    AddSpecializedTemplateIdentifiers(GenericTemplateTypes,
+                                      SpecializedItem,HeaderScope,true);
     end
     end
   else
   else
     HeaderScope:=nil;
     HeaderScope:=nil;
@@ -16519,7 +16604,7 @@ begin
     begin
     begin
     GenScope.SpecializedItem:=SpecializedItem;
     GenScope.SpecializedItem:=SpecializedItem;
     AddSpecializedTemplateIdentifiers(GenericTemplateTypes,
     AddSpecializedTemplateIdentifiers(GenericTemplateTypes,
-                                      SpecializedItem.Params,GenScope);
+                                      SpecializedItem,GenScope,false);
     end;
     end;
   // specialize sub elements
   // specialize sub elements
   SpecializeMembers(GenEl,SpecEl);
   SpecializeMembers(GenEl,SpecEl);
@@ -24958,11 +25043,15 @@ procedure TPasResolver.ComputeElement(El: TPasElement; out
     if SpecType.CustomData is TPasSpecializeTypeData then
     if SpecType.CustomData is TPasSpecializeTypeData then
       begin
       begin
       TypeEl:=TPasSpecializeTypeData(SpecType.CustomData).SpecializedType;
       TypeEl:=TPasSpecializeTypeData(SpecType.CustomData).SpecializedType;
+      if TypeEl=nil then
+        RaiseNotYetImplemented(20190908153503,El);
       SetResolverIdentifier(ResolvedEl,btContext,TypeEl,TypeEl,TypeEl,[]);
       SetResolverIdentifier(ResolvedEl,btContext,TypeEl,TypeEl,TypeEl,[]);
       end
       end
     else
     else
       begin
       begin
       TypeEl:=SpecType.DestType;
       TypeEl:=SpecType.DestType;
+      if TypeEl=nil then
+        RaiseNotYetImplemented(20190908153434,El);
       SetResolverIdentifier(ResolvedEl,btContext,SpecType,TypeEl,SpecType,[]);
       SetResolverIdentifier(ResolvedEl,btContext,SpecType,TypeEl,SpecType,[]);
       end;
       end;
   end;
   end;

+ 19 - 17
packages/fcl-passrc/tests/tcresolvegenerics.pas

@@ -32,7 +32,7 @@ type
     // ToDo: constraint T:Unit2.TBird
     // ToDo: constraint T:Unit2.TBird
     // ToDo: constraint T:Unit2.TGen<word>
     // ToDo: constraint T:Unit2.TGen<word>
     procedure TestGen_ConstraintSpecialize;
     procedure TestGen_ConstraintSpecialize;
-    procedure TestGen_ConstraintTSpecializeT; // ToDo
+    procedure TestGen_ConstraintTSpecializeT;
     procedure TestGen_TemplNameEqTypeNameFail;
     procedure TestGen_TemplNameEqTypeNameFail;
     procedure TestGen_ConstraintInheritedMissingRecordFail;
     procedure TestGen_ConstraintInheritedMissingRecordFail;
     procedure TestGen_ConstraintInheritedMissingClassTypeFail;
     procedure TestGen_ConstraintInheritedMissingClassTypeFail;
@@ -330,28 +330,30 @@ end;
 
 
 procedure TTestResolveGenerics.TestGen_ConstraintTSpecializeT;
 procedure TTestResolveGenerics.TestGen_ConstraintTSpecializeT;
 begin
 begin
-  exit; // ToDo
   StartProgram(false);
   StartProgram(false);
   Add([
   Add([
-  '{$mode objfpc}',
+  '{$mode delphi}',
   'type',
   'type',
   '  TObject = class end;',
   '  TObject = class end;',
-  '  generic TAnt<S> = class m: S; end;',
-  '  generic TBird<X; Y:specialize TAnt<X>> = class',
-  '    o: Y;',
+  '  TAnt<S> = class m: S; end;',
+  '  TBird<X; Y: TAnt<X>> = class',
+  '    Ant: Y;',
+  '  end;',
+  '  TEagle<X; Y:X> = class',
+  '    e: Y;',
   '  end;',
   '  end;',
-  //'  generic TEagle<X; Y:X> = class',
-  //'    e: Y;',
-  //'  end;',
-  //'  generic TFireAnt<F> = class(specialize TAnt<F>) end;',
+  '  TFireAnt<F> = class(TAnt<F>) end;',
+  '  TAntWord = TAnt<word>;',
+  '  TBirdAntWord = TBird<word, TAnt<word>>;',
   'var',
   'var',
-  '  b: specialize TBird<word, specialize TAnt<word>>;',
-  //'  a: specialize TAnt<word>;',
-  //'  f: specialize TEagle<specialize TAnt<boolean>, specialize TFireAnt<boolean>>;',
-  //'  fb: specialize TFireAnt<boolean>;',
-  'begin',
-  //'  b.o:=a;',
-  //'  f.e:=fb;',
+  '  a: TAnt<word>;',
+  '  b: TBird<word, TAntWord>;',
+  '  c: TBird<TBirdAntWord, TAnt<TBirdAntWord>>;',
+  '  f: TEagle<TAnt<boolean>, TFireAnt<boolean>>;',
+  '  fb: TFireAnt<boolean>;',
+  'begin',
+  '  b.Ant:=a;',
+  '  f.e:=fb;',
   '']);
   '']);
   ParseProgram;
   ParseProgram;
 end;
 end;

+ 1 - 1
utils/pas2js/docs/translation.html

@@ -2958,7 +2958,7 @@ End.
     If <i>o</i> is <i>nil</i> it will give a JS error.<br>
     If <i>o</i> is <i>nil</i> it will give a JS error.<br>
     Local types (i.e. inside a procedure) do not have typeinfo.<br>
     Local types (i.e. inside a procedure) do not have typeinfo.<br>
     Open array parameters are not yet supported.<br>
     Open array parameters are not yet supported.<br>
-    Note that FPC <i>typeinfo(aClassVar)</i> returns the compiletime type, so it works on <i>nil</i>.<br>
+    Note that FPC <i>typeinfo(aClassVar)<i> returns the compiletime type, so it works on <i>nil</i>.<br>
     </div>
     </div>