Browse Source

fcl-passrc: varargs of type

git-svn-id: trunk@43325 -
Mattias Gaertner 5 years ago
parent
commit
e64ea172c2

+ 56 - 18
packages/fcl-passrc/src/pasresolver.pp

@@ -1709,6 +1709,9 @@ type
       Arg: TPasArgument; out ArgResolved: TPasResolverResult;
       Arg: TPasArgument; out ArgResolved: TPasResolverResult;
       Expr: TPasExpr; out ExprResolved: TPasResolverResult;
       Expr: TPasExpr; out ExprResolved: TPasResolverResult;
       SetReferenceFlags: boolean);
       SetReferenceFlags: boolean);
+    procedure ComputeArgumentExpr(const ArgResolved: TPasResolverResult;
+      Access: TArgumentAccess; Expr: TPasExpr; out ExprResolved: TPasResolverResult;
+      SetReferenceFlags: boolean);
     procedure ComputeArrayParams(Params: TParamsExpr;
     procedure ComputeArrayParams(Params: TParamsExpr;
       out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
       out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
       StartEl: TPasElement);
       StartEl: TPasElement);
@@ -2200,6 +2203,9 @@ type
       Params: TParamsExpr; RaiseOnError: boolean; EmitHints: boolean = false): integer;
       Params: TParamsExpr; RaiseOnError: boolean; EmitHints: boolean = false): integer;
     function CheckParamCompatibility(Expr: TPasExpr; Param: TPasArgument;
     function CheckParamCompatibility(Expr: TPasExpr; Param: TPasArgument;
       ParamNo: integer; RaiseOnError: boolean; SetReferenceFlags: boolean = false): integer;
       ParamNo: integer; RaiseOnError: boolean; SetReferenceFlags: boolean = false): integer;
+    function CheckParamResCompatibility(Expr: TPasExpr; const ExprResolved,
+      ParamResolved: TPasResolverResult; ParamNo: integer; RaiseOnError: boolean;
+      SetReferenceFlags: boolean): integer;
     function CheckAssignCompatibilityUserType(
     function CheckAssignCompatibilityUserType(
       const LHS, RHS: TPasResolverResult; ErrorEl: TPasElement;
       const LHS, RHS: TPasResolverResult; ErrorEl: TPasElement;
       RaiseOnIncompatible: boolean): integer;
       RaiseOnIncompatible: boolean): integer;
@@ -13450,12 +13456,7 @@ end;
 procedure TPasResolver.ComputeArgumentAndExpr(Arg: TPasArgument; out
 procedure TPasResolver.ComputeArgumentAndExpr(Arg: TPasArgument; out
   ArgResolved: TPasResolverResult; Expr: TPasExpr; out
   ArgResolved: TPasResolverResult; Expr: TPasExpr; out
   ExprResolved: TPasResolverResult; SetReferenceFlags: boolean);
   ExprResolved: TPasResolverResult; SetReferenceFlags: boolean);
-var
-  NeedVar: Boolean;
-  RHSFlags: TPasResolverComputeFlags;
 begin
 begin
-  NeedVar:=Arg.Access in [argVar, argOut];
-
   ComputeElement(Arg,ArgResolved,[]);
   ComputeElement(Arg,ArgResolved,[]);
   {$IFDEF VerbosePasResolver}
   {$IFDEF VerbosePasResolver}
   writeln('TPasResolver.ComputeArgumentAndExpr Arg=',GetTreeDbg(Arg,2),' ArgResolved=',GetResolverResultDbg(ArgResolved));
   writeln('TPasResolver.ComputeArgumentAndExpr Arg=',GetTreeDbg(Arg,2),' ArgResolved=',GetResolverResultDbg(ArgResolved));
@@ -13463,18 +13464,30 @@ begin
   if (ArgResolved.LoTypeEl=nil) and (Arg.ArgType<>nil) then
   if (ArgResolved.LoTypeEl=nil) and (Arg.ArgType<>nil) then
     RaiseInternalError(20160922163628,'TypeEl=nil for '+GetTreeDbg(Arg));
     RaiseInternalError(20160922163628,'TypeEl=nil for '+GetTreeDbg(Arg));
 
 
+  ComputeArgumentExpr(ArgResolved,Arg.Access,Expr,ExprResolved,SetReferenceFlags);
+end;
+
+procedure TPasResolver.ComputeArgumentExpr(
+  const ArgResolved: TPasResolverResult; Access: TArgumentAccess;
+  Expr: TPasExpr; out ExprResolved: TPasResolverResult;
+  SetReferenceFlags: boolean);
+var
+  NeedVar: Boolean;
+  RHSFlags: TPasResolverComputeFlags;
+begin
   RHSFlags:=[];
   RHSFlags:=[];
+  NeedVar:=Access in [argVar, argOut];
   if NeedVar then
   if NeedVar then
     Include(RHSFlags,rcNoImplicitProc)
     Include(RHSFlags,rcNoImplicitProc)
   else if IsProcedureType(ArgResolved,true)
   else if IsProcedureType(ArgResolved,true)
       or (ArgResolved.BaseType=btPointer)
       or (ArgResolved.BaseType=btPointer)
-      or (Arg.ArgType=nil) then
+      or ((ArgResolved.LoTypeEl=nil) and (ArgResolved.IdentEl is TPasArgument)) then
     Include(RHSFlags,rcNoImplicitProcType);
     Include(RHSFlags,rcNoImplicitProcType);
   if SetReferenceFlags then
   if SetReferenceFlags then
     Include(RHSFlags,rcSetReferenceFlags);
     Include(RHSFlags,rcSetReferenceFlags);
   ComputeElement(Expr,ExprResolved,RHSFlags);
   ComputeElement(Expr,ExprResolved,RHSFlags);
   {$IFDEF VerbosePasResolver}
   {$IFDEF VerbosePasResolver}
-  writeln('TPasResolver.ComputeArgumentAndExpr Expr=',GetTreeDbg(Expr,2),' ExprResolved=',GetResolverResultDbg(ExprResolved),' RHSFlags=',dbgs(RHSFlags));
+  writeln('TPasResolver.ComputeArgumentExpr Expr=',GetTreeDbg(Expr,2),' ExprResolved=',GetResolverResultDbg(ExprResolved),' RHSFlags=',dbgs(RHSFlags));
   {$ENDIF}
   {$ENDIF}
 end;
 end;
 
 
@@ -22808,7 +22821,7 @@ var
   ProcArgs: TFPList;
   ProcArgs: TFPList;
   i, ParamCnt, ParamCompatibility: Integer;
   i, ParamCnt, ParamCompatibility: Integer;
   Param, Value: TPasExpr;
   Param, Value: TPasExpr;
-  ParamResolved: TPasResolverResult;
+  ParamResolved, ArgResolved: TPasResolverResult;
   Flags: TPasResolverComputeFlags;
   Flags: TPasResolverComputeFlags;
 begin
 begin
   Result:=cExact;
   Result:=cExact;
@@ -22820,6 +22833,7 @@ begin
 
 
   // check args
   // check args
   ParamCnt:=length(Params.Params);
   ParamCnt:=length(Params.Params);
+  ArgResolved.BaseType:=btNone;;
   i:=0;
   i:=0;
   while i<ParamCnt do
   while i<ParamCnt do
     begin
     begin
@@ -22838,18 +22852,32 @@ begin
       begin
       begin
       if ptmVarargs in ProcType.Modifiers then
       if ptmVarargs in ProcType.Modifiers then
         begin
         begin
-        if SetReferenceFlags then
-          Flags:=[rcNoImplicitProcType,rcSetReferenceFlags]
+        if ProcType.VarArgsType<>nil then
+          begin
+          if ArgResolved.BaseType=btNone then
+            ComputeElement(ProcType.VarArgsType,ArgResolved,[rcType]);
+          ComputeArgumentExpr(ArgResolved,argConst,
+                                 Param,ParamResolved,SetReferenceFlags);
+          ParamCompatibility:=CheckParamResCompatibility(Param,ParamResolved,
+                                   ArgResolved,i,RaiseOnError,SetReferenceFlags);
+          if ParamCompatibility=cIncompatible then
+            exit(cIncompatible);
+          end
         else
         else
-          Flags:=[rcNoImplicitProcType];
-        ComputeElement(Param,ParamResolved,Flags,Param);
-        if not (rrfReadable in ParamResolved.Flags) then
           begin
           begin
-          if RaiseOnError then
-            RaiseVarExpected(20180712001415,Param,ParamResolved.IdentEl);
-          exit(cIncompatible);
+          if SetReferenceFlags then
+            Flags:=[rcNoImplicitProcType,rcSetReferenceFlags]
+          else
+            Flags:=[rcNoImplicitProcType];
+          ComputeElement(Param,ParamResolved,Flags,Param);
+          if not (rrfReadable in ParamResolved.Flags) then
+            begin
+            if RaiseOnError then
+              RaiseVarExpected(20180712001415,Param,ParamResolved.IdentEl);
+            exit(cIncompatible);
+            end;
+          ParamCompatibility:=cExact;
           end;
           end;
-        ParamCompatibility:=cExact;
         end
         end
       else
       else
         begin
         begin
@@ -25157,7 +25185,7 @@ function TPasResolver.CheckParamCompatibility(Expr: TPasExpr;
   SetReferenceFlags: boolean): integer;
   SetReferenceFlags: boolean): integer;
 var
 var
   ExprResolved, ParamResolved: TPasResolverResult;
   ExprResolved, ParamResolved: TPasResolverResult;
-  NeedVar, UseAssignError: Boolean;
+  NeedVar: Boolean;
 begin
 begin
   Result:=cIncompatible;
   Result:=cIncompatible;
 
 
@@ -25218,6 +25246,16 @@ begin
     exit(cIncompatible);
     exit(cIncompatible);
     end;
     end;
 
 
+  Result:=CheckParamResCompatibility(Expr,ExprResolved,ParamResolved,ParamNo,
+                                     RaiseOnError,SetReferenceFlags);
+end;
+
+function TPasResolver.CheckParamResCompatibility(Expr: TPasExpr;
+  const ExprResolved, ParamResolved: TPasResolverResult; ParamNo: integer;
+  RaiseOnError: boolean; SetReferenceFlags: boolean): integer;
+var
+  UseAssignError: Boolean;
+begin
   UseAssignError:=false;
   UseAssignError:=false;
   if RaiseOnError and (ExprResolved.BaseType in [btArrayLit,btArrayOrSet]) then
   if RaiseOnError and (ExprResolved.BaseType in [btArrayLit,btArrayOrSet]) then
     // e.g. Call([1,2]) -> on mismatch jump to the wrong param expression
     // e.g. Call([1,2]) -> on mismatch jump to the wrong param expression

+ 3 - 0
packages/fcl-passrc/src/pastree.pp

@@ -848,6 +848,7 @@ type
     Args: TFPList;        // List of TPasArgument objects
     Args: TFPList;        // List of TPasArgument objects
     CallingConvention: TCallingConvention;
     CallingConvention: TCallingConvention;
     Modifiers: TProcTypeModifiers;
     Modifiers: TProcTypeModifiers;
+    VarArgsType: TPasType;
     property IsOfObject: Boolean read GetIsOfObject write SetIsOfObject;
     property IsOfObject: Boolean read GetIsOfObject write SetIsOfObject;
     property IsNested : Boolean read GetIsNested write SetIsNested;
     property IsNested : Boolean read GetIsNested write SetIsNested;
     property IsReferenceTo : Boolean Read GetIsReference write SetIsReference;
     property IsReferenceTo : Boolean Read GetIsReference write SetIsReference;
@@ -3501,6 +3502,7 @@ begin
   for i := 0 to Args.Count - 1 do
   for i := 0 to Args.Count - 1 do
     TPasArgument(Args[i]).Release{$IFDEF CheckPasTreeRefCount}('TPasProcedureType.Args'){$ENDIF};
     TPasArgument(Args[i]).Release{$IFDEF CheckPasTreeRefCount}('TPasProcedureType.Args'){$ENDIF};
   FreeAndNil(Args);
   FreeAndNil(Args);
+  ReleaseAndNil(TPasElement(VarArgsType){$IFDEF CheckPasTreeRefCount},'CreateElement'{$ENDIF});
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -3526,6 +3528,7 @@ begin
   inherited ForEachCall(aMethodCall, Arg);
   inherited ForEachCall(aMethodCall, Arg);
   for i:=0 to Args.Count-1 do
   for i:=0 to Args.Count-1 do
     ForEachChildCall(aMethodCall,Arg,TPasElement(Args[i]),false);
     ForEachChildCall(aMethodCall,Arg,TPasElement(Args[i]),false);
+  ForEachChildCall(aMethodCall,Arg,VarArgsType,false);
 end;
 end;
 
 
 { TPasResultElement }
 { TPasResultElement }

+ 18 - 0
packages/fcl-passrc/src/pparser.pp

@@ -5113,10 +5113,28 @@ end;
 
 
 procedure TPasParser.HandleProcedureTypeModifier(ProcType: TPasProcedureType;
 procedure TPasParser.HandleProcedureTypeModifier(ProcType: TPasProcedureType;
   ptm: TProcTypeModifier);
   ptm: TProcTypeModifier);
+var
+  Expr: TPasExpr;
 begin
 begin
   if ptm in ProcType.Modifiers then
   if ptm in ProcType.Modifiers then
     ParseExcSyntaxError;
     ParseExcSyntaxError;
   Include(ProcType.Modifiers,ptm);
   Include(ProcType.Modifiers,ptm);
+  if ptm=ptmVarargs then
+    begin
+    NextToken;
+    if CurToken<>tkof then
+      begin
+      UngetToken;
+      exit;
+      end;
+    NextToken;
+    Expr:=nil;
+    try
+      ProcType.VarArgsType:=ParseTypeReference(ProcType,false,Expr);
+    finally
+      if Expr<>nil then Expr.Release{$IFDEF CheckPasTreeRefCount}('20191029145019'){$ENDIF};
+    end;
+    end;
 end;
 end;
 
 
 // Next token is expected to be a "(", ";" or for a function ":". The caller
 // Next token is expected to be a "(", ";" or for a function ":". The caller

+ 37 - 0
packages/fcl-passrc/tests/tcresolver.pas

@@ -449,6 +449,8 @@ type
     Procedure TestProcedureExternal;
     Procedure TestProcedureExternal;
     Procedure TestProc_UntypedParam_Forward;
     Procedure TestProc_UntypedParam_Forward;
     Procedure TestProc_Varargs;
     Procedure TestProc_Varargs;
+    Procedure TestProc_VarargsOfT;
+    Procedure TestProc_VarargsOfTMismatch;
     Procedure TestProc_ParameterExprAccess;
     Procedure TestProc_ParameterExprAccess;
     Procedure TestProc_FunctionResult_DeclProc;
     Procedure TestProc_FunctionResult_DeclProc;
     Procedure TestProc_TypeCastFunctionResult;
     Procedure TestProc_TypeCastFunctionResult;
@@ -7354,6 +7356,41 @@ begin
   ParseProgram;
   ParseProgram;
 end;
 end;
 
 
+procedure TTestResolver.TestProc_VarargsOfT;
+begin
+  StartProgram(false);
+  Add([
+  'procedure ProcA(i:longint); varargs of word; external;',
+  'procedure ProcB; varargs of boolean; external;',
+  'procedure ProcC(i: longint = 17); varargs of double; external;',
+  'begin',
+  '  ProcA(1);',
+  '  ProcA(2,3);',
+  '  ProcA(4,5,6);',
+  '  ProcB;',
+  '  ProcB();',
+  '  ProcB(false);',
+  '  ProcB(true,false);',
+  '  ProcC;',
+  '  ProcC();',
+  '  ProcC(7);',
+  '  ProcC(8,9.3);',
+  '  ProcC(8,9.3,1.3);',
+  '']);
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestProc_VarargsOfTMismatch;
+begin
+  StartProgram(false);
+  Add([
+  'procedure ProcA(i:longint); varargs of word; external;',
+  'begin',
+  '  ProcA(1,false);',
+  '']);
+  CheckResolverException('Incompatible type arg no. 2: Got "Boolean", expected "Word"',nIncompatibleTypeArgNo);
+end;
+
 procedure TTestResolver.TestProc_ParameterExprAccess;
 procedure TTestResolver.TestProc_ParameterExprAccess;
 begin
 begin
   StartProgram(false);
   StartProgram(false);