fcl-passrc: resolver: typecast procvar and pointer

git-svn-id: trunk@35808 -
This commit is contained in:
Mattias Gaertner 2017-04-16 19:09:47 +00:00
parent 790c9995fe
commit e32782f5b3
3 changed files with 349 additions and 92 deletions

View File

@ -131,6 +131,8 @@ Works:
- built-in functions pred, succ for range type and enums
- untyped parameters
- built-in procedure str(const boolean|integer|enumvalue|classinstance,var s: string)
- pointer TPasPointerType
- nil, assigned(), typecast, class, classref, dynarray, procvar
ToDo:
- fix slow lookup declaration proc in PParser
@ -141,7 +143,6 @@ ToDo:
- nested types
- check if constant is longint or int64
- for..in..do
- pointer TPasPointerType
- records - TPasRecordType,
- const TRecordValues
- function default(record type): record
@ -253,6 +254,7 @@ const
nSymbolCannotBePublished = 3053;
nCannotTypecastAType = 3054;
nTypeIdentifierExpected = 3055;
nCannotNestAnonymousX = 3056;
// resourcestring patterns of messages
resourcestring
@ -311,6 +313,7 @@ resourcestring
sSymbolCannotBePublished = 'Symbol cannot be published';
sCannotTypecastAType = 'Cannot type cast a type';
sTypeIdentifierExpected = 'Type identifier expected';
sCannotNestAnonymousX = 'Cannot nest anonymous %s';
type
TResolverBaseType = (
@ -964,7 +967,8 @@ type
proClassOfIs, // class-of supports is and as operator
proExtClassInstanceNoTypeMembers, // class members of external class cannot be accessed by instance
proOpenAsDynArrays, // open arrays work like dynamic arrays
proProcTypeWithoutIsNested // proc types can use nested procs without 'is nested'
proProcTypeWithoutIsNested, // proc types can use nested procs without 'is nested'
proMethodAddrAsPointer // can assign @method to a pointer
);
TPasResolverOptions = set of TPasResolverOption;
@ -976,7 +980,7 @@ type
TResolveDataListKind = (lkBuiltIn,lkModule);
procedure ClearResolveDataList(Kind: TResolveDataListKind);
private
FAnonymousEnumtypePostfix: String;
FAnonymousElTypePostfix: String;
FBaseTypes: array[TResolverBaseType] of TPasUnresolvedSymbolRef;
FBaseTypeStringIndex: TResolverBaseType;
FDefaultScope: TPasDefaultScope;
@ -1090,6 +1094,7 @@ type
procedure FinishTypeDef(El: TPasType); virtual;
procedure FinishEnumType(El: TPasEnumType); virtual;
procedure FinishSetType(El: TPasSetType); virtual;
procedure FinishSubElementType(Parent, El: TPasElement); virtual;
procedure FinishRangeType(El: TPasRangeType); virtual;
procedure FinishRecordType(El: TPasRecordType); virtual;
procedure FinishClassType(El: TPasClassType); virtual;
@ -1411,8 +1416,8 @@ type
property Options: TPasResolverOptions read FOptions write FOptions;
property ScopeClass_Class: TPasClassScopeClass read FScopeClass_Class write FScopeClass_Class;
property ScopeClass_WithExpr: TPasWithExprScopeClass read FScopeClass_WithExpr write FScopeClass_WithExpr;
property AnonymousEnumtypePostfix: String read FAnonymousEnumtypePostfix
write FAnonymousEnumtypePostfix; // default empty, if set, anonymous enumtypes are named SetName+Postfix and add to declarations
property AnonymousElTypePostfix: String read FAnonymousElTypePostfix
write FAnonymousElTypePostfix; // default empty, if set, anonymous element types are named ArrayName+Postfix and added to declarations
end;
function GetObjName(o: TObject): string;
@ -1421,6 +1426,7 @@ function GetTypeDesc(aType: TPasType; AddPath: boolean = false): string;
function GetTreeDesc(El: TPasElement; Indent: integer = 0): string;
function GetResolverResultDesc(const T: TPasResolverResult): string;
function GetResolverResultDescription(const T: TPasResolverResult; OnlyType: boolean = false): string;
function GetResolverResultDbg(const T: TPasResolverResult): string;
function ResolverResultFlagsToStr(const Flags: TPasResolverResultFlags): string;
procedure SetResolverIdentifier(out ResolvedType: TPasResolverResult;
BaseType: TResolverBaseType; IdentEl: TPasElement;
@ -1482,9 +1488,9 @@ begin
Result:=Result+')';
end;
if ProcType.IsOfObject then
Result:=Result+' of object';
Result:=Result+' '+ProcTypeModifiers[ptmOfObject];
if ProcType.IsNested then
Result:=Result+' is nested';
Result:=Result+' '+ProcTypeModifiers[ptmIsNested];
if cCallingConventions[ProcType.CallingConvention]<>'' then
Result:=Result+';'+cCallingConventions[ProcType.CallingConvention];
end;
@ -1638,9 +1644,9 @@ begin
if El is TPasFunction then
Result:=Result+':'+GetTreeDesc(TPasFunctionType(TPasFunction(El).ProcType).ResultEl,Indent);
if TPasProcedureType(El).IsOfObject then
Result:=Result+' of object';
Result:=Result+' '+ProcTypeModifiers[ptmOfObject];
if TPasProcedureType(El).IsNested then
Result:=Result+' is nested';
Result:=Result+' '+ProcTypeModifiers[ptmIsNested];
if cCallingConventions[TPasProcedureType(El).CallingConvention]<>'' then
Result:=Result+'; '+cCallingConventions[TPasProcedureType(El).CallingConvention];
end
@ -1756,6 +1762,18 @@ begin
Result:=T.IdentEl.Name+':'+Result;
end;
function GetResolverResultDbg(const T: TPasResolverResult): string;
begin
Result:='bt='+BaseTypeNames[T.BaseType];
if T.SubType<>btNone then
Result:=Result+' Sub='+BaseTypeNames[T.SubType];
Result:=Result
+' Ident='+GetObjName(T.IdentEl)
+' Type='+GetObjName(T.TypeEl)
+' Expr='+GetObjName(T.ExprEl)
+' Flags='+ResolverResultFlagsToStr(T.Flags);
end;
function ResolverResultFlagsToStr(const Flags: TPasResolverResultFlags): string;
var
f: TPasResolverResultFlag;
@ -2717,9 +2735,11 @@ begin
else if (C=TPasClassType)
or (C=TPasClassOfType)
or (C=TPasEnumType)
or (C=TPasProcedureType)
or (C=TPasFunctionType)
or (C=TPasArrayType) then
begin
// type cast to a class, class-of, enum, or array
// type cast to user type
Abort:=true; // can't be overloaded
if Data^.Found<>nil then exit;
Distance:=CheckTypeCast(TPasType(El),Data^.Params,false);
@ -3149,41 +3169,12 @@ var
RangeExpr: TBinaryExpr;
C: TClass;
EnumType: TPasType;
procedure CheckAnonymousElType;
var
Decl: TPasDeclarations;
EnumScope: TPasEnumTypeScope;
begin
if (EnumType.Name<>'') or (AnonymousEnumtypePostfix='') then exit;
if El.Name='' then
RaiseNotYetImplemented(20170415165455,EnumType);
// give anonymous enumtype a name
EnumType.Name:=El.Name+AnonymousEnumtypePostfix;
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.FinishSetType set="',GetObjName(El),'" named anonymous enumtype "',GetObjName(EnumType),'"');
{$ENDIF}
if not (El.Parent is TPasDeclarations) then
RaiseNotYetImplemented(20170415161624,EnumType,GetObjName(El.Parent));
Decl:=TPasDeclarations(El.Parent);
Decl.Declarations.Add(EnumType);
EnumType.AddRef;
EnumType.Parent:=Decl;
Decl.Types.Add(EnumType);
if EnumType is TPasEnumType then
begin
EnumScope:=TPasEnumTypeScope(EnumType.CustomData);
ReleaseAndNil(TPasElement(EnumScope.CanonicalSet));
EnumScope.CanonicalSet:=El;
end;
end;
begin
EnumType:=El.EnumType;
C:=EnumType.ClassType;
if C=TPasEnumType then
begin
CheckAnonymousElType;
FinishSubElementType(El,EnumType);
exit;
end
else if C=TPasRangeType then
@ -3191,7 +3182,7 @@ begin
RangeExpr:=TPasRangeType(EnumType).RangeExpr;
if RangeExpr.Parent=El then
CheckRangeExpr(RangeExpr.left,RangeExpr.right,StartResolved,EndResolved);
CheckAnonymousElType;
FinishSubElementType(El,EnumType);
exit;
end
else if C=TPasUnresolvedSymbolRef then
@ -3207,6 +3198,37 @@ begin
RaiseXExpectedButYFound(20170216151557,'enum type',EnumType.ElementTypeName,EnumType);
end;
procedure TPasResolver.FinishSubElementType(Parent, El: TPasElement);
var
Decl: TPasDeclarations;
EnumScope: TPasEnumTypeScope;
begin
if (El.Name<>'') or (AnonymousElTypePostfix='') then exit;
if Parent.Name='' then
RaiseMsg(20170415165455,nCannotNestAnonymousX,sCannotNestAnonymousX,[El.ElementTypeName],El);
if not (Parent.Parent is TPasDeclarations) then
RaiseMsg(20170416094735,nCannotNestAnonymousX,sCannotNestAnonymousX,[El.ElementTypeName],El);
// give anonymous sub type a name
El.Name:=Parent.Name+AnonymousElTypePostfix;
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.FinishSubElementType parent="',GetObjName(Parent),'" named anonymous type "',GetObjName(El),'"');
{$ENDIF}
Decl:=TPasDeclarations(Parent.Parent);
Decl.Declarations.Add(El);
El.AddRef;
El.Parent:=Decl;
Decl.Types.Add(El);
if (El.ClassType=TPasEnumType) and (Parent.ClassType=TPasSetType) then
begin
EnumScope:=TPasEnumTypeScope(El.CustomData);
if EnumScope.CanonicalSet<>Parent then
begin
ReleaseAndNil(TPasElement(EnumScope.CanonicalSet));
EnumScope.CanonicalSet:=TPasSetType(Parent);
end;
end;
end;
procedure TPasResolver.FinishRangeType(El: TPasRangeType);
var
StartResolved, EndResolved: TPasResolverResult;
@ -3258,6 +3280,7 @@ begin
else
RaiseXExpectedButYFound(20170216151609,'range',RangeResolved.IdentEl.ElementTypeName,Expr);
end;
FinishSubElementType(El,El.ElType);
end;
procedure TPasResolver.FinishConstDef(El: TPasConst);
@ -5013,12 +5036,12 @@ begin
begin
// FoundEl one element, but it was incompatible => raise error
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.ResolveParamsExpr found one element, but it was incompatible => check again to raise error');
writeln('TPasResolver.ResolveFuncParamsExpr found one element, but it was incompatible => check again to raise error. Found=',GetObjName(FindCallData.Found));
{$ENDIF}
if FindCallData.Found is TPasProcedure then
CheckCallProcCompatibility(TPasProcedure(FindCallData.Found).ProcType,Params,true)
else if FindCallData.Found is TPasProcedureType then
CheckCallProcCompatibility(TPasProcedureType(FindCallData.Found),Params,true)
CheckTypeCast(TPasProcedureType(FindCallData.Found),Params,true)
else if FindCallData.Found.ClassType=TPasUnresolvedSymbolRef then
begin
if FindCallData.Found.CustomData is TResElDataBuiltInProc then
@ -5059,7 +5082,7 @@ begin
// ToDo: create a hint for each candidate
El:=TPasElement(FindCallData.List[i]);
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.ResolveParamsExpr Overload Candidate: ',GetElementSourcePosStr(El),' ',GetTreeDesc(El));
writeln('TPasResolver.ResolveFuncParamsExpr Overload Candidate: ',GetElementSourcePosStr(El),' ',GetTreeDesc(El));
{$ENDIF}
Msg:=Msg+', ';
Msg:=Msg+GetElementSourcePosStr(El);
@ -5094,6 +5117,10 @@ begin
if (C=TPasClassType)
or (C=TPasClassOfType)
or (C=TPasEnumType)
or (C=TPasSetType)
or (C=TPasPointerType)
or (C=TPasProcedureType)
or (C=TPasFunctionType)
or (C=TPasArrayType) then
begin
// type cast
@ -5131,11 +5158,12 @@ begin
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.ResolveFuncParamsExpr FoundEl=',GetObjName(FoundEl),' CustomData=',GetObjName(FoundEl.CustomData));
{$ENDIF}
RaiseNotYetImplemented(20170306121908,Params);
RaiseMsg(20170306121908,nIllegalExpression,sIllegalExpression,[],Params);
end;
end
else
begin
// FoundEl is not a type, maybe a var
ComputeElement(FoundEl,ResolvedEl,[rcNoImplicitProc]);
if ResolvedEl.TypeEl is TPasProcedureType then
begin
@ -5145,7 +5173,7 @@ begin
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.ResolveFuncParamsExpr FoundEl=',GetObjName(FoundEl),' CustomData=',GetObjName(FoundEl.CustomData),' Resolvedel=',GetResolverResultDesc(ResolvedEl));
{$ENDIF}
RaiseNotYetImplemented(20170306104301,Params);
RaiseMsg(20170306104301,nIllegalExpression,sIllegalExpression,[],Params);
end;
end
else if Value.ClassType=TParamsExpr then
@ -5159,7 +5187,7 @@ begin
if IsProcedureType(ResolvedEl,true) then
begin
CheckCallProcCompatibility(TPasProcedureType(ResolvedEl.TypeEl),Params,true);
CreateReference(ResolvedEl.TypeEl,Value,Access);
CreateReference(TPasProcedureType(ResolvedEl.TypeEl),Value,Access);
exit;
end
end;
@ -5354,7 +5382,7 @@ end;
procedure TPasResolver.AccessExpr(Expr: TPasExpr;
Access: TResolvedRefAccess);
// called after a call overload was found for each element
// called after a call target was found, called for each element
// to set the rraParamToUnknownProc to Access
var
Ref: TResolvedReference;
@ -6417,16 +6445,39 @@ begin
end
else if ResolvedEl.TypeEl is TPasProcedureType then
begin
if rcConstant in Flags then
RaiseConstantExprExp(20170216152639,Params);
if ResolvedEl.TypeEl is TPasFunctionType then
// function call => return result
ComputeElement(TPasFunctionType(ResolvedEl.TypeEl).ResultEl,
ResolvedEl,Flags+[rcNoImplicitProc],StartEl)
if Params.Value is TParamsExpr then
begin
// e.g. Name()() or Name[]()
Include(ResolvedEl.Flags,rrfReadable);
end;
if rrfReadable in ResolvedEl.Flags then
begin
// call procvar
if rcConstant in Flags then
RaiseConstantExprExp(20170216152639,Params);
if ResolvedEl.TypeEl is TPasFunctionType then
// function call => return result
ComputeElement(TPasFunctionType(ResolvedEl.TypeEl).ResultEl,
ResolvedEl,Flags+[rcNoImplicitProc],StartEl)
else
// procedure call, result is neither readable nor writable
SetResolverTypeExpr(ResolvedEl,btProc,TPasProcedureType(ResolvedEl.TypeEl),[]);
Include(ResolvedEl.Flags,rrfCanBeStatement);
end
else
// procedure call, result is neither readable nor writable
SetResolverTypeExpr(ResolvedEl,btProc,TPasProcedureType(ResolvedEl.TypeEl),[]);
Include(ResolvedEl.Flags,rrfCanBeStatement);
begin
// typecast proctype
if length(Params.Params)<>1 then
begin
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.ComputeFuncParams DeclEl=',GetObjName(DeclEl),' ',GetResolverResultDbg(ResolvedEl));
{$ENDIF}
RaiseMsg(20170416185211,nWrongNumberOfParametersForTypeCast,
sWrongNumberOfParametersForTypeCast,[ResolvedEl.TypeEl.Name],Params);
end;
SetResolverValueExpr(ResolvedEl,btContext,TPasProcedureType(ResolvedEl.TypeEl),
Params.Params[0],[rrfReadable]);
end;
end
else if (DeclEl is TPasType) then
begin
@ -9018,15 +9069,15 @@ begin
exit;
end;
if Proc1.IsNested<>Proc2.IsNested then
exit(ModifierError('is nested'));
exit(ModifierError(ProcTypeModifiers[ptmIsNested]));
if Proc1.IsOfObject<>Proc2.IsOfObject then
begin
if (proProcTypeWithoutIsNested in Options) then
exit(ModifierError('of object'))
exit(ModifierError(ProcTypeModifiers[ptmOfObject]))
else if Proc1.IsNested then
// "is nested" can handle both, proc and method.
else
exit(ModifierError('of object'))
exit(ModifierError(ProcTypeModifiers[ptmOfObject]))
end;
if Proc1.CallingConvention<>Proc2.CallingConvention then
begin
@ -9234,7 +9285,7 @@ begin
[],ErrorEl);
exit(cIncompatible);
end
else if LHS.BaseType in [btRange,btSet,btModule,btArray] then
else if LHS.BaseType in [btRange,btSet,btModule,btArray,btProc] then
begin
if RaiseOnIncompatible then
RaiseMsg(20170216152432,nIllegalExpression,sIllegalExpression,[],ErrorEl);
@ -9300,8 +9351,10 @@ begin
Result:=cExact+1 // any pointer can take a btPointer
else if IsSameType(LHS.TypeEl,RHS.TypeEl) then
Result:=cExact // pointer of same type
else if (LHS.TypeEl<>nil) and (RHS.TypeEl<>nil) then
Result:=CheckAssignCompatibility(LHS.TypeEl,RHS.TypeEl,RaiseOnIncompatible);
else if (LHS.TypeEl.ClassType=TPasPointerType)
and (RHS.TypeEl.ClassType=TPasPointerType) then
Result:=CheckAssignCompatibility(TPasPointerType(LHS.TypeEl).DestType,
TPasPointerType(RHS.TypeEl).DestType,RaiseOnIncompatible);
end
else if IsBaseType(LHS.TypeEl,btPointer) then
begin
@ -9316,7 +9369,9 @@ begin
begin
if IsDynArray(RHS.TypeEl) then
Result:=cExact;
end;
end
else if (C=TPasProcedureType) or (C=TPasFunctionType) then
Result:=cExact+1;
end;
end;
end
@ -9713,7 +9768,7 @@ begin
if not ResolvedElCanBeVarParam(ExprResolved) then
begin
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.CheckParamCompatibility NeedWritable: Identifier=',GetObjName(ExprResolved.IdentEl),' Type=',GetObjName(ExprResolved.TypeEl),' Expr=',GetObjName(ExprResolved.ExprEl),' Flags=',ResolverResultFlagsToStr(ExprResolved.Flags));
writeln('TPasResolver.CheckParamCompatibility NeedWritable: ',GetResolverResultDbg(ExprResolved));
{$ENDIF}
if RaiseOnError then
RaiseMsg(20170216152450,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr);
@ -10152,8 +10207,8 @@ begin
exit(cIncompatible);
end;
Param:=Params.Params[0];
ComputeElement(Param,ParamResolved,[]);
ComputeElement(El,ResolvedEl,[]);
ComputeElement(Param,ParamResolved,[rcNoImplicitProc]);
ComputeElement(El,ResolvedEl,[rcNoImplicitProc]);
Result:=CheckTypeCastRes(ParamResolved,ResolvedEl,Param,RaiseOnError);
end;
@ -10164,9 +10219,10 @@ var
ToTypeEl, ToClassType, FromClassType: TPasType;
ToTypeBaseType: TResolverBaseType;
C: TClass;
ToProcType, FromProcType: TPasProcedureType;
begin
Result:=cIncompatible;
ToTypeEl:=ToResolved.TypeEl;
ToTypeEl:=ResolveAliasType(ToResolved.TypeEl);
if (ToTypeEl<>nil)
and (rrfReadable in FromResolved.Flags) then
begin
@ -10217,7 +10273,30 @@ begin
or (C=TPasClassOfType)
or (C=TPasPointerType)
or ((C=TPasArrayType) and IsDynArray(FromResolved.TypeEl)) then
Result:=cExact;
Result:=cExact
else if (C=TPasProcedureType) or (C=TPasFunctionType) then
begin
// from procvar to pointer
FromProcType:=TPasProcedureType(FromResolved.TypeEl);
if FromProcType.IsOfObject then
begin
if proMethodAddrAsPointer in Options then
Result:=cExact+1
else if RaiseOnError then
RaiseMsg(20170416183615,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
[FromProcType.ElementTypeName+' '+ProcTypeModifiers[ptmOfObject],
BaseTypeNames[btPointer]],ErrorEl);
end
else if FromProcType.IsNested then
begin
if RaiseOnError then
RaiseMsg(20170416183800,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
[FromProcType.ElementTypeName+' '+ProcTypeModifiers[ptmIsNested],
BaseTypeNames[btPointer]],ErrorEl);
end
else
Result:=cExact+1;
end;
end;
end;
end;
@ -10285,25 +10364,77 @@ begin
and IsBaseType(FromResolved.TypeEl,btPointer) then
Result:=cExact; // untyped pointer to dynnamic array
end;
end
else if (C=TPasProcedureType) or (C=TPasFunctionType) then
begin
ToProcType:=TPasProcedureType(ToTypeEl);
if IsBaseType(FromResolved.TypeEl,btPointer) then
begin
// type cast untyped pointer value to proctype
if ToProcType.IsOfObject then
begin
if proMethodAddrAsPointer in Options then
Result:=cExact+1
else if RaiseOnError then
RaiseMsg(20170416183940,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
[BaseTypeNames[btPointer],
ToProcType.ElementTypeName+' '+ProcTypeModifiers[ptmOfObject]],ErrorEl);
end
else if ToProcType.IsNested then
begin
if RaiseOnError then
RaiseMsg(20170416184149,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
[BaseTypeNames[btPointer],
ToProcType.ElementTypeName+' '+ProcTypeModifiers[ptmIsNested]],ErrorEl);
end
else
Result:=cExact+1;
end
else if FromResolved.BaseType=btContext then
begin
if FromResolved.TypeEl is TPasProcedureType then
begin
// type cast procvar to proctype
FromProcType:=TPasProcedureType(FromResolved.TypeEl);
if (FromProcType.IsOfObject<>ToProcType.IsOfObject)
and not (proMethodAddrAsPointer in Options) then
begin
if RaiseOnError then
RaiseMsg(20170416183109,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
[FromProcType.ElementTypeName+BoolToStr(FromProcType.IsOfObject,' '+ProcTypeModifiers[ptmOfObject],''),
ToProcType.ElementTypeName+BoolToStr(ToProcType.IsOfObject,' '+ProcTypeModifiers[ptmOfObject],'')],ErrorEl);
end
else if FromProcType.IsNested<>ToProcType.IsNested then
begin
if RaiseOnError then
RaiseMsg(20170416183305,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
[FromProcType.ElementTypeName+BoolToStr(FromProcType.IsNested,' '+ProcTypeModifiers[ptmIsNested],''),
ToProcType.ElementTypeName+BoolToStr(ToProcType.IsNested,' '+ProcTypeModifiers[ptmIsNested],'')],ErrorEl);
end
else
Result:=cExact+1;
end;
end;
end;
end
else if ToTypeEl<>nil then
begin
// FromResolved is not readable
if (FromResolved.BaseType=btContext)
and (FromResolved.TypeEl.ClassType=TPasClassType)
and (FromResolved.TypeEl=FromResolved.IdentEl)
and (ToResolved.BaseType=btContext)
and (ToResolved.TypeEl.ClassType=TPasClassOfType)
and (ToResolved.TypeEl=ToResolved.IdentEl) then
if FromResolved.BaseType=btContext then
begin
// for example class-of(Self) in a class function
ToClassType:=TPasClassOfType(ToTypeEl).DestType;
FromClassType:=TPasClassType(FromResolved.TypeEl);
Result:=CheckClassesAreRelated(ToClassType,FromClassType,ErrorEl);
if Result<cIncompatible then exit;
if (FromResolved.TypeEl.ClassType=TPasClassType)
and (FromResolved.TypeEl=FromResolved.IdentEl)
and (ToResolved.BaseType=btContext)
and (ToResolved.TypeEl.ClassType=TPasClassOfType)
and (ToResolved.TypeEl=ToResolved.IdentEl) then
begin
// for example class-of(Self) in a class function
ToClassType:=TPasClassOfType(ToTypeEl).DestType;
FromClassType:=TPasClassType(FromResolved.TypeEl);
Result:=CheckClassesAreRelated(ToClassType,FromClassType,ErrorEl);
end;
end;
if RaiseOnError then
if (Result=cIncompatible) and RaiseOnError then
begin
if FromResolved.IdentEl is TPasType then
RaiseMsg(20170404162610,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl);
@ -11014,6 +11145,7 @@ var
Value: TPasExpr;
Ref: TResolvedReference;
Decl: TPasElement;
C: TClass;
begin
Result:=false;
if (Params=nil) or (Params.Kind<>pekFuncParams) then exit;
@ -11023,13 +11155,20 @@ begin
if not (Value.CustomData is TResolvedReference) then exit;
Ref:=TResolvedReference(Value.CustomData);
Decl:=Ref.Declaration;
if (Decl.ClassType=TPasAliasType) or (Decl.ClassType=TPasTypeAliasType) then
C:=Decl.ClassType;
if (C=TPasAliasType) or (C=TPasTypeAliasType) then
begin
Decl:=ResolveAliasType(TPasAliasType(Decl));
if (Decl.ClassType=TPasClassType)
or (Decl.ClassType=TPasClassOfType)
or (Decl.ClassType=TPasEnumType) then
exit(true);
if (Decl.ClassType=TPasUnresolvedSymbolRef)
C:=Decl.ClassType;
end;
if (C=TPasProcedureType)
or (C=TPasFunctionType) then
exit(true)
else if (C=TPasClassType)
or (C=TPasClassOfType)
or (C=TPasEnumType) then
exit(true)
else if (C=TPasUnresolvedSymbolRef)
and (Decl.CustomData is TResElDataBaseType) then
exit(true);
end;

View File

@ -473,6 +473,7 @@ type
Procedure TestDynArrayOfLongint;
Procedure TestStaticArray;
Procedure TestArrayOfArray;
Procedure TestArrayOfArray_NameAnonymous;
Procedure TestFunctionReturningArray;
Procedure TestArray_LowHigh;
Procedure TestArray_AssignSameSignatureFail;
@ -528,10 +529,14 @@ type
Procedure TestProcType_AsArgOtherUnit;
Procedure TestProcType_Property;
Procedure TestProcType_PropertyCallWrongArgFail;
Procedure TestProcType_Typecast;
// pointer
Procedure TestPointer;
Procedure TestPointer_AssignPointerToClassFail;
Procedure TestPointer_TypecastToMethodTypeFail;
Procedure TestPointer_TypecastFromMethodTypeFail;
Procedure TestPointer_TypecastMethod_proMethodAddrAsPointer;
end;
function LinesToStr(Args: array of const): string;
@ -2439,7 +2444,7 @@ end;
procedure TTestResolver.TestSet_AnonymousEnumtypeName;
begin
ResolverEngine.AnonymousEnumtypePostfix:='$enum';
ResolverEngine.AnonymousElTypePostfix:='$enum';
StartProgram(false);
Add('type');
Add(' TFlags = set of (red, green);');
@ -7358,6 +7363,22 @@ begin
ParseProgram;
end;
procedure TTestResolver.TestArrayOfArray_NameAnonymous;
begin
ResolverEngine.AnonymousElTypePostfix:='$array';
StartProgram(false);
Add('type');
Add(' TArrA = array of array of longint;');
Add('var');
Add(' a: TArrA;');
Add('begin');
Add(' a[1][2]:=5;');
Add(' a[1,2]:=5;');
Add(' if a[2,1]=a[0,1] then ;');
Add(' a[3][4]:=a[5,6];');
ParseProgram;
end;
procedure TTestResolver.TestFunctionReturningArray;
begin
StartProgram(false);
@ -8110,7 +8131,7 @@ begin
Add('var n: TNotifyEvent;');
Add('begin');
Add(' n:=@ProcA;');
CheckResolverException('procedure type modifier "of object" mismatch',
CheckResolverException('procedure type modifier "of Object" mismatch',
PasResolver.nXModifierMismatchY);
end;
@ -8129,7 +8150,7 @@ begin
Add(' o: TObject;');
Add('begin');
Add(' n:=@o.ProcA;');
CheckResolverException('procedure type modifier "of object" mismatch',
CheckResolverException('procedure type modifier "of Object" mismatch',
PasResolver.nXModifierMismatchY);
end;
@ -8304,7 +8325,7 @@ begin
Add('begin');
Add(' Button1.OnClick := App.BtnClickHandler();');
CheckResolverException(
'Wrong number of parameters specified for call to "procedure BtnClickHandler(TObject) of object"',
'Wrong number of parameters specified for call to "procedure BtnClickHandler(TObject) of Object"',
nWrongNumberOfParametersForCallTo);
end;
@ -8328,7 +8349,7 @@ begin
Add('begin');
Add(' Button1.OnClick := @App.BtnClickHandler();');
CheckResolverException(
'Wrong number of parameters specified for call to "procedure BtnClickHandler(TObject) of object"',
'Wrong number of parameters specified for call to "procedure BtnClickHandler(TObject) of Object"',
nWrongNumberOfParametersForCallTo);
end;
@ -8538,6 +8559,32 @@ begin
nIncompatibleTypeArgNo);
end;
procedure TTestResolver.TestProcType_Typecast;
begin
StartProgram(false);
Add('type');
Add(' TNotifyEvent = procedure(Sender: Pointer) of object;');
Add(' TEvent = procedure of object;');
Add(' TProcA = procedure(i: longint);');
Add(' TFuncB = function(i, j: longint): longint;');
Add('var');
Add(' Notify: TNotifyEvent;');
Add(' Event: TEvent;');
Add(' ProcA: TProcA;');
Add(' FuncB: TFuncB;');
Add(' p: pointer;');
Add('begin');
Add(' Notify:=TNotifyEvent(Event);');
Add(' Event:=TEvent(Event);');
Add(' Event:=TEvent(Notify);');
Add(' ProcA:=TProcA(FuncB);');
Add(' FuncB:=TFuncB(FuncB);');
Add(' FuncB:=TFuncB(ProcA);');
Add(' ProcA:=TProcA(p);');
Add(' FuncB:=TFuncB(p);');
ParseProgram;
end;
procedure TTestResolver.TestPointer;
begin
StartProgram(false);
@ -8546,11 +8593,14 @@ begin
Add(' TClass = class of TObject;');
Add(' TMyPtr = pointer;');
Add(' TArrInt = array of longint;');
Add(' TFunc = function: longint;');
Add('procedure DoIt; begin end;');
Add('var');
Add(' p: TMyPtr;');
Add(' Obj: TObject;');
Add(' Cl: TClass;');
Add(' a: tarrint;');
Add(' f: TFunc;');
Add('begin');
Add(' p:=nil;');
Add(' if p=nil then;');
@ -8559,6 +8609,9 @@ begin
Add(' p:=obj;');
Add(' p:=cl;');
Add(' p:=a;');
Add(' p:=Pointer(f);');
Add(' p:=@DoIt;');
Add(' p:=Pointer(@DoIt)');
Add(' obj:=TObject(p);');
Add(' cl:=TClass(p);');
Add(' a:=TArrInt(p);');
@ -8579,6 +8632,49 @@ begin
nIncompatibleTypesGotExpected);
end;
procedure TTestResolver.TestPointer_TypecastToMethodTypeFail;
begin
StartProgram(false);
Add('type');
Add(' TEvent = procedure of object;');
Add('var');
Add(' p: pointer;');
Add(' e: TEvent;');
Add('begin');
Add(' e:=TEvent(p);');
CheckResolverException('Illegal type conversion: "Pointer" to "procedure type of Object"',
nIllegalTypeConversionTo);
end;
procedure TTestResolver.TestPointer_TypecastFromMethodTypeFail;
begin
StartProgram(false);
Add('type');
Add(' TEvent = procedure of object;');
Add('var');
Add(' p: pointer;');
Add(' e: TEvent;');
Add('begin');
Add(' p:=Pointer(e);');
CheckResolverException('Illegal type conversion: "procedure type of Object" to "Pointer"',
nIllegalTypeConversionTo);
end;
procedure TTestResolver.TestPointer_TypecastMethod_proMethodAddrAsPointer;
begin
ResolverEngine.Options:=ResolverEngine.Options+[proMethodAddrAsPointer];
StartProgram(false);
Add('type');
Add(' TEvent = procedure of object;');
Add('var');
Add(' p: pointer;');
Add(' e: TEvent;');
Add('begin');
Add(' e:=TEvent(p);');
Add(' p:=Pointer(e);');
ParseProgram;
end;
initialization
RegisterTests([TTestResolver]);

View File

@ -79,6 +79,7 @@ type
procedure TestM_Hint_UnitNotUsed_No_OnlyExternal;
procedure TestM_Hint_ParameterNotUsed;
procedure TestM_Hint_ParameterNotUsed_Abstract;
procedure TestM_Hint_ParameterNotUsedTypecast;
procedure TestM_Hint_LocalVariableNotUsed;
procedure TestM_Hint_InterfaceUnitVariableUsed;
procedure TestM_Hint_ValueParameterIsAssignedButNeverUsed;
@ -900,6 +901,27 @@ begin
CheckUnexpectedMessages;
end;
procedure TTestUseAnalyzer.TestM_Hint_ParameterNotUsedTypecast;
begin
StartProgram(true);
Add('type');
Add(' TObject = class end;');
Add(' TSortCompare = function(a,b: Pointer): integer;');
Add(' TObjCompare = function(a,b: TObject): integer;');
Add('procedure Sort(const Compare: TSortCompare);');
Add('begin');
Add(' Compare(nil,nil);');
Add('end;');
Add('procedure DoIt(const Compare: TObjCompare);');
Add('begin');
Add(' Sort(TSortCompare(Compare));');
Add('end;');
Add('begin');
Add(' DoIt(nil);');
AnalyzeProgram;
CheckUnexpectedMessages;
end;
procedure TTestUseAnalyzer.TestM_Hint_LocalVariableNotUsed;
begin
StartProgram(true);