Browse Source

fcl-passrc: resolver: mode delphi: error when passing alias type to var argument

git-svn-id: trunk@38897 -
Mattias Gaertner 7 years ago
parent
commit
76391fab52

+ 178 - 40
packages/fcl-passrc/src/pasresolver.pp

@@ -1334,6 +1334,10 @@ type
     procedure ComputeFuncParams(Params: TParamsExpr;
     procedure ComputeFuncParams(Params: TParamsExpr;
       out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
       out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
       StartEl: TPasElement);
       StartEl: TPasElement);
+    procedure ComputeTypeCast(ToLoType, ToHiType: TPasType;
+      Param: TPasExpr; const ParamResolved: TPasResolverResult;
+      out ResolvedEl: TPasResolverResult;
+      Flags: TPasResolverComputeFlags); virtual;
     procedure ComputeSetParams(Params: TParamsExpr;
     procedure ComputeSetParams(Params: TParamsExpr;
       out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
       out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
       StartEl: TPasElement);
       StartEl: TPasElement);
@@ -9887,9 +9891,10 @@ var
   BuiltInProc: TResElDataBuiltInProc;
   BuiltInProc: TResElDataBuiltInProc;
   Proc: TPasProcedure;
   Proc: TPasProcedure;
   aClass: TPasClassType;
   aClass: TPasClassType;
-  ResolvedTypeEl: TPasResolverResult;
+  ParamResolved: TPasResolverResult;
   Ref: TResolvedReference;
   Ref: TResolvedReference;
-  ParamTypeEl: TPasType;
+  DeclType: TPasType;
+  Param0: TPasExpr;
 begin
 begin
   if Params.Value.CustomData is TResolvedReference then
   if Params.Value.CustomData is TResolvedReference then
     begin
     begin
@@ -9913,16 +9918,18 @@ begin
       else if DeclEl.CustomData is TResElDataBaseType then
       else if DeclEl.CustomData is TResElDataBaseType then
         begin
         begin
         // type cast to base type
         // type cast to base type
-        if TResElDataBaseType(DeclEl.CustomData).BaseType=btCustom then
-          // custom base type
-          SetResolverValueExpr(ResolvedEl,btCustom,
-            TPasUnresolvedSymbolRef(DeclEl),TPasUnresolvedSymbolRef(DeclEl),
-            Params.Params[0],[rrfReadable])
-        else
-          SetResolverValueExpr(ResolvedEl,
-            TResElDataBaseType(DeclEl.CustomData).BaseType,
-            TPasUnresolvedSymbolRef(DeclEl),TPasUnresolvedSymbolRef(DeclEl),
-            Params.Params[0],[rrfReadable]);
+        DeclType:=TPasUnresolvedSymbolRef(DeclEl);
+        if length(Params.Params)<>1 then
+          begin
+          {$IFDEF VerbosePasResolver}
+          writeln('TPasResolver.ComputeFuncParams DeclEl=',GetObjName(DeclEl));
+          {$ENDIF}
+          RaiseMsg(20180503105409,nWrongNumberOfParametersForTypeCast,
+            sWrongNumberOfParametersForTypeCast,[DeclType.Name],Params);
+          end;
+        Param0:=Params.Params[0];
+        ComputeElement(Param0,ParamResolved,[]);
+        ComputeTypeCast(DeclType,DeclType,Param0,ParamResolved,ResolvedEl,Flags);
         end
         end
       else
       else
         RaiseNotYetImplemented(20161006133040,Params,GetResolverResultDbg(ResolvedEl));
         RaiseNotYetImplemented(20161006133040,Params,GetResolverResultDbg(ResolvedEl));
@@ -9978,7 +9985,7 @@ begin
           end
           end
         else
         else
           begin
           begin
-          // typecast proctype
+          // typecast to proctype
           if length(Params.Params)<>1 then
           if length(Params.Params)<>1 then
             begin
             begin
             {$IFDEF VerbosePasResolver}
             {$IFDEF VerbosePasResolver}
@@ -9987,32 +9994,19 @@ begin
             RaiseMsg(20170416185211,nWrongNumberOfParametersForTypeCast,
             RaiseMsg(20170416185211,nWrongNumberOfParametersForTypeCast,
               sWrongNumberOfParametersForTypeCast,[ResolvedEl.LoTypeEl.Name],Params);
               sWrongNumberOfParametersForTypeCast,[ResolvedEl.LoTypeEl.Name],Params);
             end;
             end;
-          SetResolverValueExpr(ResolvedEl,btContext,
-            ResolvedEl.LoTypeEl,ResolvedEl.HiTypeEl,
-            Params.Params[0],[rrfReadable]);
+          Param0:=Params.Params[0];
+          ComputeElement(Param0,ParamResolved,[]);
+          ComputeTypeCast(ResolvedEl.LoTypeEl,ResolvedEl.HiTypeEl,Param0,
+                          ParamResolved,ResolvedEl,Flags);
           end;
           end;
         end
         end
       else if (DeclEl is TPasType) then
       else if (DeclEl is TPasType) then
         begin
         begin
         // type cast
         // type cast
-        ResolvedTypeEl:=ResolvedEl;
-        ComputeElement(Params.Params[0],ResolvedEl,Flags,StartEl);
-        ParamTypeEl:=ResolvedEl.LoTypeEl;
-
-        ResolvedEl.BaseType:=ResolvedTypeEl.BaseType;
-        ResolvedEl.LoTypeEl:=ResolvedTypeEl.LoTypeEl;
-        ResolvedEl.HiTypeEl:=ResolvedTypeEl.HiTypeEl;
-        if not (rrfReadable in ResolvedEl.Flags) then
-          begin
-          // typecast a type to a value, e.g. Pointer(TObject)
-          ResolvedEl.Flags:=ResolvedEl.Flags+[rrfReadable];
-          end;
-        if (DeclEl is TPasClassType) and (ParamTypeEl is TPasClassType)
-            and (TPasClassType(DeclEl).ObjKind<>TPasClassType(ParamTypeEl).ObjKind) then
-          begin
-          // e.g. IntfType(ClassInstVar)
-          ResolvedEl.Flags:=ResolvedEl.Flags-[rrfWritable,rrfAssignable];
-          end;
+        Param0:=Params.Params[0];
+        ComputeElement(Param0,ParamResolved,[]);
+        ComputeTypeCast(ResolvedEl.LoTypeEl,ResolvedEl.HiTypeEl,Param0,
+                        ParamResolved,ResolvedEl,Flags);
         end
         end
       else
       else
         RaiseNotYetImplemented(20160928180048,Params,GetResolverResultDbg(ResolvedEl));
         RaiseNotYetImplemented(20160928180048,Params,GetResolverResultDbg(ResolvedEl));
@@ -10022,6 +10016,138 @@ begin
     RaiseNotYetImplemented(20160928174124,Params);
     RaiseNotYetImplemented(20160928174124,Params);
 end;
 end;
 
 
+procedure TPasResolver.ComputeTypeCast(ToLoType, ToHiType: TPasType;
+  Param: TPasExpr; const ParamResolved: TPasResolverResult; out
+  ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags);
+
+  function ParamIsVar: boolean;
+  var
+    IdentEl: TPasElement;
+  begin
+    IdentEl:=ParamResolved.IdentEl;
+    if IdentEl=nil then exit(false);
+    if [rcConstant,rcType]*Flags<>[] then
+      Result:=(IdentEl.ClassType=TPasConst) and (TPasConst(IdentEl).IsConst)
+    else
+      Result:=(IdentEl is TPasVariable)
+           or (IdentEl.ClassType=TPasArgument)
+           or (IdentEl.ClassType=TPasResultElement);
+  end;
+
+var
+  WriteFlags: TPasResolverResultFlags;
+  KeepWriteFlags: Boolean;
+  bt: TResolverBaseType;
+  Expr: TPasExpr;
+begin
+  {$IFDEF VerbosePasResolver}
+  writeln('TPasResolver.ComputeFuncParams START ToLoType=',GetObjName(ToLoType),' ',BoolToStr(ToLoType<>ToHiType,'ToHiType='+GetObjName(ToHiType),''),' ',GetResolverResultDbg(ParamResolved));
+  {$ENDIF}
+  if ToLoType.CustomData is TResElDataBaseType then
+    begin
+    // type cast to base type (or alias of base type)
+    bt:=GetActualBaseType(TResElDataBaseType(ToLoType.CustomData).BaseType);
+    SetResolverValueExpr(ResolvedEl,
+      TResElDataBaseType(ToLoType.CustomData).BaseType,
+      ToLoType,ToHiType,
+      Param,[rrfReadable]);
+    ResolvedEl.IdentEl:=ParamResolved.IdentEl;
+
+    WriteFlags:=ParamResolved.Flags*[rrfWritable,rrfAssignable];
+    if (WriteFlags<>[]) and ParamIsVar then
+      begin
+      KeepWriteFlags:=false;
+      // Param is writable -> check if typecast keeps this
+      if (bt=btPointer) then
+        begin
+        // typecast to pointer
+        if (ParamResolved.BaseType=btPointer)
+        or (ParamResolved.BaseType in [btString,btUnicodeString,btWideString])
+        or (ParamResolved.LoTypeEl=nil) // untyped
+        or (ParamResolved.LoTypeEl.ClassType=TPasClassType)
+        or IsDynArray(ParamResolved.LoTypeEl)
+        then
+          // e.g. pointer(ObjVar)
+          KeepWriteFlags:=true;
+        end
+      else if IsSameType(ToLoType,ParamResolved.LoTypeEl,prraNone) then
+        // e.g. Byte(TAliasByte)
+        KeepWriteFlags:=true;
+      if KeepWriteFlags then
+        ResolvedEl.Flags:=ResolvedEl.Flags+WriteFlags;
+      end;
+    end
+  else if ToLoType is TPasProcedureType then
+    begin
+    // typecast to proctype
+    if ParamIsVar then
+      WriteFlags:=ParamResolved.Flags*[rrfWritable,rrfAssignable]
+    else
+      WriteFlags:=[];
+    SetResolverValueExpr(ResolvedEl,btContext,
+      ToLoType,ToHiType,
+      Param,[rrfReadable]+WriteFlags);
+    ResolvedEl.IdentEl:=ParamResolved.IdentEl;
+    end
+  else
+    begin
+    // typecast to custom type, e.g. to classtype, recordtype, arraytype, range, set
+    if (Param.Parent is TParamsExpr) then
+      Expr:=TParamsExpr(Param.Parent)
+    else
+      Expr:=Param;
+    ComputeElement(ToHiType,ResolvedEl,Flags,Expr);
+    ResolvedEl.ExprEl:=Expr;
+    ResolvedEl.IdentEl:=ParamResolved.IdentEl;
+    ResolvedEl.Flags:=[rrfReadable];
+
+    WriteFlags:=ParamResolved.Flags*[rrfWritable,rrfAssignable];
+    if (WriteFlags<>[]) and ParamIsVar then
+      begin
+      KeepWriteFlags:=false;
+      if (rrfReadable in ResolvedEl.Flags) then
+        begin
+        // typecast a value
+        if ParamResolved.BaseType=btPointer then
+          begin
+          if (ToLoType.ClassType=TPasClassType)
+              or IsDynArray(ParamResolved.LoTypeEl) then
+            // aClassType(aPointer)
+            KeepWriteFlags:=true;
+          end
+        else if ParamResolved.LoTypeEl=nil then
+          // e.g. TAliasType(untyped)
+          KeepWriteFlags:=true
+        else if ToLoType=ParamResolved.LoTypeEl then
+          // e.g. TAliasType(ActualType)
+          KeepWriteFlags:=true
+        else if (ToLoType.ClassType=TPasClassType)
+            and (ParamResolved.LoTypeEl.ClassType=TPasClassType) then
+          begin
+          // e.g. aClassType(ObjVar)
+          if (TPasClassType(ToLoType).ObjKind<>TPasClassType(ParamResolved.LoTypeEl).ObjKind) then
+            // e.g. IntfType(ObjVar)
+          else
+            KeepWriteFlags:=true;
+          end
+        else if (ToLoType.ClassType=TPasRecordType)
+            and (ParamResolved.LoTypeEl.ClassType=TPasRecordType) then
+          // typecast record
+          KeepWriteFlags:=true;
+        end
+      else
+        begin
+        // typecast a type to a value, e.g. Pointer(TObject)
+        end;
+      if KeepWriteFlags then
+        ResolvedEl.Flags:=ResolvedEl.Flags+WriteFlags;
+      end;
+    end;
+  {$IFDEF VerbosePasResolver}
+  writeln('TPasResolver.ComputeFuncParams END ToLoType=',GetObjName(ToLoType),' ',BoolToStr(ToLoType<>ToHiType,'ToHiType='+GetObjName(ToHiType),''),' ',GetResolverResultDbg(ParamResolved),' Result=',GetResolverResultDbg(ResolvedEl));
+  {$ENDIF}
+end;
+
 procedure TPasResolver.ComputeSetParams(Params: TParamsExpr; out
 procedure TPasResolver.ComputeSetParams(Params: TParamsExpr; out
   ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
   ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
   StartEl: TPasElement);
   StartEl: TPasElement);
@@ -14789,11 +14915,15 @@ begin
     begin
     begin
     GotDesc:=GetTypeDescription(GotType);
     GotDesc:=GetTypeDescription(GotType);
     ExpDesc:=GetTypeDescription(ExpType);
     ExpDesc:=GetTypeDescription(ExpType);
-    if GotDesc=ExpDesc then
+    if GotDesc<>ExpDesc then exit;
+    if GotType.HiTypeEl<>ExpType.HiTypeEl then
       begin
       begin
-      GotDesc:=GetTypeDescription(GotType,true);
-      ExpDesc:=GetTypeDescription(ExpType,true);
+      GotDesc:=GetTypeDescription(GotType.HiTypeEl);
+      ExpDesc:=GetTypeDescription(ExpType.HiTypeEl);
+      if GotDesc<>ExpDesc then exit;
       end;
       end;
+    GotDesc:=GetTypeDescription(GotType,true);
+    ExpDesc:=GetTypeDescription(ExpType,true);
     end
     end
   else
   else
     begin
     begin
@@ -16459,7 +16589,9 @@ begin
   Result:=false;
   Result:=false;
   if [rrfReadable,rrfWritable]*ResolvedEl.Flags<>[rrfReadable,rrfWritable] then
   if [rrfReadable,rrfWritable]*ResolvedEl.Flags<>[rrfReadable,rrfWritable] then
     exit;
     exit;
-  if ResolvedEl.IdentEl=nil then exit;
+  if ResolvedEl.IdentEl=nil then
+    exit(true);
+
   IdentEl:=ResolvedEl.IdentEl;
   IdentEl:=ResolvedEl.IdentEl;
   if IdentEl.ClassType=TPasVariable then
   if IdentEl.ClassType=TPasVariable then
     exit(NotLocked(IdentEl));
     exit(NotLocked(IdentEl));
@@ -16846,13 +16978,19 @@ begin
         if ExprResolved.IdentEl is TPasConst then
         if ExprResolved.IdentEl is TPasConst then
           RaiseMsg(20180430012609,nCantAssignValuesToConstVariable,sCantAssignValuesToConstVariable,[],Expr)
           RaiseMsg(20180430012609,nCantAssignValuesToConstVariable,sCantAssignValuesToConstVariable,[],Expr)
         else
         else
-          RaiseMsg(20180430012457,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr);
+          RaiseMsg(20180430012457,nVariableIdentifierExpected,sVariableIdentifierExpected,
+            [],Expr);
         end;
         end;
       exit;
       exit;
       end;
       end;
     if (ParamResolved.BaseType=ExprResolved.BaseType) then
     if (ParamResolved.BaseType=ExprResolved.BaseType) then
       begin
       begin
-      if IsSameType(ParamResolved.LoTypeEl,ExprResolved.LoTypeEl,prraNone) then
+      if msDelphi in CurrentParser.CurrentModeswitches then
+        begin
+        if IsSameType(ParamResolved.HiTypeEl,ExprResolved.HiTypeEl,prraSimple) then
+          exit(cExact);
+        end
+      else if IsSameType(ParamResolved.LoTypeEl,ExprResolved.LoTypeEl,prraNone) then
         exit(cExact);
         exit(cExact);
       end;
       end;
     if (Param.ArgType=nil) then
     if (Param.ArgType=nil) then

+ 71 - 2
packages/fcl-passrc/tests/tcresolver.pas

@@ -369,7 +369,10 @@ type
     Procedure TestProcParamAccess;
     Procedure TestProcParamAccess;
     Procedure TestFunctionResult;
     Procedure TestFunctionResult;
     Procedure TestProcedureResultFail;
     Procedure TestProcedureResultFail;
-    Procedure TestProc_ArgVarTypeAlias;
+    Procedure TestProc_ArgVarPrecisionLossFail;
+    Procedure TestProc_ArgVarTypeAliasObjFPC;
+    Procedure TestProc_ArgVarTypeAliasDelphi; // ToDo
+    Procedure TestProc_ArgVarTypeAliasDelphiMismatchFail; // ToDo
     Procedure TestProcOverload;
     Procedure TestProcOverload;
     Procedure TestProcOverloadImplDuplicateFail;
     Procedure TestProcOverloadImplDuplicateFail;
     Procedure TestProcOverloadImplDuplicate2Fail;
     Procedure TestProcOverloadImplDuplicate2Fail;
@@ -5525,26 +5528,92 @@ begin
     nParserExpectTokenError);
     nParserExpectTokenError);
 end;
 end;
 
 
-procedure TTestResolver.TestProc_ArgVarTypeAlias;
+procedure TTestResolver.TestProc_ArgVarPrecisionLossFail;
 begin
 begin
   StartProgram(false);
   StartProgram(false);
   Add([
   Add([
   'type',
   'type',
   '  TColor = type longint;',
   '  TColor = type longint;',
+  '  TByte = byte;',
   'procedure DoColor(var c: TColor); external;',
   'procedure DoColor(var c: TColor); external;',
+  'var',
+  '  b: TByte;',
+  'begin',
+  '  DoColor(TColor(b));',
+  '']);
+  CheckResolverException(sVariableIdentifierExpected,nVariableIdentifierExpected);
+end;
+
+procedure TTestResolver.TestProc_ArgVarTypeAliasObjFPC;
+begin
+  StartProgram(false);
+  Add([
+  'type',
+  '  TColor = type longint;',
+  'procedure DoColor(var c: TColor); external;',
+  'procedure TakeColor(c: TColor); external;',
   'procedure DoInt(var i: longint); external;',
   'procedure DoInt(var i: longint); external;',
   'var',
   'var',
   '  i: longint;',
   '  i: longint;',
   '  c: TColor;',
   '  c: TColor;',
   'begin',
   'begin',
   '  DoColor(c);',
   '  DoColor(c);',
+  '  DoColor(longint(c));',
   '  DoColor(i);',
   '  DoColor(i);',
+  '  DoColor(TColor(i));',
+  '  TakeColor(c);',
+  '  TakeColor(longint(c));',
+  '  TakeColor(i);',
+  '  TakeColor(TColor(i));',
   '  DoInt(i);',
   '  DoInt(i);',
+  '  DoInt(TColor(i));',
   '  DoInt(c);',
   '  DoInt(c);',
+  '  DoInt(longint(c));',
+  '']);
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestProc_ArgVarTypeAliasDelphi;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode delphi}',
+  'type',
+  '  TColor = type longint;',
+  'procedure DoColor(var c: TColor); external;',
+  'procedure TakeColor(c: TColor); external;',
+  'procedure DoInt(var i: longint); external;',
+  'var',
+  '  i: longint;',
+  '  c: TColor;',
+  'begin',
+  '  DoColor(c);',
+  '  DoColor(TColor(i));',
+  '  TakeColor(i);',
+  '  TakeColor(longint(c));',
+  '  DoInt(i);',
+  '  DoInt(longint(c));',
   '']);
   '']);
   ParseProgram;
   ParseProgram;
 end;
 end;
 
 
+procedure TTestResolver.TestProc_ArgVarTypeAliasDelphiMismatchFail;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode delphi}',
+  'type',
+  '  TColor = type longint;',
+  'procedure DoColor(var c: TColor); external;',
+  'var',
+  '  i: longint;',
+  'begin',
+  '  DoColor(i);',
+  '']);
+  CheckResolverException('Incompatible type arg no. 1: Got "Longint", expected "TColor". Var param must match exactly.',
+    nIncompatibleTypeArgNoVarParamMustMatchExactly);
+end;
+
 procedure TTestResolver.TestProcOverload;
 procedure TTestResolver.TestProcOverload;
 var
 var
   El: TPasElement;
   El: TPasElement;

+ 1 - 0
packages/pastojs/src/fppas2js.pp

@@ -344,6 +344,7 @@ Works:
 - typecast byte(longword) -> value & $ff
 - typecast byte(longword) -> value & $ff
 
 
 ToDos:
 ToDos:
+- TRecType(anotherRec).field
 - 'new', 'Function' -> class var use .prototype
 - 'new', 'Function' -> class var use .prototype
 - btArrayLit
 - btArrayLit
   a: array of jsvalue;
   a: array of jsvalue;