Browse Source

fcl-passrc: template is, is template, template(), atype(template)

git-svn-id: trunk@42881 -
Mattias Gaertner 6 years ago
parent
commit
869fc5c7b3

+ 57 - 3
packages/fcl-passrc/src/pasresolver.pp

@@ -4789,14 +4789,15 @@ begin
         or (C=TPasProcedureType)
         or (C=TPasProcedureType)
         or (C=TPasFunctionType)
         or (C=TPasFunctionType)
         or (C=TPasArrayType)
         or (C=TPasArrayType)
-        or (C=TPasRangeType) then
+        or (C=TPasRangeType)
+        or (C=TPasGenericTemplateType) then
       begin
       begin
       // type cast to user type
       // type cast to user type
       Abort:=true; // can't be overloaded
       Abort:=true; // can't be overloaded
       if Data^.Found<>nil then exit;
       if Data^.Found<>nil then exit;
       Distance:=CheckTypeCast(TPasType(El),Data^.Params,false);
       Distance:=CheckTypeCast(TPasType(El),Data^.Params,false);
       {$IFDEF VerbosePasResolver}
       {$IFDEF VerbosePasResolver}
-      writeln('TPasResolver.OnFindCallElements type cast to =',GetObjName(El),' Distance=',Distance);
+      writeln('TPasResolver.OnFindCallElements type cast to "',GetObjName(El),'" Distance=',Distance);
       {$ENDIF}
       {$ENDIF}
       CandidateFound:=true;
       CandidateFound:=true;
       end;
       end;
@@ -10209,7 +10210,8 @@ begin
         or (C=TPasSetType)
         or (C=TPasSetType)
         or (C=TPasPointerType)
         or (C=TPasPointerType)
         or (C=TPasArrayType)
         or (C=TPasArrayType)
-        or (C=TPasRangeType) then
+        or (C=TPasRangeType)
+        or (C=TPasGenericTemplateType) then
       begin
       begin
       // type cast
       // type cast
       FinishUntypedParams(Access);
       FinishUntypedParams(Access);
@@ -24217,6 +24219,10 @@ var
   ToTypeBaseType: TResolverBaseType;
   ToTypeBaseType: TResolverBaseType;
   C: TClass;
   C: TClass;
   ToProcType, FromProcType: TPasProcedureType;
   ToProcType, FromProcType: TPasProcedureType;
+  TemplType: TPasGenericTemplateType;
+  i: Integer;
+  Expr: TPasExpr;
+  ExprToken: TToken;
 begin
 begin
   Result:=cIncompatible;
   Result:=cIncompatible;
   ToTypeEl:=ToResolved.LoTypeEl;
   ToTypeEl:=ToResolved.LoTypeEl;
@@ -24372,6 +24378,25 @@ begin
           if Result=cIncompatible then
           if Result=cIncompatible then
             Result:=CheckTypeCastClassInstanceToClass(FromResolved,ToResolved,ErrorEl);
             Result:=CheckTypeCastClassInstanceToClass(FromResolved,ToResolved,ErrorEl);
           end
           end
+        else if FromTypeEl.ClassType=TPasGenericTemplateType then
+          begin
+          // e.g. classtype(T)
+          TemplType:=TPasGenericTemplateType(FromTypeEl);
+          for i:=0 to length(TemplType.Constraints)-1 do
+            begin
+            Expr:=TemplType.Constraints[i];
+            ExprToken:=GetGenericConstraintKeyword(Expr);
+            case ExprToken of
+            tkrecord: ; // invalid type cast
+            tkClass, tkconstructor:
+              Result:=cExact;
+            else
+              // identifier constraint: class or interface -> allow
+              Result:=cExact;
+              break;
+            end;
+            end;
+          end;
         end
         end
       else if FromResolved.BaseType=btPointer then
       else if FromResolved.BaseType=btPointer then
         begin
         begin
@@ -24381,6 +24406,35 @@ begin
       else if FromResolved.BaseType=btNil then
       else if FromResolved.BaseType=btNil then
         Result:=cExact; // nil to class or interface
         Result:=cExact; // nil to class or interface
       end
       end
+    else if C=TPasGenericTemplateType then
+      begin
+      // e.g. T(var)
+      TemplType:=TPasGenericTemplateType(ToTypeEl);
+      FromTypeEl:=FromResolved.LoTypeEl;
+      for i:=0 to length(TemplType.Constraints)-1 do
+        begin
+        Expr:=TemplType.Constraints[i];
+        ExprToken:=GetGenericConstraintKeyword(Expr);
+        case ExprToken of
+        tkrecord:
+          if FromResolved.BaseType=btContext then
+            begin
+            if FromTypeEl.ClassType=TPasRecordType then
+              // typecast record to template record
+              Result:=cExact
+            else if FromTypeEl.ClassType=TPasGenericType then
+              // typecast template to template record
+              Result:=cExact;
+            end;
+        tkClass, tkconstructor:
+          Result:=cExact;
+        else
+          // identifier constraint: class or interface -> allow
+          Result:=cExact;
+          break;
+        end;
+        end;
+      end
     else if C=TPasClassOfType then
     else if C=TPasClassOfType then
       begin
       begin
       //writeln('TPasResolver.CheckTypeCast class-of FromRes.TypeEl=',GetObjName(FromResolved.LoTypeEl),' FromRes.IdentEl=',GetObjName(FromResolved.IdentEl));
       //writeln('TPasResolver.CheckTypeCast class-of FromRes.TypeEl=',GetObjName(FromResolved.LoTypeEl),' FromRes.IdentEl=',GetObjName(FromResolved.IdentEl));

+ 36 - 3
packages/fcl-passrc/tests/tcresolvegenerics.pas

@@ -36,6 +36,7 @@ type
     procedure TestGen_ConstraintInheritedMissingClassTypeFail;
     procedure TestGen_ConstraintInheritedMissingClassTypeFail;
     procedure TestGen_ConstraintMultiParam;
     procedure TestGen_ConstraintMultiParam;
     procedure TestGen_ConstraintMultiParamClassMismatch;
     procedure TestGen_ConstraintMultiParamClassMismatch;
+    procedure TestGen_ConstraintClassType_DotIsAsTypeCast;
 
 
     // generic record
     // generic record
     procedure TestGen_RecordLocalNameDuplicateFail;
     procedure TestGen_RecordLocalNameDuplicateFail;
@@ -109,9 +110,6 @@ type
     // ToDo: for-in
     // ToDo: for-in
     procedure TestGen_TryExcept;
     procedure TestGen_TryExcept;
     // ToDo: call
     // ToDo: call
-    // ToDo: dot
-    // ToDo: is as
-    // ToDo: typecast
     // ToTo: nested proc
     // ToTo: nested proc
   end;
   end;
 
 
@@ -392,6 +390,41 @@ begin
     nIncompatibleTypesGotExpected);
     nIncompatibleTypesGotExpected);
 end;
 end;
 
 
+procedure TTestResolveGenerics.TestGen_ConstraintClassType_DotIsAsTypeCast;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode objfpc}',
+  'type',
+  '  TObject = class end;',
+  '  TAnt = class',
+  '    procedure Run; external; overload;',
+  '  end;',
+  '  TRedAnt = class(TAnt)',
+  '    procedure Run(w: word); external; overload;',
+  '  end;',
+  '  generic TBird<T: TRedAnt> = class',
+  '    y: T;',
+  '    procedure Fly;',
+  '  end;',
+  '  TFireAnt = class(TRedAnt);',
+  '  generic TEagle<U: TRedAnt> = class(TBird<U>) end;',
+  '  TRedEagle = specialize TEagle<TRedAnt>;',
+  'procedure TBird.Fly;',
+  'var f: TFireAnt;',
+  'begin',
+  '  y.Run;',
+  '  y.Run(3);',
+  '  if y is TFireAnt then',
+  '    f:=y as TFireAnt;',
+  '  f:=TFireAnt(y);',
+  '  y:=T(f);',
+  'end;',
+  'begin',
+  '']);
+  ParseProgram;
+end;
+
 procedure TTestResolveGenerics.TestGen_RecordLocalNameDuplicateFail;
 procedure TTestResolveGenerics.TestGen_RecordLocalNameDuplicateFail;
 begin
 begin
   StartProgram(false);
   StartProgram(false);