Răsfoiți Sursa

fcl-passrc: resolver: typecast unrelated classes: only warn instead of error

git-svn-id: trunk@44137 -
Mattias Gaertner 5 ani în urmă
părinte
comite
5498835ad8

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

@@ -205,6 +205,7 @@ const
   nCouldNotInferTypeArgXForMethodY = 3139;
   nCouldNotInferTypeArgXForMethodY = 3139;
   nInferredTypeXFromDiffArgsMismatchFromMethodY = 3140;
   nInferredTypeXFromDiffArgsMismatchFromMethodY = 3140;
   nParamOfThisTypeCannotHaveDefVal = 3141;
   nParamOfThisTypeCannotHaveDefVal = 3141;
+  nClassTypesAreNotRelatedXY = 3142;
 
 
   // using same IDs as FPC
   // using same IDs as FPC
   nVirtualMethodXHasLowerVisibility = 3250; // was 3050
   nVirtualMethodXHasLowerVisibility = 3250; // was 3050
@@ -357,6 +358,7 @@ resourcestring
   sCouldNotInferTypeArgXForMethodY = 'Could not infer generic type argument "%s" for method "%s"';
   sCouldNotInferTypeArgXForMethodY = 'Could not infer generic type argument "%s" for method "%s"';
   sInferredTypeXFromDiffArgsMismatchFromMethodY = 'Inferred type "%s" from different arguments mismatch for method "%s"';
   sInferredTypeXFromDiffArgsMismatchFromMethodY = 'Inferred type "%s" from different arguments mismatch for method "%s"';
   sParamOfThisTypeCannotHaveDefVal = 'Parameters of this type cannot have default values';
   sParamOfThisTypeCannotHaveDefVal = 'Parameters of this type cannot have default values';
+  sClassTypesAreNotRelatedXY = 'Class types "%s" and "%s" are not related';
 
 
 type
 type
   { TResolveData - base class for data stored in TPasElement.CustomData }
   { TResolveData - base class for data stored in TPasElement.CustomData }

+ 48 - 14
packages/fcl-passrc/src/pasresolver.pp

@@ -10786,6 +10786,24 @@ begin
         [GetElementTypeName(FoundEl)+' '+FoundEl.Name],NameExpr);
         [GetElementTypeName(FoundEl)+' '+FoundEl.Name],NameExpr);
     end;
     end;
 
 
+  if FoundEl is TPasType then
+    begin
+      // typecast
+      TypeEl:=ResolveAliasType(TPasType(FoundEl));
+      C:=TypeEl.ClassType;
+      if C=TPasUnresolvedSymbolRef then
+        begin
+        // typecast to built-in type
+        if TypeEl.CustomData is TResElDataBaseType then
+          CheckTypeCast(TypeEl,Params,true); // emit warnings
+        end
+      else
+        begin
+        // typecast to user type
+        CheckTypeCast(TypeEl,Params,true); // emit warnings
+        end;
+    end;
+
   // FoundEl compatible element -> create reference
   // FoundEl compatible element -> create reference
   Ref:=CreateReference(FoundEl,NameExpr,rraRead);
   Ref:=CreateReference(FoundEl,NameExpr,rraRead);
   if FindCallData.StartScope.ClassType=ScopeClass_WithExpr then
   if FindCallData.StartScope.ClassType=ScopeClass_WithExpr then
@@ -26101,8 +26119,18 @@ end;
 function TPasResolver.CheckTypeCastRes(const FromResolved,
 function TPasResolver.CheckTypeCastRes(const FromResolved,
   ToResolved: TPasResolverResult; ErrorEl: TPasElement; RaiseOnError: boolean
   ToResolved: TPasResolverResult; ErrorEl: TPasElement; RaiseOnError: boolean
   ): integer;
   ): integer;
+
+  procedure WarnClassTypesAreNotRelated(GotType, ExpType: TPasClassType);
+  var
+    GotDesc, ExpDesc: String;
+  begin
+    GetIncompatibleTypeDesc(GotType,ExpType,GotDesc,ExpDesc);
+    LogMsg(20200209140450,mtWarning,nClassTypesAreNotRelatedXY,
+      sClassTypesAreNotRelatedXY,[GotDesc,ExpDesc],ErrorEl);
+  end;
+
 var
 var
-  ToTypeEl, ToClassType, FromClassType, FromTypeEl: TPasType;
+  ToTypeEl, ToType, FromType, FromTypeEl: TPasType;
   ToTypeBaseType: TResolverBaseType;
   ToTypeBaseType: TResolverBaseType;
   C: TClass;
   C: TClass;
   ToProcType, FromProcType: TPasProcedureType;
   ToProcType, FromProcType: TPasProcedureType;
@@ -26110,6 +26138,7 @@ var
   i: Integer;
   i: Integer;
   ConToken: TToken;
   ConToken: TToken;
   ConEl: TPasElement;
   ConEl: TPasElement;
+  ToClassType, FromClassType: TPasClassType;
 begin
 begin
   Result:=cIncompatible;
   Result:=cIncompatible;
   ToTypeEl:=ToResolved.LoTypeEl;
   ToTypeEl:=ToResolved.LoTypeEl;
@@ -26229,34 +26258,39 @@ begin
       end
       end
     else if C=TPasClassType then
     else if C=TPasClassType then
       begin
       begin
+      ToClassType:=TPasClassType(ToTypeEl);
       // to class
       // to class
       if FromResolved.BaseType=btContext then
       if FromResolved.BaseType=btContext then
         begin
         begin
         FromTypeEl:=FromResolved.LoTypeEl;
         FromTypeEl:=FromResolved.LoTypeEl;
         if FromTypeEl.ClassType=TPasClassType then
         if FromTypeEl.ClassType=TPasClassType then
           begin
           begin
+          FromClassType:=TPasClassType(FromTypeEl);
           if FromResolved.IdentEl is TPasType then
           if FromResolved.IdentEl is TPasType then
             RaiseMsg(20170404162606,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl);
             RaiseMsg(20170404162606,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl);
-          if TPasClassType(FromTypeEl).ObjKind=TPasClassType(ToTypeEl).ObjKind then
+          if FromClassType.ObjKind=ToClassType.ObjKind then
             begin
             begin
             // type cast upwards or downwards
             // type cast upwards or downwards
             Result:=CheckSrcIsADstType(FromResolved,ToResolved);
             Result:=CheckSrcIsADstType(FromResolved,ToResolved);
             if Result=cIncompatible then
             if Result=cIncompatible then
               Result:=CheckSrcIsADstType(ToResolved,FromResolved);
               Result:=CheckSrcIsADstType(ToResolved,FromResolved);
+            if RaiseOnError then
+              WarnClassTypesAreNotRelated(FromClassType,ToClassType);
+            Result:=cCompatible;
             end
             end
-          else if TPasClassType(ToTypeEl).ObjKind=okInterface then
+          else if ToClassType.ObjKind=okInterface then
             begin
             begin
-            if (TPasClassType(FromTypeEl).ObjKind=okClass)
-                and (not TPasClassType(FromTypeEl).IsExternal) then
+            if (FromClassType.ObjKind=okClass)
+                and (not FromClassType.IsExternal) then
               begin
               begin
               // e.g. intftype(classinstvar)
               // e.g. intftype(classinstvar)
               Result:=cCompatible;
               Result:=cCompatible;
               end;
               end;
             end
             end
-          else if TPasClassType(FromTypeEl).ObjKind=okInterface then
+          else if FromClassType.ObjKind=okInterface then
             begin
             begin
-            if (TPasClassType(ToTypeEl).ObjKind=okClass)
-                and (not TPasClassType(ToTypeEl).IsExternal) then
+            if (ToClassType.ObjKind=okClass)
+                and (not ToClassType.IsExternal) then
               begin
               begin
               // e.g. classtype(intfvar)
               // e.g. classtype(intfvar)
               Result:=cCompatible;
               Result:=cCompatible;
@@ -26339,9 +26373,9 @@ begin
           if (FromResolved.IdentEl is TPasType) then
           if (FromResolved.IdentEl is TPasType) then
             RaiseMsg(20170404162604,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl);
             RaiseMsg(20170404162604,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl);
           // type cast  classof(classof-var)  upwards or downwards
           // type cast  classof(classof-var)  upwards or downwards
-          ToClassType:=TPasClassOfType(ToTypeEl).DestType;
-          FromClassType:=TPasClassOfType(FromResolved.LoTypeEl).DestType;
-          Result:=CheckClassesAreRelated(ToClassType,FromClassType);
+          ToType:=TPasClassOfType(ToTypeEl).DestType;
+          FromType:=TPasClassOfType(FromResolved.LoTypeEl).DestType;
+          Result:=CheckClassesAreRelated(ToType,FromType);
           end;
           end;
         end
         end
       else if FromResolved.BaseType=btPointer then
       else if FromResolved.BaseType=btPointer then
@@ -26526,9 +26560,9 @@ begin
             and (ToTypeEl=ToResolved.IdentEl) then
             and (ToTypeEl=ToResolved.IdentEl) then
           begin
           begin
           // for example  class-of(Self) in a class function
           // for example  class-of(Self) in a class function
-          ToClassType:=TPasClassOfType(ToTypeEl).DestType;
-          FromClassType:=TPasClassType(FromTypeEl);
-          Result:=CheckClassesAreRelated(ToClassType,FromClassType);
+          ToType:=TPasClassOfType(ToTypeEl).DestType;
+          FromType:=TPasClassType(FromTypeEl);
+          Result:=CheckClassesAreRelated(ToType,FromType);
           end;
           end;
         end;
         end;
       end;
       end;

+ 21 - 19
packages/fcl-passrc/tests/tcresolver.pas

@@ -592,7 +592,7 @@ type
     Procedure TestClass_OperatorAsOnNonTypeFail;
     Procedure TestClass_OperatorAsOnNonTypeFail;
     Procedure TestClassAsFuncResult;
     Procedure TestClassAsFuncResult;
     Procedure TestClassTypeCast;
     Procedure TestClassTypeCast;
-    Procedure TestClassTypeCastUnrelatedFail;
+    Procedure TestClassTypeCastUnrelatedWarn;
     Procedure TestClass_TypeCastSelf;
     Procedure TestClass_TypeCastSelf;
     Procedure TestClass_TypeCaseMultipleParamsFail;
     Procedure TestClass_TypeCaseMultipleParamsFail;
     Procedure TestClass_TypeCastAssign;
     Procedure TestClass_TypeCastAssign;
@@ -10350,26 +10350,28 @@ begin
   ParseProgram;
   ParseProgram;
 end;
 end;
 
 
-procedure TTestResolver.TestClassTypeCastUnrelatedFail;
+procedure TTestResolver.TestClassTypeCastUnrelatedWarn;
 begin
 begin
   StartProgram(false);
   StartProgram(false);
-  Add('type');
-  Add('  {#TOBJ}TObject = class');
-  Add('  end;');
-  Add('  {#A}TClassA = class');
-  Add('    id: longint;');
-  Add('  end;');
-  Add('  {#B}TClassB = class');
-  Add('    Name: string;');
-  Add('  end;');
-  Add('var');
-  Add('  {#o}{=TOBJ}o: TObject;');
-  Add('  {#va}{=A}va: TClassA;');
-  Add('  {#vb}{=B}vb: TClassB;');
-  Add('begin');
-  Add('  {@vb}vb:=TClassB({@va}va);');
-  CheckResolverException('Illegal type conversion: "TClassA" to "class TClassB"',
-    nIllegalTypeConversionTo);
+  Add([
+  'type',
+  '  {#TOBJ}TObject = class',
+  '  end;',
+  '  {#A}TClassA = class',
+  '    id: longint;',
+  '  end;',
+  '  {#B}TClassB = class',
+  '    Name: string;',
+  '  end;',
+  'var',
+  '  {#o}{=TOBJ}o: TObject;',
+  '  {#va}{=A}va: TClassA;',
+  '  {#vb}{=B}vb: TClassB;',
+  'begin',
+  '  {@vb}vb:=TClassB({@va}va);']);
+  ParseProgram;
+  CheckResolverHint(mtWarning,nClassTypesAreNotRelatedXY,'Class types "TClassA" and "TClassB" are not related');
+  CheckResolverUnexpectedHints;
 end;
 end;
 
 
 procedure TTestResolver.TestClass_TypeCastSelf;
 procedure TTestResolver.TestClass_TypeCastSelf;

+ 51 - 1
packages/pastojs/tests/tcgenerics.pas

@@ -6,7 +6,7 @@ interface
 
 
 uses
 uses
   Classes, SysUtils, fpcunit, testregistry,
   Classes, SysUtils, fpcunit, testregistry,
-  TCModules, FPPas2Js;
+  TCModules, FPPas2Js, PScanner, PasResolveEval;
 
 
 type
 type
 
 
@@ -34,9 +34,12 @@ type
     //Procedure TestGen_Class_ReferGenClass_DelphiFail;
     //Procedure TestGen_Class_ReferGenClass_DelphiFail;
     Procedure TestGen_Class_ClassConstructor;
     Procedure TestGen_Class_ClassConstructor;
     // ToDo: rename local const T
     // ToDo: rename local const T
+    Procedure TestGen_Class_TypeCastSpecializesWarn;
 
 
     // generic external class
     // generic external class
     procedure TestGen_ExtClass_Array;
     procedure TestGen_ExtClass_Array;
+    // ToDo: TestGen_ExtClass_GenJSValueAssign  TExt<JSValue> := TExt<Word>
+    // ToDo: TestGen_ExtClass_TypeCastJSValue  TExt<Word>(aTExt<JSValue>) and vice versa
 
 
     // statements
     // statements
     Procedure TestGen_InlineSpec_Constructor;
     Procedure TestGen_InlineSpec_Constructor;
@@ -628,6 +631,53 @@ begin
     '']));
     '']));
 end;
 end;
 
 
+procedure TTestGenerics.TestGen_Class_TypeCastSpecializesWarn;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode delphi}',
+  'type',
+  '  TObject = class end;',
+  '  TBird<T> = class F: T; end;',
+  '  TBirdWord = TBird<Word>;',
+  '  TBirdChar = TBird<Char>;',
+  'var',
+  '  w: TBirdWord;',
+  '  c: TBirdChar;',
+  'begin',
+  '  w:=TBirdWord(c);',
+  '']);
+  ConvertProgram;
+  CheckSource('TestGen_Class_TypeCastSpecializesWarn',
+    LinesToStr([ // statements
+    'rtl.createClass($mod, "TObject", null, function () {',
+    '  this.$init = function () {',
+    '  };',
+    '  this.$final = function () {',
+    '  };',
+    '});',
+    'rtl.createClass($mod, "TBird$G1", $mod.TObject, function () {',
+    '  this.$init = function () {',
+    '    $mod.TObject.$init.call(this);',
+    '    this.F = 0;',
+    '  };',
+    '});',
+    'rtl.createClass($mod, "TBird$G2", $mod.TObject, function () {',
+    '  this.$init = function () {',
+    '    $mod.TObject.$init.call(this);',
+    '    this.F = "";',
+    '  };',
+    '});',
+    'this.w = null;',
+    'this.c = null;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.w = $mod.c;',
+    '']));
+  CheckHint(mtWarning,nClassTypesAreNotRelatedXY,'Class types "TBird$G2<Char>" and "TBird$G1<Word>" are not related');
+  CheckResolverUnexpectedHints();
+end;
+
 procedure TTestGenerics.TestGen_ExtClass_Array;
 procedure TTestGenerics.TestGen_ExtClass_Array;
 begin
 begin
   StartProgram(false);
   StartProgram(false);