fcl-passrc: generics: allow both a<t> and a<s,t>

git-svn-id: trunk@42735 -
This commit is contained in:
Mattias Gaertner 2019-08-19 08:22:22 +00:00
parent 652e1985a9
commit c3b1450c4b
8 changed files with 558 additions and 219 deletions

View File

@ -160,7 +160,7 @@ const
nIllegalQualifierAfter = 3084; nIllegalQualifierAfter = 3084;
nIllegalQualifierInFrontOf = 3085; nIllegalQualifierInFrontOf = 3085;
nIllegalQualifierWithin = 3086; nIllegalQualifierWithin = 3086;
nMethodClassXInOtherUnitY = 3087; nClassXNotFoundInThisModule = 3087;
nClassMethodsMustBeStaticInX = 3088; nClassMethodsMustBeStaticInX = 3088;
nCannotMixMethodResolutionAndDelegationAtX = 3089; nCannotMixMethodResolutionAndDelegationAtX = 3089;
nImplementsDoesNotSupportArrayProperty = 3101; nImplementsDoesNotSupportArrayProperty = 3101;
@ -199,6 +199,7 @@ const
nTypeParamXIsMissingConstraintY = 3133; nTypeParamXIsMissingConstraintY = 3133;
nTypeParamXIsNotCompatibleWithY = 3134; nTypeParamXIsNotCompatibleWithY = 3134;
nTypeParamXMustSupportIntfY = 3135; nTypeParamXMustSupportIntfY = 3135;
nTypeParamsNotAllowedOnX = 3136;
// using same IDs as FPC // using same IDs as FPC
nVirtualMethodXHasLowerVisibility = 3250; // was 3050 nVirtualMethodXHasLowerVisibility = 3250; // was 3050
@ -306,7 +307,7 @@ resourcestring
sIllegalQualifierAfter = 'illegal qualifier "%s" after "%s"'; sIllegalQualifierAfter = 'illegal qualifier "%s" after "%s"';
sIllegalQualifierInFrontOf = 'illegal qualifier "%s" in front of "%s"'; sIllegalQualifierInFrontOf = 'illegal qualifier "%s" in front of "%s"';
sIllegalQualifierWithin = 'illegal qualifier "%s" within "%s"'; sIllegalQualifierWithin = 'illegal qualifier "%s" within "%s"';
sMethodClassXInOtherUnitY = 'method class "%s" in other unit "%s"'; sClassXNotFoundInThisModule = 'class "%s" not found in this module';
sNoMatchingImplForIntfMethodXFound = 'No matching implementation for interface method "%s" found'; sNoMatchingImplForIntfMethodXFound = 'No matching implementation for interface method "%s" found';
sClassMethodsMustBeStaticInX = 'Class methods must be static in %s'; sClassMethodsMustBeStaticInX = 'Class methods must be static in %s';
sCannotMixMethodResolutionAndDelegationAtX = 'Cannot mix method resolution and delegation at %s'; sCannotMixMethodResolutionAndDelegationAtX = 'Cannot mix method resolution and delegation at %s';
@ -345,6 +346,7 @@ resourcestring
sTypeParamXIsMissingConstraintY = 'Type parameter "%s" is missing constraint "%s"'; sTypeParamXIsMissingConstraintY = 'Type parameter "%s" is missing constraint "%s"';
sTypeParamXIsNotCompatibleWithY = 'Type parameter "%s" is not compatible with type "%s"'; sTypeParamXIsNotCompatibleWithY = 'Type parameter "%s" is not compatible with type "%s"';
sTypeParamXMustSupportIntfY = 'Type parameter "%s" must support interface "%s"'; sTypeParamXMustSupportIntfY = 'Type parameter "%s" must support interface "%s"';
sTypeParamsNotAllowedOnX = 'Type parameters not allowed on %s';
type type
{ TResolveData - base class for data stored in TPasElement.CustomData } { TResolveData - base class for data stored in TPasElement.CustomData }
@ -785,6 +787,7 @@ function CodePointToUnicodeString(u: longword): UnicodeString;
function GetObjName(o: TObject): string; function GetObjName(o: TObject): string;
function GetObjPath(o: TObject): string; function GetObjPath(o: TObject): string;
function GetTypeParamCommas(Cnt: integer): string;
function dbgs(const Flags: TResEvalFlags): string; overload; function dbgs(const Flags: TResEvalFlags): string; overload;
function dbgs(v: TResEvalValue): string; overload; function dbgs(v: TResEvalValue): string; overload;
@ -1002,11 +1005,23 @@ begin
end; end;
function GetObjName(o: TObject): string; function GetObjName(o: TObject): string;
var
GenType: TPasGenericType;
begin begin
if o=nil then if o=nil then
Result:='nil' Result:='nil'
else if o is TPasElement then else if o is TPasElement then
Result:=TPasElement(o).Name+':'+o.ClassName begin
Result:=TPasElement(o).Name;
if o is TPasGenericType then
begin
GenType:=TPasGenericType(o);
if (GenType.GenericTemplateTypes<>nil)
and (GenType.GenericTemplateTypes.Count>0) then
Result:=Result+GetTypeParamCommas(GenType.GenericTemplateTypes.Count);
end;
Result:=Result+':'+o.ClassName;
end
else else
Result:=o.ClassName; Result:=o.ClassName;
end; end;
@ -1014,6 +1029,7 @@ end;
function GetObjPath(o: TObject): string; function GetObjPath(o: TObject): string;
var var
El: TPasElement; El: TPasElement;
GenType: TPasGenericType;
begin begin
if o is TPasElement then if o is TPasElement then
begin begin
@ -1023,6 +1039,13 @@ begin
begin begin
if El<>o then if El<>o then
Result:='.'+Result; Result:='.'+Result;
if El is TPasGenericType then
begin
GenType:=TPasGenericType(El);
if (GenType.GenericTemplateTypes<>nil)
and (GenType.GenericTemplateTypes.Count>0) then
Result:=GetTypeParamCommas(GenType.GenericTemplateTypes.Count)+Result;
end;
if El.Name<>'' then if El.Name<>'' then
begin begin
if IsValidIdent(El.Name) then if IsValidIdent(El.Name) then
@ -1039,6 +1062,14 @@ begin
Result:=GetObjName(o); Result:=GetObjName(o);
end; end;
function GetTypeParamCommas(Cnt: integer): string;
begin
if Cnt<=0 then
Result:=''
else
Result:='<'+StringOfChar(',',Cnt-1)+'>';
end;
function dbgs(const Flags: TResEvalFlags): string; function dbgs(const Flags: TResEvalFlags): string;
var var
s: string; s: string;

View File

@ -1547,7 +1547,7 @@ type
procedure AddEnumValue(El: TPasEnumValue); virtual; procedure AddEnumValue(El: TPasEnumValue); virtual;
procedure AddProperty(El: TPasProperty); virtual; procedure AddProperty(El: TPasProperty); virtual;
procedure AddProcedureType(El: TPasProcedureType; TypeParams: TFPList); virtual; procedure AddProcedureType(El: TPasProcedureType; TypeParams: TFPList); virtual;
procedure AddProcedure(El: TPasProcedure); virtual; procedure AddProcedure(El: TPasProcedure; TypeParams: TFPList); virtual;
procedure AddProcedureBody(El: TProcedureBody); virtual; procedure AddProcedureBody(El: TProcedureBody); virtual;
procedure AddArgument(El: TPasArgument); virtual; procedure AddArgument(El: TPasArgument); virtual;
procedure AddFunctionResult(El: TPasResultElement); virtual; procedure AddFunctionResult(El: TPasResultElement); virtual;
@ -1609,11 +1609,10 @@ type
procedure FinishClassOfType(El: TPasClassOfType); virtual; procedure FinishClassOfType(El: TPasClassOfType); virtual;
procedure FinishPointerType(El: TPasPointerType); virtual; procedure FinishPointerType(El: TPasPointerType); virtual;
procedure FinishArrayType(El: TPasArrayType); virtual; procedure FinishArrayType(El: TPasArrayType); virtual;
procedure FinishGenericTemplateTypes(aType: TPasGenericType); virtual; procedure FinishAliasType(El: TPasAliasType); virtual;
procedure FinishGenericTemplateType(El: TPasGenericTemplateType); virtual; procedure FinishGenericTemplateType(El: TPasGenericTemplateType); virtual;
procedure FinishSpecializeType(El: TPasSpecializeType); virtual; procedure FinishSpecializeType(El: TPasSpecializeType); virtual;
procedure FinishResourcestring(El: TPasResString); virtual; procedure FinishResourcestring(El: TPasResString); virtual;
procedure FinishProcNameParts(aProc: TPasProcedure); virtual;
procedure FinishProcedure(aProc: TPasProcedure); virtual; procedure FinishProcedure(aProc: TPasProcedure); virtual;
procedure FinishProcedureType(El: TPasProcedureType); virtual; procedure FinishProcedureType(El: TPasProcedureType); virtual;
procedure FinishMethodDeclHeader(Proc: TPasProcedure); virtual; procedure FinishMethodDeclHeader(Proc: TPasProcedure); virtual;
@ -1643,6 +1642,7 @@ type
procedure CheckProcSignatureMatch(DeclProc, ImplProc: TPasProcedure; CheckNames: boolean); procedure CheckProcSignatureMatch(DeclProc, ImplProc: TPasProcedure; CheckNames: boolean);
procedure CheckPendingForwardProcs(El: TPasElement); procedure CheckPendingForwardProcs(El: TPasElement);
procedure CheckPointerCycle(El: TPasPointerType); procedure CheckPointerCycle(El: TPasPointerType);
procedure CheckGenericTemplateTypes(El: TPasGenericType); virtual;
procedure ComputeUnaryNot(El: TUnaryExpr; var ResolvedEl: TPasResolverResult; procedure ComputeUnaryNot(El: TUnaryExpr; var ResolvedEl: TPasResolverResult;
Flags: TPasResolverComputeFlags); virtual; Flags: TPasResolverComputeFlags); virtual;
procedure ComputeBinaryExpr(Bin: TBinaryExpr; procedure ComputeBinaryExpr(Bin: TBinaryExpr;
@ -1762,6 +1762,7 @@ type
procedure SpecializeProcedureBody(GenEl, SpecEl: TProcedureBody); procedure SpecializeProcedureBody(GenEl, SpecEl: TProcedureBody);
procedure SpecializeDeclarations(GenEl, SpecEl: TPasDeclarations); procedure SpecializeDeclarations(GenEl, SpecEl: TPasDeclarations);
procedure SpecializeSpecializeType(GenEl, SpecEl: TPasSpecializeType); procedure SpecializeSpecializeType(GenEl, SpecEl: TPasSpecializeType);
procedure SpecializeGenericTemplateType(GenEl, SpecEl: TPasGenericTemplateType);
procedure SpecializeArgument(GenEl, SpecEl: TPasArgument); procedure SpecializeArgument(GenEl, SpecEl: TPasArgument);
procedure SpecializeImplBlock(GenEl, SpecEl: TPasImplBlock); procedure SpecializeImplBlock(GenEl, SpecEl: TPasImplBlock);
procedure SpecializeImplAsmStatement(GenEl, SpecEl: TPasImplAsmStatement); procedure SpecializeImplAsmStatement(GenEl, SpecEl: TPasImplAsmStatement);
@ -5206,13 +5207,31 @@ end;
function TPasResolver.AddIdentifier(Scope: TPasIdentifierScope; function TPasResolver.AddIdentifier(Scope: TPasIdentifierScope;
const aName: String; El: TPasElement; const Kind: TPasIdentifierKind const aName: String; El: TPasElement; const Kind: TPasIdentifierKind
): TPasIdentifier; ): TPasIdentifier;
function SkipGenericTypes(Identifier: TPasIdentifier;
TypeParamCnt: integer): TPasIdentifier;
var
CurEl: TPasElement;
begin
while Identifier<>nil do
begin
CurEl:=Identifier.Element;
if not (CurEl is TPasGenericType) then break;
if GetTypeParameterCount(TPasGenericType(CurEl))=TypeParamCnt then break;
Identifier:=Identifier.NextSameIdentifier;
end;
Result:=Identifier;
end;
var var
Group: TPasGroupScope; Group: TPasGroupScope;
Identifier, OlderIdentifier: TPasIdentifier; Identifier, OlderIdentifier: TPasIdentifier;
OlderEl: TPasElement; OlderEl: TPasElement;
C: TClass; C: TClass;
i: Integer; i, TypeParamCnt: Integer;
OtherScope: TPasIdentifierScope; OtherScope: TPasIdentifierScope;
ParentScope: TPasScope;
IsGeneric: Boolean;
begin begin
if aName='' then exit(nil); if aName='' then exit(nil);
if Scope is TPasGroupScope then if Scope is TPasGroupScope then
@ -5222,6 +5241,16 @@ begin
end end
else else
Group:=nil; Group:=nil;
if El is TPasGenericType then
begin
IsGeneric:=true;
TypeParamCnt:=GetTypeParameterCount(TPasGenericType(El));
end
else
begin
IsGeneric:=false;
TypeParamCnt:=0;
end;
if (El.Visibility=visPublished) then if (El.Visibility=visPublished) then
begin begin
@ -5241,6 +5270,8 @@ begin
begin begin
OtherScope:=Group.Scopes[i]; OtherScope:=Group.Scopes[i];
OlderIdentifier:=OtherScope.FindLocalIdentifier(aName); OlderIdentifier:=OtherScope.FindLocalIdentifier(aName);
if IsGeneric then
OlderIdentifier:=SkipGenericTypes(OlderIdentifier,TypeParamCnt);
while OlderIdentifier<>nil do while OlderIdentifier<>nil do
begin begin
OlderEl:=OlderIdentifier.Element; OlderEl:=OlderIdentifier.Element;
@ -5263,29 +5294,51 @@ begin
// check duplicate in current scope // check duplicate in current scope
OlderIdentifier:=Identifier.NextSameIdentifier; OlderIdentifier:=Identifier.NextSameIdentifier;
if IsGeneric then
OlderIdentifier:=SkipGenericTypes(OlderIdentifier,TypeParamCnt);
if OlderIdentifier<>nil then if OlderIdentifier<>nil then
begin begin
if (OlderIdentifier.Element.ClassType=TPasEnumValue) OlderEl:=OlderIdentifier.Element;
and (OlderIdentifier.Element.Parent.Parent<>Scope.Element) then if (OlderEl.ClassType=TPasEnumValue)
and (OlderEl.Parent.Parent<>Scope.Element) then
begin begin
// this enum was propagated from a sub type -> remove enum from this scope // this enum was propagated from a sub type -> remove enum from this scope
if OlderIdentifier.NextSameIdentifier<>nil then if OlderIdentifier.NextSameIdentifier<>nil then
RaiseNotYetImplemented(20190807114726,El,GetElementSourcePosStr(OlderIdentifier.Element)); RaiseNotYetImplemented(20190807114726,El,GetElementSourcePosStr(OlderEl));
Scope.RemoveLocalIdentifier(OlderIdentifier.Element); Scope.RemoveLocalIdentifier(OlderEl);
OlderIdentifier:=nil; OlderIdentifier:=nil;
end; OlderEl:=nil;
if (El.Visibility=visPublished) and (El is TPasProcedure) end
and (OlderIdentifier.Element is TPasProcedure) then else if (El.Visibility=visPublished) and (El is TPasProcedure)
and (OlderEl is TPasProcedure) then
// published method bites method in same scope // published method bites method in same scope
RaiseMsg(20190626175432,nDuplicatePublishedMethodXAtY, RaiseMsg(20190626175432,nDuplicatePublishedMethodXAtY,
sDuplicatePublishedMethodXAtY, sDuplicatePublishedMethodXAtY,
[aName,GetElementSourcePosStr(OlderIdentifier.Element)],El); [aName,GetElementSourcePosStr(OlderEl)],El)
if (Identifier.Kind=pikSimple) else if (Identifier.Kind=pikSimple)
or (OlderIdentifier.Kind=pikSimple) then or (OlderIdentifier.Kind=pikSimple) then
// duplicate identifier // duplicate identifier
RaiseMsg(20170216151530,nDuplicateIdentifier,sDuplicateIdentifier, RaiseMsg(20170216151530,nDuplicateIdentifier,sDuplicateIdentifier,
[aName,GetElementSourcePosStr(OlderIdentifier.Element)],El); [aName,GetElementSourcePosStr(OlderEl)],El);
end;
if (Scope=TopScope) and (Scope is TPasSectionScope) then
begin
ParentScope:=Scopes[ScopeCount-2];
if ParentScope is TPasSectionScope then
begin
OlderIdentifier:=TPasSectionScope(ParentScope).FindLocalIdentifier(aName);
if IsGeneric then
OlderIdentifier:=SkipGenericTypes(OlderIdentifier,TypeParamCnt);
if OlderIdentifier<>nil then
begin
OlderEl:=OlderIdentifier.Element;
if (Identifier.Kind=pikSimple)
or (OlderIdentifier.Kind=pikSimple) then
RaiseMsg(20190818141630,nDuplicateIdentifier,sDuplicateIdentifier,
[aName,GetElementSourcePosStr(OlderEl)],El);
end;
end;
end; end;
Result:=Identifier; Result:=Identifier;
@ -5648,7 +5701,6 @@ end;
procedure TPasResolver.FinishTypeDef(El: TPasType); procedure TPasResolver.FinishTypeDef(El: TPasType);
var var
C: TClass; C: TClass;
aType: TPasType;
begin begin
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
//writeln('TPasResolver.FinishTypeDef El=',GetObjName(El)); //writeln('TPasResolver.FinishTypeDef El=',GetObjName(El));
@ -5671,12 +5723,7 @@ begin
else if C=TPasArrayType then else if C=TPasArrayType then
FinishArrayType(TPasArrayType(El)) FinishArrayType(TPasArrayType(El))
else if (C=TPasAliasType) or (C=TPasTypeAliasType) then else if (C=TPasAliasType) or (C=TPasTypeAliasType) then
begin FinishAliasType(TPasAliasType(El))
aType:=ResolveAliasType(El);
if (aType is TPasMembersType) and (aType.CustomData=nil) then
exit;
EmitTypeHints(El,TPasAliasType(El).DestType);
end
else if (C=TPasPointerType) then else if (C=TPasPointerType) then
EmitTypeHints(El,TPasPointerType(El).DestType) EmitTypeHints(El,TPasPointerType(El).DestType)
else if C=TPasGenericTemplateType then else if C=TPasGenericTemplateType then
@ -6149,47 +6196,18 @@ begin
PopScope; PopScope;
end; end;
procedure TPasResolver.FinishGenericTemplateTypes(aType: TPasGenericType); procedure TPasResolver.FinishAliasType(El: TPasAliasType);
var var
C: TClass; aType: TPasType;
GenTemplates: TFPList;
TemplType: TPasGenericTemplateType;
i: Integer;
ClassHeaderScope: TPasClassHeaderScope;
begin begin
{$IFDEF VerbosePasResolver} aType:=ResolveAliasType(El);
writeln('TPasResolver.FinishGenericTemplateTypes ',GetObjName(aType)); if (aType is TPasMembersType) and (aType.CustomData=nil) then
{$ENDIF} exit;
GenTemplates:=aType.GenericTemplateTypes; if (aType is TPasGenericType)
if (GenTemplates=nil) or (GenTemplates.Count=0) then and (GetTypeParameterCount(TPasGenericType(aType))>0) then
RaiseNotYetImplemented(20190726184902,aType,'emty generic template list'); RaiseMsg(20190818135830,nXExpectedButYFound,sXExpectedButYFound,
['type',GetTypeDescription(aType)],El);
// template names must differ from generic type name EmitTypeHints(El,TPasAliasType(El).DestType);
for i:=0 to GenTemplates.Count-1 do
begin
TemplType:=TPasGenericTemplateType(GenTemplates[i]);
if SameText(TemplType.Name,aType.Name) then
RaiseMsg(20190801101444,nDuplicateIdentifier,sDuplicateIdentifier,[
TemplType.Name,GetElementSourcePosStr(aType)],TemplType);
end;
// add template names to scope
C:=aType.ClassType;
if C=TPasRecordType then
else if C=TPasClassType then
begin
// Note: TPasClassType.Forward is not yet set!
// create class header scope
TemplType:=TPasGenericTemplateType(GenTemplates[0]);
ClassHeaderScope:=TPasClassHeaderScope(PushScope(TemplType,TPasClassHeaderScope));
ClassHeaderScope.GenericType:=aType;
AddGenericTemplateIdentifiers(GenTemplates,ClassHeaderScope);
end
else if C=TPasArrayType then
else if (C=TPasProcedureType)
or (C=TPasFunctionType) then
else
RaiseNotYetImplemented(20190726150359,aType,GetObjName(aType));
end; end;
procedure TPasResolver.FinishGenericTemplateType(El: TPasGenericTemplateType); procedure TPasResolver.FinishGenericTemplateType(El: TPasGenericTemplateType);
@ -6365,19 +6383,6 @@ begin
RaiseXExpectedButYFound(20171004135753,'string',GetTypeDescription(ResolvedEl),El.Expr); RaiseXExpectedButYFound(20171004135753,'string',GetTypeDescription(ResolvedEl),El.Expr);
end; end;
procedure TPasResolver.FinishProcNameParts(aProc: TPasProcedure);
var
i, j: Integer;
begin
for i:=0 to length(aProc.NameParts)-1 do
with aProc.NameParts[i] do
begin
if Templates<>nil then
for j:=0 to Templates.Count-1 do
AddType(TPasGenericTemplateType(Templates[j]));
end;
end;
procedure TPasResolver.FinishProcedure(aProc: TPasProcedure); procedure TPasResolver.FinishProcedure(aProc: TPasProcedure);
var var
i: Integer; i: Integer;
@ -11029,6 +11034,26 @@ begin
end; end;
end; end;
procedure TPasResolver.CheckGenericTemplateTypes(El: TPasGenericType);
var
GenTemplates: TFPList;
i: Integer;
TemplType: TPasGenericTemplateType;
begin
GenTemplates:=El.GenericTemplateTypes;
if (GenTemplates=nil) or (GenTemplates.Count=0) then
RaiseNotYetImplemented(20190726184902,El,'emty generic template list');
// template names must differ from generic type name
for i:=0 to GenTemplates.Count-1 do
begin
TemplType:=TPasGenericTemplateType(GenTemplates[i]);
if SameText(TemplType.Name,El.Name) then
RaiseMsg(20190801101444,nDuplicateIdentifier,sDuplicateIdentifier,[
TemplType.Name,GetElementSourcePosStr(El)],TemplType);
end;
end;
procedure TPasResolver.ComputeUnaryNot(El: TUnaryExpr; procedure TPasResolver.ComputeUnaryNot(El: TUnaryExpr;
var ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags); var ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags);
begin begin
@ -11101,16 +11126,23 @@ begin
if El.Name<>'' then begin if El.Name<>'' then begin
if not (TopScope is TPasIdentifierScope) then if not (TopScope is TPasIdentifierScope) then
RaiseInvalidScopeForElement(20190812215622,El); RaiseInvalidScopeForElement(20190812215622,El);
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
end; if TypeParams<>nil then
begin
El.SetGenericTemplates(TypeParams);
TypeParams:=El.GenericTemplateTypes;
CheckGenericTemplateTypes(El);
end;
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
if TypeParams<>nil then if TypeParams<>nil then
begin begin
// generic array
if El.Name='' then
RaiseNotYetImplemented(20190812215851,El);
Scope:=TPasArrayScope(PushScope(El,TPasArrayScope)); Scope:=TPasArrayScope(PushScope(El,TPasArrayScope));
AddGenericTemplateIdentifiers(TypeParams,Scope); AddGenericTemplateIdentifiers(TypeParams,Scope);
end; end;
end else if TypeParams<>nil then
RaiseNotYetImplemented(20190812215851,El);
end; end;
procedure TPasResolver.AddRecordType(El: TPasRecordType; TypeParams: TFPList); procedure TPasResolver.AddRecordType(El: TPasRecordType; TypeParams: TFPList);
@ -11122,6 +11154,14 @@ begin
{$ENDIF} {$ENDIF}
if not (TopScope is TPasIdentifierScope) then if not (TopScope is TPasIdentifierScope) then
RaiseInvalidScopeForElement(20160922163508,El); RaiseInvalidScopeForElement(20160922163508,El);
if TypeParams<>nil then
begin
El.SetGenericTemplates(TypeParams);
TypeParams:=El.GenericTemplateTypes;
CheckGenericTemplateTypes(El);
end;
if El.Name<>'' then begin if El.Name<>'' then begin
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple); AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
@ -11154,11 +11194,12 @@ var
GenTemplCnt, i, j: Integer; GenTemplCnt, i, j: Integer;
DuplEl: TPasElement; DuplEl: TPasElement;
ClassScope: TPasClassScope; ClassScope: TPasClassScope;
ForwGenTempl, ActGenTempl: TPasGenericTemplateType; ForwGenTempl, ActGenTempl, TemplType: TPasGenericTemplateType;
ForwConstraints, ActConstraints: TPasExprArray; ForwConstraints, ActConstraints: TPasExprArray;
ForwExpr, ActExpr: TPasExpr; ForwExpr, ActExpr: TPasExpr;
ForwToken, ActToken: TToken; ForwToken, ActToken: TToken;
ForwConstraintResolved, ActConstraintResolved: TPasResolverResult; ForwConstraintResolved, ActConstraintResolved: TPasResolverResult;
ClassHeaderScope: TPasClassHeaderScope;
begin begin
// Beware: El.ObjKind is not yet set! // Beware: El.ObjKind is not yet set!
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
@ -11166,6 +11207,15 @@ begin
{$ENDIF} {$ENDIF}
if not (TopScope is TPasIdentifierScope) then if not (TopScope is TPasIdentifierScope) then
RaiseInvalidScopeForElement(20160922163510,El); RaiseInvalidScopeForElement(20160922163510,El);
if TypeParams=nil then
GenTemplCnt:=0
else
begin
GenTemplCnt:=TypeParams.Count;
El.SetGenericTemplates(TypeParams);
TypeParams:=El.GenericTemplateTypes;
CheckGenericTemplateTypes(El);
end;
CurScope:=TPasIdentifierScope(TopScope); CurScope:=TPasIdentifierScope(TopScope);
if CurScope is TPasGroupScope then if CurScope is TPasGroupScope then
@ -11173,10 +11223,6 @@ begin
else else
LocalScope:=CurScope; LocalScope:=CurScope;
Duplicate:=LocalScope.FindLocalIdentifier(El.Name); Duplicate:=LocalScope.FindLocalIdentifier(El.Name);
if TypeParams=nil then
GenTemplCnt:=0
else
GenTemplCnt:=TypeParams.Count;
while Duplicate<>nil do while Duplicate<>nil do
begin begin
DuplEl:=Duplicate.Element; DuplEl:=Duplicate.Element;
@ -11254,6 +11300,15 @@ begin
else else
AddIdentifier(CurScope,El.Name,El,pikSimple); AddIdentifier(CurScope,El.Name,El,pikSimple);
if TypeParams<>nil then
begin
// Parsing the ancestor+interface list requires the type params.
TemplType:=TPasGenericTemplateType(TypeParams[0]);
ClassHeaderScope:=TPasClassHeaderScope(PushScope(TemplType,TPasClassHeaderScope));
ClassHeaderScope.GenericType:=El;
AddGenericTemplateIdentifiers(TypeParams,ClassHeaderScope);
end;
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
if FPendingForwardProcs.IndexOf(El)>=0 then if FPendingForwardProcs.IndexOf(El)>=0 then
RaiseNotYetImplemented(20190804114746,El); RaiseNotYetImplemented(20190804114746,El);
@ -11390,19 +11445,26 @@ begin
{$ENDIF} {$ENDIF}
if not (TopScope is TPasIdentifierScope) then if not (TopScope is TPasIdentifierScope) then
RaiseInvalidScopeForElement(20190813193703,El); RaiseInvalidScopeForElement(20190813193703,El);
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
end; if TypeParams<>nil then
begin
El.SetGenericTemplates(TypeParams);
TypeParams:=El.GenericTemplateTypes;
CheckGenericTemplateTypes(El);
end;
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
if TypeParams<>nil then if TypeParams<>nil then
begin begin
// generic procedure type
if El.Name='' then
RaiseNotYetImplemented(20190813193745,El);
Scope:=TPasProcTypeScope(PushScope(El,TPasProcTypeScope)); Scope:=TPasProcTypeScope(PushScope(El,TPasProcTypeScope));
AddGenericTemplateIdentifiers(TypeParams,Scope); AddGenericTemplateIdentifiers(TypeParams,Scope);
end; end;
end else if TypeParams<>nil then
RaiseNotYetImplemented(20190813193745,El);
end; end;
procedure TPasResolver.AddProcedure(El: TPasProcedure); procedure TPasResolver.AddProcedure(El: TPasProcedure; TypeParams: TFPList);
procedure AddClassConDestructor(ClassOrRecordScope: TPasClassOrRecordScope; procedure AddClassConDestructor(ClassOrRecordScope: TPasClassOrRecordScope;
var Field: TPasProcedure); var Field: TPasProcedure);
@ -11415,23 +11477,95 @@ procedure TPasResolver.AddProcedure(El: TPasProcedure);
Field:=El; Field:=El;
end; end;
function FindBestMembersType(const ClassOrRecName: string;
TypeParamCnt: integer; Scope: TPasIdentifierScope;
var Best: TPasMembersType; ErrorPos: TPasElement): integer;
// returns number of candidates
var
Identifier: TPasIdentifier;
CurEl: TPasElement;
begin
Result:=0;
Identifier:=Scope.FindLocalIdentifier(ClassOrRecName);
while Identifier<>nil do
begin
CurEl:=Identifier.Element;
if not (CurEl is TPasMembersType) then
RaiseXExpectedButYFound(20170216152557,
'class',CurEl.Name+':'+GetElementTypeName(CurEl),ErrorPos);
inc(Result);
if Best=nil then
Best:=TPasMembersType(CurEl);
if GetTypeParameterCount(TPasGenericType(CurEl))=TypeParamCnt then
begin
// fits
Best:=TPasMembersType(CurEl);
exit;
end;
Identifier:=Identifier.NextSameIdentifier;
end;
end;
function FindMembersType(Scope: TPasIdentifierScope;
const ClassOrRecName: string; TypeParamCnt: integer; IsDelphi: boolean;
ErrorPos: TPasElement): TPasMembersType;
var
Found: integer;
begin
Result:=nil;
if Scope<>nil then
Found:=FindBestMembersType(ClassOrRecName,TypeParamCnt,Scope,Result,ErrorPos)
else if TopScope is TPasIdentifierScope then
begin
Found:=FindBestMembersType(ClassOrRecName,TypeParamCnt,
TPasIdentifierScope(TopScope),Result,ErrorPos);
if (Result=nil) or (TypeParamCnt<>GetTypeParameterCount(Result)) then
begin
if (TopScope is TPasSectionScope)
and (ScopeCount>1) and (Scopes[ScopeCount-2] is TPasSectionScope) then
// search in unit interface too
Found:=Found+FindBestMembersType(ClassOrRecName,TypeParamCnt,
TPasIdentifierScope(Scopes[ScopeCount-2]),Result,ErrorPos);
end;
end;
if Result=nil then
RaiseMsg(20190818112356,nClassXNotFoundInThisModule,sClassXNotFoundInThisModule,
[ClassOrRecName],ErrorPos);
if TypeParamCnt=GetTypeParameterCount(Result) then
exit; // fits perfectly
if (not IsDelphi) and (TypeParamCnt=0) and (Found=1) then
exit; // in objfpc type params can be omitted if there is only one type
// found one or more, but type param count do not fit
RaiseMsg(20190818112856,nXExpectedButYFound,sXExpectedButYFound,
[Result.Name+GetTypeParamCommas(GetTypeParameterCount(Result)),
ClassOrRecName+GetTypeParamCommas(TypeParamCnt)],ErrorPos);
end;
var var
ProcName, aClassName: String; ProcName, aClassName: String;
p: SizeInt; p: SizeInt;
ClassOrRecType: TPasMembersType; ClassOrRecType: TPasMembersType;
ProcScope: TPasProcedureScope; ProcScope: TPasProcedureScope;
HasDot, IsClassConDestructor: Boolean; HasDot, IsClassConDestructor, IsDelphi: Boolean;
CurEl: TPasElement;
Identifier: TPasIdentifier;
ClassOrRecScope: TPasClassOrRecordScope; ClassOrRecScope: TPasClassOrRecordScope;
C: TClass; C: TClass;
CurScope: TPasScope; CurScope: TPasScope;
LocalScope: TPasScope; LocalScope: TPasScope;
Level, TypeParamCount, i: Integer;
TypeParam: TProcedureNamePart;
TemplType: TPasGenericTemplateType;
begin begin
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
writeln('TPasResolver.AddProcedure ',GetObjName(El)); writeln('TPasResolver.AddProcedure ',GetObjName(El));
{$ENDIF} {$ENDIF}
if TypeParams<>nil then
begin
// move type param elements to El
El.SetNameParts(TypeParams);
TypeParams:=El.NameParts;
end;
CurScope:=TopScope; CurScope:=TopScope;
if CurScope.ClassType=TPasGroupScope then if CurScope.ClassType=TPasGroupScope then
LocalScope:=TPasGroupScope(CurScope).Scopes[0] LocalScope:=TPasGroupScope(CurScope).Scopes[0]
@ -11448,6 +11582,8 @@ begin
else else
begin begin
// anonymous proc // anonymous proc
if TypeParams<>nil then
RaiseNotYetImplemented(20190818101856,El);
C:=LocalScope.ClassType; C:=LocalScope.ClassType;
if (C=ScopeClass_InitialFinalization) if (C=ScopeClass_InitialFinalization)
or C.InheritsFrom(TPasProcedureScope) or C.InheritsFrom(TPasProcedureScope)
@ -11463,6 +11599,10 @@ begin
// Note: El.ProcType is nil ! It is parsed later. // Note: El.ProcType is nil ! It is parsed later.
HasDot:=Pos('.',ProcName)>1; HasDot:=Pos('.',ProcName)>1;
if (TypeParams<>nil) then
if HasDot<>(TypeParams.Count>1) then
RaiseNotYetImplemented(20190818093923,El);
if El.CustomData is TPasProcedureScope then if El.CustomData is TPasProcedureScope then
begin begin
// adding a specialized implementation proc // adding a specialized implementation proc
@ -11480,6 +11620,7 @@ begin
end end
else else
begin begin
IsDelphi:=msDelphi in CurrentParser.CurrentModeswitches;
IsClassConDestructor:=(El.ClassType=TPasClassConstructor) IsClassConDestructor:=(El.ClassType=TPasClassConstructor)
or (El.ClassType=TPasClassDestructor); or (El.ClassType=TPasClassDestructor);
if (not HasDot) and IsClassConDestructor then if (not HasDot) and IsClassConDestructor then
@ -11493,6 +11634,9 @@ begin
AddClassConDestructor(ClassOrRecScope,TPasProcedure(ClassOrRecScope.ClassConstructor)) AddClassConDestructor(ClassOrRecScope,TPasProcedure(ClassOrRecScope.ClassConstructor))
else else
AddClassConDestructor(ClassOrRecScope,TPasProcedure(ClassOrRecScope.ClassDestructor)); AddClassConDestructor(ClassOrRecScope,TPasProcedure(ClassOrRecScope.ClassDestructor));
if TypeParams<>nil then
RaiseMsg(20190818094753,nTypeParamsNotAllowedOnX,sTypeParamsNotAllowedOnX,
[El.ElementTypeName],El);
end; end;
if (not HasDot) and (ProcName<>'') if (not HasDot) and (ProcName<>'')
@ -11503,7 +11647,7 @@ begin
AddIdentifier(TPasIdentifierScope(CurScope),ProcName,El,pikProc); AddIdentifier(TPasIdentifierScope(CurScope),ProcName,El,pikProc);
end; end;
ProcScope:=TPasProcedureScope(PushScope(El,FScopeClass_Proc)); ProcScope:=TPasProcedureScope(CreateScope(El,FScopeClass_Proc));
ProcScope.ModeSwitches:=CurrentParser.CurrentModeswitches; ProcScope.ModeSwitches:=CurrentParser.CurrentModeswitches;
if HasDot then if HasDot then
begin begin
@ -11512,7 +11656,9 @@ begin
writeln('TPasResolver.AddProcedure searching class of "',ProcName,'" ...'); writeln('TPasResolver.AddProcedure searching class of "',ProcName,'" ...');
{$ENDIF} {$ENDIF}
ClassOrRecType:=nil; ClassOrRecType:=nil;
Level:=0;
repeat repeat
inc(Level);
p:=Pos('.',ProcName); p:=Pos('.',ProcName);
if p<1 then if p<1 then
begin begin
@ -11522,8 +11668,29 @@ begin
end; end;
aClassName:=LeftStr(ProcName,p-1); aClassName:=LeftStr(ProcName,p-1);
Delete(ProcName,1,p); Delete(ProcName,1,p);
TypeParamCount:=0;
if TypeParams<>nil then
begin
// e.g. aclassname<T>.
if Level>TypeParams.Count then
RaiseNotYetImplemented(20190818122217,El);
TypeParam:=TProcedureNamePart(TypeParams[Level-1]);
if TypeParam.Name<>aClassName then
RaiseNotYetImplemented(20190818102541,El,IntToStr(Level)+': '+TypeParam.Name+'<>'+aClassName);
if TypeParam.Templates<>nil then
begin
TypeParamCount:=TypeParam.Templates.Count;
for i:=0 to TypeParamCount-1 do
begin
TemplType:=TPasGenericTemplateType(TypeParam.Templates[i]);
if length(TemplType.Constraints)>0 then
RaiseMsg(20190818102850,nXCannotHaveParameters,sXCannotHaveParameters,
[TemplType.Name],TemplType.Constraints[0]);
end;
end;
end;
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
writeln('TPasResolver.AddProcedure searching class "',aClassName,'" ProcName="',ProcName,'" ...'); writeln('TPasResolver.AddProcedure searching class "',aClassName,GetTypeParamCommas(TypeParamCount),'" ProcName="',ProcName,'" ...');
{$ENDIF} {$ENDIF}
if not IsValidIdent(aClassName) then if not IsValidIdent(aClassName) then
RaiseNotYetImplemented(20161013170844,El); RaiseNotYetImplemented(20161013170844,El);
@ -11531,41 +11698,25 @@ begin
if ClassOrRecType<>nil then if ClassOrRecType<>nil then
begin begin
ClassOrRecScope:=TPasClassOrRecordScope(ClassOrRecType.CustomData); ClassOrRecScope:=TPasClassOrRecordScope(ClassOrRecType.CustomData);
Identifier:=ClassOrRecScope.FindLocalIdentifier(aClassName); ClassOrRecType:=FindMembersType(ClassOrRecScope,aClassName,
if Identifier=nil then TypeParamCount,IsDelphi,El);
RaiseIdentifierNotFound(20180430130635,aClassName,El)
else
CurEl:=Identifier.Element;
end end
else else
CurEl:=FindElementWithoutParams(aClassName,El,false); ClassOrRecType:=FindMembersType(nil,aClassName,
TypeParamCount,IsDelphi,El);
if not (CurEl is TPasMembersType) then
begin
aClassName:=LeftStr(El.Name,length(El.Name)-length(ProcName)-1);
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.AddProcedure searching class "',aClassName,'" ProcName="',ProcName,'" found: '+GetObjName(CurEl));
{$ENDIF}
RaiseXExpectedButYFound(20170216152557,
'class',aClassname+':'+GetElementTypeName(CurEl),El);
end;
ClassOrRecType:=TPasMembersType(CurEl);
if ClassOrRecType is TPasClassType then if ClassOrRecType is TPasClassType then
begin begin
if not (TPasClassType(ClassOrRecType).ObjKind in if not (TPasClassType(ClassOrRecType).ObjKind in
([okClass]+okAllHelpers)) then ([okClass]+okAllHelpers)) then
begin begin
aClassName:=LeftStr(El.Name,length(El.Name)-length(ProcName)-1); aClassName:=LeftStr(El.Name,length(El.Name)-length(ProcName)-1);
RaiseXExpectedButYFound(20180321161722, RaiseXExpectedButYFound(20180321161722,'class',
'class',aClassname+':'+GetElementTypeName(CurEl),El); aClassname+GetTypeParamCommas(GetTypeParameterCount(ClassOrRecType))+':'+GetElementTypeName(ClassOrRecType),El);
end end
end; end;
if ClassOrRecType.GetModule<>El.GetModule then if ClassOrRecType.GetModule<>El.GetModule then
begin RaiseNotYetImplemented(20190818120051,El);
aClassName:=LeftStr(El.Name,length(El.Name)-length(ProcName)-1);
RaiseMsg(20180211230432,nMethodClassXInOtherUnitY,sMethodClassXInOtherUnitY,
[aClassName,ClassOrRecType.GetModule.Name],El);
end;
until false; until false;
if not IsValidIdent(ProcName) then if not IsValidIdent(ProcName) then
@ -11573,8 +11724,30 @@ begin
ProcScope.VisibilityContext:=ClassOrRecType; ProcScope.VisibilityContext:=ClassOrRecType;
ProcScope.ClassRecScope:=NoNil(ClassOrRecType.CustomData) as TPasClassOrRecordScope; ProcScope.ClassRecScope:=NoNil(ClassOrRecType.CustomData) as TPasClassOrRecordScope;
end; // HasDot=true
if TypeParams<>nil then
begin
if Level<>TypeParams.Count then
RaiseNotYetImplemented(20190818122315,El);
TypeParam:=TProcedureNamePart(TypeParams[Level-1]);
if TypeParam.Name<>ProcName then
RaiseNotYetImplemented(20190818122551,El,IntToStr(Level)+': '+TypeParam.Name+'<>'+ProcName);
if TypeParam.Templates<>nil then
begin
// ToDo: generic method
RaiseNotYetImplemented(20190818122619,El);
end; end;
end;
end
else
begin
// HasDot=false
if TypeParams<>nil then
RaiseNotYetImplemented(20190818095452,El);
end;
PushScope(ProcScope);
end;// source proc, not specialized
if HasDot then if HasDot then
begin begin
@ -14577,7 +14750,7 @@ begin
if Item=nil then if Item=nil then
begin begin
// new specialization // new specialization
SrcModule:=El.GetModule; SrcModule:=GenericType.GetModule;
SrcModuleScope:=SrcModule.CustomData as TPasModuleScope; SrcModuleScope:=SrcModule.CustomData as TPasModuleScope;
SrcResolver:=SrcModuleScope.Owner as TPasResolver; SrcResolver:=SrcModuleScope.Owner as TPasResolver;
Item:=SrcResolver.CreateSpecializedType(El,ParamsResolved); Item:=SrcResolver.CreateSpecializedType(El,ParamsResolved);
@ -15242,6 +15415,8 @@ begin
AddType(TPasSpecializeType(SpecEl)); AddType(TPasSpecializeType(SpecEl));
SpecializeSpecializeType(TPasSpecializeType(GenEl),TPasSpecializeType(SpecEl)); SpecializeSpecializeType(TPasSpecializeType(GenEl),TPasSpecializeType(SpecEl));
end end
else if C=TPasGenericTemplateType then
SpecializeGenericTemplateType(TPasGenericTemplateType(GenEl),TPasGenericTemplateType(SpecEl))
// empty statement // empty statement
else if C=TPasImplCommand then else if C=TPasImplCommand then
// TPasImplBlock // TPasImplBlock
@ -15320,12 +15495,12 @@ begin
end end
else if C=TPasOperator then else if C=TPasOperator then
begin begin
AddProcedure(TPasOperator(SpecEl)); AddProcedure(TPasOperator(SpecEl),nil);
SpecializeOperator(TPasOperator(GenEl),TPasOperator(SpecEl)); SpecializeOperator(TPasOperator(GenEl),TPasOperator(SpecEl));
end end
else if C.InheritsFrom(TPasProcedure) then else if C.InheritsFrom(TPasProcedure) then
begin begin
AddProcedure(TPasProcedure(SpecEl)); AddProcedure(TPasProcedure(SpecEl),nil);
SpecializeProcedure(TPasProcedure(GenEl),TPasProcedure(SpecEl)); SpecializeProcedure(TPasProcedure(GenEl),TPasProcedure(SpecEl));
end end
else if C.InheritsFrom(TPasProcedureType) then else if C.InheritsFrom(TPasProcedureType) then
@ -15507,6 +15682,9 @@ var
NewClass: TPTreeElement; NewClass: TPTreeElement;
SpecProcScope: TPasProcedureScope; SpecProcScope: TPasProcedureScope;
GenBody: TProcedureBody; GenBody: TProcedureBody;
i, j: Integer;
GenPart, SpecPart: TProcedureNamePart;
GenTempl, SpecTempl: TPasGenericTemplateType;
begin begin
SpecProcScope:=SpecEl.CustomData as TPasProcedureScope; SpecProcScope:=SpecEl.CustomData as TPasProcedureScope;
Include(SpecProcScope.Flags,ppsfIsSpecialized); Include(SpecProcScope.Flags,ppsfIsSpecialized);
@ -15525,6 +15703,33 @@ begin
SpecEl.MessageType:=GenEl.MessageType; SpecEl.MessageType:=GenEl.MessageType;
SpecEl.AliasName:=GenEl.AliasName; SpecEl.AliasName:=GenEl.AliasName;
SpecEl.Modifiers:=GenEl.Modifiers; SpecEl.Modifiers:=GenEl.Modifiers;
if GenEl.NameParts<>nil then
begin
if SpecEl.NameParts<>nil then
RaiseNotYetImplemented(20190818125620,SpecEl);
SpecEl.NameParts:=TFPList.Create;
for i:=0 to GenEl.NameParts.Count-1 do
begin
GenPart:=TProcedureNamePart(GenEl.NameParts[i]);
SpecPart:=TProcedureNamePart.Create;
SpecEl.NameParts.Add(SpecPart);
SpecPart.Name:=GenPart.Name;
if GenPart.Templates<>nil then
begin
SpecPart.Templates:=TFPList.Create;
for j:=0 to GenPart.Templates.Count-1 do
begin
GenTempl:=TPasGenericTemplateType(GenPart.Templates[j]);
if GenTempl.Parent<>GenEl then
RaiseNotYetImplemented(20190818130001,GenEl,IntToStr(i)+','+IntToStr(j)+':'+GenTempl.Name+' GenParent='+GetObjName(GenTempl.Parent)+' GenEl='+GetObjName(GenEl));
NewClass:=TPTreeElement(GenTempl.ClassType);
SpecTempl:=TPasGenericTemplateType(NewClass.Create(GenTempl.Name,SpecEl));
SpecPart.Templates.Add(SpecTempl);
SpecializeElement(GenTempl,SpecTempl);
end;
end;
end;
end;
if GenEl.ProcType<>nil then if GenEl.ProcType<>nil then
begin begin
GenProcType:=GenEl.ProcType; GenProcType:=GenEl.ProcType;
@ -15544,8 +15749,6 @@ begin
SpecializeElement(GenBody,SpecEl.Body); SpecializeElement(GenBody,SpecEl.Body);
end; end;
if length(GenEl.NameParts)>0 then RaiseNotYetImplemented(20190803215418,GenEl);
FinishProcedure(SpecEl); FinishProcedure(SpecEl);
end; end;
@ -15695,6 +15898,12 @@ begin
{$ENDIF} {$ENDIF}
end; end;
procedure TPasResolver.SpecializeGenericTemplateType(GenEl,
SpecEl: TPasGenericTemplateType);
begin
SpecializeExprArray(GenEl,SpecEl,GenEl.Constraints,SpecEl.Constraints);
end;
procedure TPasResolver.SpecializeArgument(GenEl, SpecEl: TPasArgument); procedure TPasResolver.SpecializeArgument(GenEl, SpecEl: TPasArgument);
begin begin
SpecEl.Access:=GenEl.Access; SpecEl.Access:=GenEl.Access;
@ -18420,7 +18629,7 @@ begin
AddClassType(TPasClassType(El),TypeParams) AddClassType(TPasClassType(El),TypeParams)
else if AClass=TPasVariant then else if AClass=TPasVariant then
else if AClass.InheritsFrom(TPasProcedure) then else if AClass.InheritsFrom(TPasProcedure) then
AddProcedure(TPasProcedure(El)) AddProcedure(TPasProcedure(El),TypeParams)
else if AClass=TPasResultElement then else if AClass=TPasResultElement then
AddFunctionResult(TPasResultElement(El)) AddFunctionResult(TPasResultElement(El))
else if AClass=TProcedureBody then else if AClass=TProcedureBody then
@ -19169,11 +19378,6 @@ begin
stTypeSection: FinishTypeSection(El); stTypeSection: FinishTypeSection(El);
stTypeDef: FinishTypeDef(El as TPasType); stTypeDef: FinishTypeDef(El as TPasType);
stResourceString: FinishResourcestring(El as TPasResString); stResourceString: FinishResourcestring(El as TPasResString);
stGenericTypeTemplates:
if El is TPasGenericType then
FinishGenericTemplateTypes(TPasGenericType(El))
else
FinishProcNameParts(El as TPasProcedure);
stProcedure: FinishProcedure(El as TPasProcedure); stProcedure: FinishProcedure(El as TPasProcedure);
stProcedureHeader: FinishProcedureType(El as TPasProcedureType); stProcedureHeader: FinishProcedureType(El as TPasProcedureType);
stExceptOnExpr: FinishExceptOnExpr; stExceptOnExpr: FinishExceptOnExpr;
@ -22689,7 +22893,9 @@ function TPasResolver.GetTypeDescription(aType: TPasType; AddPath: boolean): str
end end
else else
Result:=GetElementTypeName(aType); Result:=GetElementTypeName(aType);
end; end
else if aType is TPasGenericType then
Result:=Result+GetTypeParamCommas(GetTypeParameterCount(TPasGenericType(aType)));
if AddPath then if AddPath then
begin begin
s:=aType.ParentPath; s:=aType.ParentPath;

View File

@ -1049,11 +1049,11 @@ type
{ TProcedureNamePart } { TProcedureNamePart }
TProcedureNamePart = record TProcedureNamePart = class
Name: string; Name: string;
Templates: TFPList; // optional list of TPasGenericTemplateType, can nil! Templates: TFPList; // optional list of TPasGenericTemplateType, can be nil!
end; end;
TProcedureNameParts = array of TProcedureNamePart; TProcedureNameParts = TFPList;
TProcedureBody = class; TProcedureBody = class;
@ -1097,7 +1097,7 @@ type
Function IsStatic : Boolean; Function IsStatic : Boolean;
Function IsForward: Boolean; Function IsForward: Boolean;
Function GetProcTypeEnum: TProcType; virtual; Function GetProcTypeEnum: TProcType; virtual;
procedure SetNameParts(var Parts: TProcedureNameParts); procedure SetNameParts(Parts: TProcedureNameParts);
Property Modifiers : TProcedureModifiers Read FModifiers Write FModifiers; Property Modifiers : TProcedureModifiers Read FModifiers Write FModifiers;
Property CallingConvention : TCallingConvention Read GetCallingConvention Write SetCallingConvention; Property CallingConvention : TCallingConvention Read GetCallingConvention Write SetCallingConvention;
Property MessageName : String Read FMessageName Write FMessageName; Property MessageName : String Read FMessageName Write FMessageName;
@ -1809,21 +1809,27 @@ procedure ReleaseProcNameParts(var NameParts: TProcedureNameParts);
var var
El: TPasElement; El: TPasElement;
i, j: Integer; i, j: Integer;
Part: TProcedureNamePart;
begin begin
for i := 0 to length(NameParts)-1 do if NameParts=nil then exit;
for i := NameParts.Count-1 downto 0 do
begin begin
with NameParts[i] do Part:=TProcedureNamePart(NameParts[i]);
if Templates<>nil then if Part.Templates<>nil then
begin begin
for j:=0 to Templates.Count-1 do for j:=0 to Part.Templates.Count-1 do
begin begin
El:=TPasGenericTemplateType(Templates[j]); El:=TPasGenericTemplateType(Part.Templates[j]);
El.Parent:=nil; El.Parent:=nil;
El.Release{$IFDEF CheckPasTreeRefCount}('TPasProcedure.NameParts'){$ENDIF}; El.Release{$IFDEF CheckPasTreeRefCount}('TPasProcedure.NameParts'){$ENDIF};
end; end;
Templates.Free; Part.Templates.Free;
Part.Templates:=nil;
end; end;
NameParts.Delete(i);
Part.Free;
end; end;
NameParts.Free;
NameParts:=nil; NameParts:=nil;
end; end;
@ -4664,8 +4670,9 @@ var
i, j: Integer; i, j: Integer;
begin begin
inherited ForEachCall(aMethodCall, Arg); inherited ForEachCall(aMethodCall, Arg);
for i:=0 to length(NameParts)-1 do if NameParts<>nil then
with NameParts[i] do for i:=0 to NameParts.Count-1 do
with TProcedureNamePart(NameParts[i]) do
if Templates<>nil then if Templates<>nil then
for j:=0 to Templates.Count-1 do for j:=0 to Templates.Count-1 do
ForEachChildCall(aMethodCall,Arg,TPasElement(Templates[i]),false); ForEachChildCall(aMethodCall,Arg,TPasElement(Templates[i]),false);
@ -4743,17 +4750,18 @@ begin
Result:=ptProcedure; Result:=ptProcedure;
end; end;
procedure TPasProcedure.SetNameParts(var Parts: TProcedureNameParts); procedure TPasProcedure.SetNameParts(Parts: TProcedureNameParts);
var var
i, j: Integer; i, j: Integer;
El: TPasElement; El: TPasElement;
begin begin
if length(NameParts)>0 then if NameParts<>nil then
ReleaseProcNameParts(NameParts); ReleaseProcNameParts(NameParts);
NameParts:=Parts; NameParts:=TFPList.Create;
Parts:=nil; NameParts.Assign(Parts);
for i:=0 to length(NameParts)-1 do Parts.Clear;
with NameParts[i] do for i:=0 to NameParts.Count-1 do
with TProcedureNamePart(NameParts[i]) do
if Templates<>nil then if Templates<>nil then
for j:=0 to Templates.Count-1 do for j:=0 to Templates.Count-1 do
begin begin
@ -4773,14 +4781,14 @@ begin
If Full then If Full then
begin begin
T:=TypeName; T:=TypeName;
if length(NameParts)>0 then if NameParts<>nil then
begin begin
T:=T+' '; T:=T+' ';
for i:=0 to length(NameParts)-1 do for i:=0 to NameParts.Count-1 do
begin begin
if i>0 then if i>0 then
T:=T+'.'; T:=T+'.';
with NameParts[i] do with TProcedureNamePart(NameParts[i]) do
begin begin
T:=T+Name; T:=T+Name;
if Templates<>nil then if Templates<>nil then

View File

@ -169,7 +169,6 @@ type
stTypeSection, stTypeSection,
stTypeDef, // e.g. a TPasType stTypeDef, // e.g. a TPasType
stResourceString, // e.g. TPasResString stResourceString, // e.g. TPasResString
stGenericTypeTemplates, // called after TPasGenericType.SetGenericTemplates or TPasProcedure.setNameParts
stProcedure, // also method, procedure, constructor, destructor, ... stProcedure, // also method, procedure, constructor, destructor, ...
stProcedureHeader, stProcedureHeader,
stWithExpr, // calls BeginScope after parsing every WITH-expression stWithExpr, // calls BeginScope after parsing every WITH-expression
@ -4306,8 +4305,12 @@ function TPasParser.ParseGenericTypeDecl(Parent: TPasElement;
{$IFDEF CheckPasTreeRefCount}NewEl.ChangeRefId('CreateElement','TPasMembersType.Members');{$ENDIF} {$IFDEF CheckPasTreeRefCount}NewEl.ChangeRefId('CreateElement','TPasMembersType.Members');{$ENDIF}
end; end;
end; end;
if GenericTemplateTypes.Count>0 then
begin
// Note: TPasResolver sets GenericTemplateTypes already in CreateElement
// This is for other tools like fpdoc.
NewEl.SetGenericTemplates(GenericTemplateTypes); NewEl.SetGenericTemplates(GenericTemplateTypes);
Engine.FinishScope(stGenericTypeTemplates,NewEl); end;
end; end;
var var
@ -4392,7 +4395,8 @@ begin
begin begin
if CurToken=tkFunction then if CurToken=tkFunction then
begin begin
ProcTypeEl := CreateFunctionType(TypeName, 'Result', Parent, False, NamePos, TypeParams); ProcTypeEl := CreateFunctionType(TypeName, 'Result', Parent, False,
NamePos, TypeParams);
ProcType:=ptFunction; ProcType:=ptFunction;
end end
else else
@ -6366,6 +6370,7 @@ var
L : TFPList; L : TFPList;
I , Cnt, p: Integer; I , Cnt, p: Integer;
CurName: String; CurName: String;
Part: TProcedureNamePart;
begin begin
Result:=ExpectIdentifier; Result:=ExpectIdentifier;
Cnt:=1; Cnt:=1;
@ -6378,10 +6383,11 @@ var
inc(Cnt); inc(Cnt);
CurName:=ExpectIdentifier; CurName:=ExpectIdentifier;
Result:=Result+'.'+CurName; Result:=Result+'.'+CurName;
if length(NameParts)>0 then if NameParts<>nil then
begin begin
SetLength(NameParts,Cnt); Part:=TProcedureNamePart.Create;
NameParts[Cnt-1].Name:=CurName; NameParts.Add(Part);
Part.Name:=CurName;
end; end;
end end
else else
@ -6392,32 +6398,34 @@ var
if (not MustBeGeneric) and not (msDelphi in CurrentModeswitches) then if (not MustBeGeneric) and not (msDelphi in CurrentModeswitches) then
ParseExc(nParserGenericFunctionNeedsGenericKeyword,SParserGenericFunctionNeedsGenericKeyword); ParseExc(nParserGenericFunctionNeedsGenericKeyword,SParserGenericFunctionNeedsGenericKeyword);
// generic templates // generic templates
if length(NameParts)=0 then if NameParts=nil then
begin begin
// initialize NameParts // initialize NameParts
SetLength(NameParts,Cnt); NameParts:=TProcedureNameParts.Create;
i:=0; i:=0;
CurName:=Result; CurName:=Result;
repeat repeat
Part:=TProcedureNamePart.Create;
NameParts.Add(Part);
p:=Pos('.',CurName); p:=Pos('.',CurName);
if p>0 then if p>0 then
begin begin
NameParts[i].Name:=LeftStr(CurName,p-1); Part.Name:=LeftStr(CurName,p-1);
System.Delete(CurName,1,p); System.Delete(CurName,1,p);
end end
else else
begin begin
NameParts[i].Name:=CurName; Part.Name:=CurName;
break; break;
end; end;
inc(i); inc(i);
until false; until false;
end end
else if NameParts[Cnt-1].Templates<>nil then else if TProcedureNamePart(NameParts[Cnt-1]).Templates<>nil then
ParseExcSyntaxError; ParseExcSyntaxError;
UnGetToken; UnGetToken;
L:=TFPList.Create; L:=TFPList.Create;
NameParts[Cnt-1].Templates:=L; TProcedureNamePart(NameParts[Cnt-1]).Templates:=L;
ReadGenericArguments(L,Parent); ReadGenericArguments(L,Parent);
end end
else else
@ -6431,6 +6439,7 @@ var
PC : TPTreeElement; PC : TPTreeElement;
Ot : TOperatorType; Ot : TOperatorType;
IsTokenBased , ok: Boolean; IsTokenBased , ok: Boolean;
j, i: Integer;
begin begin
NameParts:=nil; NameParts:=nil;
Result:=nil; Result:=nil;
@ -6463,13 +6472,24 @@ begin
PC:=GetProcedureClass(ProcType); PC:=GetProcedureClass(ProcType);
if Name<>'' then if Name<>'' then
Parent:=CheckIfOverLoaded(Parent,Name); Parent:=CheckIfOverLoaded(Parent,Name);
Result:=TPasProcedure(CreateElement(PC,Name,Parent,AVisibility)); //TPasProcedure(CreateElement(PC,Name,Parent,AVisibility));
Result := TPasProcedure(Engine.CreateElement(PC, Name, Parent, AVisibility,
CurSourcePos, NameParts));
if NameParts<>nil then if NameParts<>nil then
begin begin
if Result.NameParts=nil then
// CreateElement has not used the NameParts -> do it now
Result.SetNameParts(NameParts); Result.SetNameParts(NameParts);
Engine.FinishScope(stGenericTypeTemplates,Result); // sanity check
for i:=0 to Result.NameParts.Count-1 do
with TProcedureNamePart(Result.NameParts[i]) do
if Templates<>nil then
for j:=0 to Templates.Count-1 do
if TPasElement(Templates[j]).Parent<>Result then
ParseExc(nParserError,SParserError+'[20190818131750] '+TPasElement(Templates[j]).Parent.Name+':'+TPasElement(Templates[j]).Parent.ClassName);
if NameParts.Count>0 then
ParseExc(nParserError,SParserError+'[20190818131909] "'+Name+'"');
end; end;
case ProcType of case ProcType of
ptFunction, ptClassFunction, ptOperator, ptClassOperator, ptAnonymousFunction: ptFunction, ptClassFunction, ptOperator, ptClassOperator, ptAnonymousFunction:
begin begin
@ -6506,7 +6526,7 @@ begin
end; end;
ok:=true; ok:=true;
finally finally
if NameParts<>nil then; if NameParts<>nil then
ReleaseProcNameParts(NameParts); ReleaseProcNameParts(NameParts);
if (not ok) and (Result<>nil) then if (not ok) and (Result<>nil) then
Result.Release{$IFDEF CheckPasTreeRefCount}('CreateElement'){$ENDIF}; Result.Release{$IFDEF CheckPasTreeRefCount}('CreateElement'){$ENDIF};

View File

@ -19,6 +19,7 @@ type
procedure TestGen_GenTypeWithWrongParamCountFail; procedure TestGen_GenTypeWithWrongParamCountFail;
procedure TestGen_GenericNotFoundFail; procedure TestGen_GenericNotFoundFail;
procedure TestGen_SameNameSameParamCountFail; procedure TestGen_SameNameSameParamCountFail;
procedure TestGen_TypeAliasWithoutSpecializeFail;
// constraints // constraints
procedure TestGen_ConstraintStringFail; procedure TestGen_ConstraintStringFail;
@ -53,13 +54,18 @@ type
procedure TestGen_ClassForwardConstraintKeywordMismatch; procedure TestGen_ClassForwardConstraintKeywordMismatch;
procedure TestGen_ClassForwardConstraintTypeMismatch; procedure TestGen_ClassForwardConstraintTypeMismatch;
procedure TestGen_ClassForward_Circle; procedure TestGen_ClassForward_Circle;
procedure TestGen_Class_RedeclareInUnitImplFail;
// ToDo: add another in unit implementation
procedure TestGen_Class_Method; procedure TestGen_Class_Method;
// ToDo: procedure TestGen_Class_MethodOverride; // ToDo: procedure TestGen_Class_MethodOverride;
procedure TestGen_Class_MethodDelphi;
// ToDo: procedure TestGen_Class_MethodDelphiTypeParamMissing;
// ToDo: procedure TestGen_Class_MethodImplConstraintFail;
procedure TestGen_Class_SpecializeSelfInside; procedure TestGen_Class_SpecializeSelfInside;
// ToDo: generic class overload <T> <S,T> // ToDo: generic class overload <T> <S,T>
procedure TestGen_Class_GenAncestor; procedure TestGen_Class_GenAncestor;
procedure TestGen_Class_AncestorSelfFail; procedure TestGen_Class_AncestorSelfFail;
// ToDo: class-of // ToDo: class of TBird<word> fail
// ToDo: UnitA.impl uses UnitB.intf uses UnitA.intf, UnitB has specialize of UnitA // ToDo: UnitA.impl uses UnitB.intf uses UnitA.intf, UnitB has specialize of UnitA
procedure TestGen_Class_NestedType; procedure TestGen_Class_NestedType;
procedure TestGen_Class_NestedRecord; procedure TestGen_Class_NestedRecord;
@ -81,12 +87,14 @@ type
procedure TestGen_ProcType; procedure TestGen_ProcType;
// ToDo: pointer of generic // ToDo: pointer of generic
// ToDo: PBird = ^TBird<word> fail
// ToDo: helpers for generics // ToDo: helpers for generics
// generic functions // generic functions
// ToDo: generic class method overload <T> <S,T>
procedure TestGen_GenericFunction; // ToDo procedure TestGen_GenericFunction; // ToDo
// ToDo: generic class method overload <T> <S,T>
// ToDo: procedure TestGen_GenMethod_ClassConstructorFail;
// generic statements // generic statements
procedure TestGen_LocalVar; procedure TestGen_LocalVar;
@ -166,6 +174,20 @@ begin
nDuplicateIdentifier); nDuplicateIdentifier);
end; end;
procedure TTestResolveGenerics.TestGen_TypeAliasWithoutSpecializeFail;
begin
StartProgram(false);
Add([
'{$mode delphi}',
'type',
' TBird<T> = record w: T; end;',
' TBirdAlias = TBird;',
'begin',
'']);
CheckResolverException('type expected, but TBird<> found',
nXExpectedButYFound);
end;
procedure TTestResolveGenerics.TestGen_ConstraintStringFail; procedure TTestResolveGenerics.TestGen_ConstraintStringFail;
begin begin
StartProgram(false); StartProgram(false);
@ -381,7 +403,7 @@ begin
' end;', ' end;',
'begin', 'begin',
'']); '']);
CheckResolverException('type "TBird" is not yet completely defined', CheckResolverException('type "TBird<>" is not yet completely defined',
nTypeXIsNotYetCompletelyDefined); nTypeXIsNotYetCompletelyDefined);
end; end;
@ -595,6 +617,21 @@ begin
ParseProgram; ParseProgram;
end; end;
procedure TTestResolveGenerics.TestGen_Class_RedeclareInUnitImplFail;
begin
StartUnit(false);
Add([
'interface',
'type',
' TObject = class end;',
' generic TBird<T> = class v: T; end;',
'implementation',
'type generic TBird<T> = record v: T; end;',
'']);
CheckResolverException('Duplicate identifier "TBird" at afile.pp(5,16)',
nDuplicateIdentifier);
end;
procedure TTestResolveGenerics.TestGen_Class_Method; procedure TTestResolveGenerics.TestGen_Class_Method;
begin begin
StartProgram(false); StartProgram(false);
@ -620,6 +657,31 @@ begin
ParseProgram; ParseProgram;
end; end;
procedure TTestResolveGenerics.TestGen_Class_MethodDelphi;
begin
StartProgram(false);
Add([
'{$mode delphi}',
'type',
' TObject = class end;',
' {#Typ}T = word;',
' TBird<{#Templ}T> = class',
' function Fly(p:T): T; virtual; abstract;',
' function Run(p:T): T;',
' end;',
'function TBird<T>.Run(p:T): T;',
'begin',
'end;',
'var',
' b: TBird<word>;',
' {=Typ}w: T;',
'begin',
' w:=b.Fly(w);',
' w:=b.Run(w);',
'']);
ParseProgram;
end;
procedure TTestResolveGenerics.TestGen_Class_SpecializeSelfInside; procedure TTestResolveGenerics.TestGen_Class_SpecializeSelfInside;
begin begin
StartProgram(false); StartProgram(false);
@ -676,7 +738,7 @@ begin
' b: specialize TBird<word>;', ' b: specialize TBird<word>;',
'begin', 'begin',
'']); '']);
CheckResolverException('type "TBird" is not yet completely defined',nTypeXIsNotYetCompletelyDefined); CheckResolverException('type "TBird<>" is not yet completely defined',nTypeXIsNotYetCompletelyDefined);
end; end;
procedure TTestResolveGenerics.TestGen_Class_NestedType; procedure TTestResolveGenerics.TestGen_Class_NestedType;
@ -960,6 +1022,7 @@ end;
procedure TTestResolveGenerics.TestGen_GenericFunction; procedure TTestResolveGenerics.TestGen_GenericFunction;
begin begin
exit;
StartProgram(false); StartProgram(false);
Add([ Add([
'generic function DoIt<T>(a: T): T;', 'generic function DoIt<T>(a: T): T;',

View File

@ -9149,7 +9149,7 @@ begin
Add('begin'); Add('begin');
Add('end;'); Add('end;');
Add('begin'); Add('begin');
CheckResolverException('identifier not found "TClassA"',nIdentifierNotFound); CheckResolverException('class "TClassA" not found in this module',nClassXNotFoundInThisModule);
end; end;
procedure TTestResolver.TestClass_MethodInOtherUnitFail; procedure TTestResolver.TestClass_MethodInOtherUnitFail;
@ -9170,7 +9170,8 @@ begin
'begin', 'begin',
'end;', 'end;',
'begin']); 'begin']);
CheckResolverException('method class "TObject" in other unit "unit1"',nMethodClassXInOtherUnitY); CheckResolverException('class "TObject" not found in this module',
nClassXNotFoundInThisModule);
end; end;
procedure TTestResolver.TestClass_MethodWithParams; procedure TTestResolver.TestClass_MethodWithParams;

View File

@ -3762,14 +3762,14 @@ var
NameParts: TProcedureNameParts; NameParts: TProcedureNameParts;
begin begin
NameParts:=El.NameParts; NameParts:=El.NameParts;
if length(NameParts)=0 then exit; if (NameParts=nil) or (NameParts.Count=0) then exit;
Arr:=TJSONArray.Create; Arr:=TJSONArray.Create;
Obj.Add('NameParts',Arr); Obj.Add('NameParts',Arr);
for i:=0 to length(NameParts)-1 do for i:=0 to NameParts.Count-1 do
begin begin
NamePartObj:=TJSONObject.Create; NamePartObj:=TJSONObject.Create;
Arr.Add(NamePartObj); Arr.Add(NamePartObj);
with NameParts[i] do with TProcedureNamePart(NameParts[i]) do
begin begin
NamePartObj.Add('Name',Name); NamePartObj.Add('Name',Name);
if Templates<>nil then if Templates<>nil then
@ -7484,15 +7484,21 @@ var
NamePartObj, TemplObj: TJSONObject; NamePartObj, TemplObj: TJSONObject;
GenTypeName: string; GenTypeName: string;
GenType: TPasGenericTemplateType; GenType: TPasGenericTemplateType;
NamePart: TProcedureNamePart;
begin begin
ReleaseProcNameParts(El.NameParts); ReleaseProcNameParts(El.NameParts);
if ReadArray(Obj,'NameParts',Arr,El) then if ReadArray(Obj,'NameParts',Arr,El) then
begin begin
SetLength(El.NameParts,Arr.Count); if El.NameParts=nil then
El.NameParts:=TProcedureNameParts.Create
else
El.NameParts.Clear;
for i:=0 to Arr.Count-1 do for i:=0 to Arr.Count-1 do
begin begin
NamePartObj:=CheckJSONObject(Arr[i],20190718113441); NamePartObj:=CheckJSONObject(Arr[i],20190718113441);
with El.NameParts[i] do NamePart:=TProcedureNamePart.Create;
El.NameParts.Add(NamePart);
with NamePart do
begin begin
if not ReadString(NamePartObj,'Name',Name,El) then if not ReadString(NamePartObj,'Name',Name,El) then
RaiseMsg(20190718113739,El,IntToStr(i)); RaiseMsg(20190718113739,El,IntToStr(i));

View File

@ -1549,17 +1549,21 @@ var
begin begin
OrigNameParts:=Orig.NameParts; OrigNameParts:=Orig.NameParts;
RestNameParts:=Rest.NameParts; RestNameParts:=Rest.NameParts;
AssertEquals(Path+'.NameParts length',length(OrigNameParts),length(RestNameParts)); AssertEquals(Path+'.NameParts<>nil',OrigNameParts<>nil,RestNameParts<>nil);
for i:=0 to length(OrigNameParts)-1 do if OrigNameParts<>nil then
begin
AssertEquals(Path+'.NameParts.Count',OrigNameParts.Count,RestNameParts.Count);
for i:=0 to OrigNameParts.Count-1 do
begin begin
SubPath:=Path+'.NameParts['+IntToStr(i)+']'; SubPath:=Path+'.NameParts['+IntToStr(i)+']';
AssertEquals(SubPath+'.Name',OrigNameParts[i].Name,RestNameParts[i].Name); AssertEquals(SubPath+'.Name',TProcedureNamePart(OrigNameParts[i]).Name,TProcedureNamePart(RestNameParts[i]).Name);
OrigTemplates:=OrigNameParts[i].Templates; OrigTemplates:=TProcedureNamePart(OrigNameParts[i]).Templates;
RestTemplates:=RestNameParts[i].Templates; RestTemplates:=TProcedureNamePart(RestNameParts[i]).Templates;
CheckRestoredObject(SubPath+'.Templates',OrigTemplates,RestTemplates); CheckRestoredObject(SubPath+'.Templates',OrigTemplates,RestTemplates);
if OrigTemplates=nil then continue; if OrigTemplates=nil then continue;
CheckRestoredElementList(SubPath+'.Templates',OrigTemplates,RestTemplates); CheckRestoredElementList(SubPath+'.Templates',OrigTemplates,RestTemplates);
end; end;
end;
end; end;
procedure TCustomTestPrecompile.CheckRestoredProcedure(const Path: string; procedure TCustomTestPrecompile.CheckRestoredProcedure(const Path: string;