fcl-passrc: resolver: implemented overriding property with index specifier

git-svn-id: trunk@37382 -
This commit is contained in:
Mattias Gaertner 2017-10-02 12:56:11 +00:00
parent 99a3855e6b
commit 6148ba8c31
3 changed files with 95 additions and 42 deletions

View File

@ -142,6 +142,7 @@ const
nDivByZero = 3069; nDivByZero = 3069;
nRangeCheckInSetConstructor = 3070; nRangeCheckInSetConstructor = 3070;
nIncompatibleTypesGotParametersExpected = 3071; nIncompatibleTypesGotParametersExpected = 3071;
nAddingIndexSpecifierRequiresNewX = 3072;
// resourcestring patterns of messages // resourcestring patterns of messages
resourcestring resourcestring
@ -216,6 +217,7 @@ resourcestring
sDivByZero = 'Division by zero'; sDivByZero = 'Division by zero';
sRangeCheckInSetConstructor = 'range check error in set constructor or duplicate set element'; sRangeCheckInSetConstructor = 'range check error in set constructor or duplicate set element';
sIncompatibleTypesGotParametersExpected = 'Incompatible types, got %s parameters, expected %s'; sIncompatibleTypesGotParametersExpected = 'Incompatible types, got %s parameters, expected %s';
sAddingIndexSpecifierRequiresNewX = 'adding index specifier requires new "%s" specifier';
type type
{ TResolveData - base class for data stored in TPasElement.CustomData } { TResolveData - base class for data stored in TPasElement.CustomData }

View File

@ -1352,6 +1352,7 @@ type
function GetPasPropertyAncestor(El: TPasProperty; WithRedeclarations: boolean = false): TPasProperty; function GetPasPropertyAncestor(El: TPasProperty; WithRedeclarations: boolean = false): TPasProperty;
function GetPasPropertyGetter(El: TPasProperty): TPasElement; function GetPasPropertyGetter(El: TPasProperty): TPasElement;
function GetPasPropertySetter(El: TPasProperty): TPasElement; function GetPasPropertySetter(El: TPasProperty): TPasElement;
function GetPasPropertyIndex(El: TPasProperty): TPasExpr;
function GetPasPropertyStoredExpr(El: TPasProperty): TPasExpr; function GetPasPropertyStoredExpr(El: TPasProperty): TPasExpr;
function GetPasClassAncestor(ClassEl: TPasClassType; SkipAlias: boolean): TPasType; function GetPasClassAncestor(ClassEl: TPasClassType; SkipAlias: boolean): TPasType;
function GetLoop(El: TPasElement): TPasImplElement; function GetLoop(El: TPasElement): TPasImplElement;
@ -3933,11 +3934,12 @@ procedure TPasResolver.FinishPropertyOfClass(PropEl: TPasProperty);
var var
PropType: TPasType; PropType: TPasType;
ClassScope: TPasClassScope; ClassScope: TPasClassScope;
AncestorProp: TPasProperty;
IndexExpr: TPasExpr;
procedure GetPropType; procedure GetPropType;
var var
AncEl: TPasElement; AncEl: TPasElement;
AncProp: TPasProperty;
begin begin
if PropType<>nil then exit; if PropType<>nil then exit;
AncEl:=nil; AncEl:=nil;
@ -3946,14 +3948,14 @@ var
if AncEl is TPasProperty then if AncEl is TPasProperty then
begin begin
// override or redeclaration property // override or redeclaration property
AncProp:=TPasProperty(AncEl); AncestorProp:=TPasProperty(AncEl);
TPasPropertyScope(PropEl.CustomData).AncestorProp:=AncProp; TPasPropertyScope(PropEl.CustomData).AncestorProp:=AncestorProp;
AncProp.AddRef; AncestorProp.AddRef;
if proFixCaseOfOverrides in Options then if proFixCaseOfOverrides in Options then
PropEl.Name:=AncProp.Name; PropEl.Name:=AncestorProp.Name;
end end
else else
AncProp:=nil; AncestorProp:=nil;
if PropEl.VarType<>nil then if PropEl.VarType<>nil then
begin begin
@ -3963,15 +3965,15 @@ var
else else
begin begin
// property override // property override
if AncProp=nil then if AncestorProp=nil then
RaiseMsg(20170216151741,nNoPropertyFoundToOverride,sNoPropertyFoundToOverride,[],PropEl); RaiseMsg(20170216151741,nNoPropertyFoundToOverride,sNoPropertyFoundToOverride,[],PropEl);
// check property versus class property // check property versus class property
if PropEl.ClassType<>AncProp.ClassType then if PropEl.ClassType<>AncestorProp.ClassType then
RaiseXExpectedButYFound(20170216151744,AncProp.ElementTypeName,PropEl.ElementTypeName,PropEl); RaiseXExpectedButYFound(20170216151744,AncestorProp.ElementTypeName,PropEl.ElementTypeName,PropEl);
// get inherited type // get inherited type
PropType:=GetPasPropertyType(AncProp); PropType:=GetPasPropertyType(AncestorProp);
// update DefaultProperty // update DefaultProperty
if (ClassScope.DefaultProperty=AncProp) then if (ClassScope.DefaultProperty=AncestorProp) then
ClassScope.DefaultProperty:=PropEl; ClassScope.DefaultProperty:=PropEl;
end; end;
end; end;
@ -4043,7 +4045,7 @@ var
[IntToStr(ArgNo),'untyped',GetTypeDescription(IndexResolved)],ErrorEl) [IntToStr(ArgNo),'untyped',GetTypeDescription(IndexResolved)],ErrorEl)
else else
begin begin
if CheckParamCompatibility(PropEl.IndexExpr,ProcArg,ArgNo,true)=cIncompatible then if CheckParamCompatibility(IndexExpr,ProcArg,ArgNo,true)=cIncompatible then
begin begin
ComputeElement(ProcArg.ArgType,ProcArgResolved,[rcType]); ComputeElement(ProcArg.ArgType,ProcArgResolved,[rcType]);
RaiseIncompatibleTypeRes(20170924203829,nIncompatibleTypeArgNo, RaiseIncompatibleTypeRes(20170924203829,nIncompatibleTypeArgNo,
@ -4157,19 +4159,15 @@ var
end; end;
exit; exit;
end; end;
if (IndexVal=nil) if (IdentEl<>nil)
and (IdentEl<>nil)
and ((IdentEl.ClassType=TPasVariable) and ((IdentEl.ClassType=TPasVariable)
or ((IdentEl.ClassType=TPasConst) and not TPasConst(IdentEl).IsConst)) or ((IdentEl.ClassType=TPasConst) and not TPasConst(IdentEl).IsConst)) then
then
begin begin
// field // field
aVar:=TPasVariable(IdentEl); aVar:=TPasVariable(IdentEl);
// check if member // check if member
if not (Expr is TPrimitiveExpr) then if not (Expr is TPrimitiveExpr) then
RaiseXExpectedButYFound(20170923202003,'member variable','foreign '+aVar.ElementTypeName,Expr); RaiseXExpectedButYFound(20170923202003,'member variable','foreign '+aVar.ElementTypeName,Expr);
if PropEl.IndexExpr<>nil then
RaiseNotYetImplemented(20170409214006,PropEl.StoredAccessor,'stored with index');
// check type boolean // check type boolean
TypeEl:=aVar.VarType; TypeEl:=aVar.VarType;
TypeEl:=ResolveAliasType(TypeEl); TypeEl:=ResolveAliasType(TypeEl);
@ -4207,9 +4205,11 @@ var
Proc: TPasProcedure; Proc: TPasProcedure;
Arg: TPasArgument; Arg: TPasArgument;
PropArgCount, NeedArgCnt: Integer; PropArgCount, NeedArgCnt: Integer;
PropTypeResolved, DefaultResolved, IndexResolved: TPasResolverResult; PropTypeResolved, DefaultResolved, IndexResolved,
AncIndexResolved: TPasResolverResult;
m: TVariableModifier; m: TVariableModifier;
IndexVal: TResEvalValue; IndexVal: TResEvalValue;
AncIndexExpr: TPasExpr;
begin begin
CheckTopScope(TPasPropertyScope); CheckTopScope(TPasPropertyScope);
PopScope; PopScope;
@ -4223,15 +4223,23 @@ begin
PropType:=nil; PropType:=nil;
CurClassType:=PropEl.Parent as TPasClassType; CurClassType:=PropEl.Parent as TPasClassType;
ClassScope:=NoNil(CurClassType.CustomData) as TPasClassScope; ClassScope:=NoNil(CurClassType.CustomData) as TPasClassScope;
AncestorProp:=nil;
GetPropType; GetPropType;
IndexVal:=nil; IndexVal:=nil;
try try
if PropEl.IndexExpr<>nil then if PropEl.IndexExpr<>nil then
begin begin
// index specifier -> check if simple value // index specifier
ResolveExpr(PropEl.IndexExpr,rraRead); // -> check if simple value
ComputeElement(PropEl.IndexExpr,IndexResolved,[rcConstant]); IndexExpr:=PropEl.IndexExpr;
IndexVal:=Eval(PropEl.IndexExpr,[refConst]); ResolveExpr(IndexExpr,rraRead);
end
else
IndexExpr:=GetPasPropertyIndex(PropEl);
if IndexExpr<>nil then
begin
ComputeElement(IndexExpr,IndexResolved,[rcConstant]);
IndexVal:=Eval(IndexExpr,[refConst]);
case IndexVal.Kind of case IndexVal.Kind of
revkBool, revkBool,
revkInt, revkUInt, revkInt, revkUInt,
@ -4241,6 +4249,43 @@ begin
else else
RaiseXExpectedButYFound(20170924202837,'ordinal',GetTypeDescription(IndexResolved),PropEl.IndexExpr); RaiseXExpectedButYFound(20170924202837,'ordinal',GetTypeDescription(IndexResolved),PropEl.IndexExpr);
end; end;
if (PropEl.IndexExpr<>nil) and (PropEl.VarType=nil) then
begin
// check if index is compatible to ancestor index specifier
AncIndexExpr:=GetPasPropertyIndex(AncestorProp);
if AncIndexExpr=nil then
begin
// ancestor had no index specifier
if PropEl.ReadAccessor=nil then
begin
AccEl:=GetPasPropertyGetter(AncestorProp);
if AccEl is TPasProcedure then
RaiseMsg(20171002144103,nAddingIndexSpecifierRequiresNewX,
sAddingIndexSpecifierRequiresNewX,['read'],IndexExpr);
end;
if PropEl.WriteAccessor=nil then
begin
AccEl:=GetPasPropertySetter(AncestorProp);
if AccEl is TPasProcedure then
RaiseMsg(20171002144419,nAddingIndexSpecifierRequiresNewX,
sAddingIndexSpecifierRequiresNewX,['write'],IndexExpr);
end;
if PropEl.StoredAccessor=nil then
begin
AccEl:=GetPasPropertyStoredExpr(AncestorProp);
if AccEl<>nil then
begin
ComputeElement(AccEl,AncIndexResolved,[rcNoImplicitProc]);
if AncIndexResolved.IdentEl is TPasProcedure then
RaiseMsg(20171002144644,nAddingIndexSpecifierRequiresNewX,
sAddingIndexSpecifierRequiresNewX,['stored'],IndexExpr);
end;
end;
end
else
// ancestor had already an index specifier -> check same type
CheckEqualElCompatibility(PropEl.IndexExpr,AncIndexExpr,PropEl.IndexExpr,true);
end;
end; end;
if PropEl.ReadAccessor<>nil then if PropEl.ReadAccessor<>nil then
@ -4249,7 +4294,7 @@ begin
AccEl:=GetAccessor(PropEl.ReadAccessor); AccEl:=GetAccessor(PropEl.ReadAccessor);
if (AccEl.ClassType=TPasVariable) or (AccEl.ClassType=TPasConst) then if (AccEl.ClassType=TPasVariable) or (AccEl.ClassType=TPasConst) then
begin begin
if (PropEl.Args.Count>0) or (IndexVal<>nil) then if (PropEl.Args.Count>0) then
RaiseXExpectedButYFound(20170216151823,'function',AccEl.ElementTypeName,PropEl.ReadAccessor); RaiseXExpectedButYFound(20170216151823,'function',AccEl.ElementTypeName,PropEl.ReadAccessor);
if not IsSameType(TPasVariable(AccEl).VarType,PropType,true) then if not IsSameType(TPasVariable(AccEl).VarType,PropType,true) then
RaiseIncompatibleType(20170216151826,nIncompatibleTypesGotExpected, RaiseIncompatibleType(20170216151826,nIncompatibleTypesGotExpected,
@ -4293,8 +4338,6 @@ begin
RaiseMsg(20170216151847,nWrongNumberOfParametersForCallTo,sWrongNumberOfParametersForCallTo, RaiseMsg(20170216151847,nWrongNumberOfParametersForCallTo,sWrongNumberOfParametersForCallTo,
[Proc.Name],PropEl.ReadAccessor); [Proc.Name],PropEl.ReadAccessor);
end end
else if IndexVal<>nil then
RaiseXExpectedButYFound(20170216151849,'function',AccEl.ElementTypeName,PropEl.ReadAccessor)
else else
RaiseXExpectedButYFound(20170216151850,'variable',AccEl.ElementTypeName,PropEl.ReadAccessor); RaiseXExpectedButYFound(20170216151850,'variable',AccEl.ElementTypeName,PropEl.ReadAccessor);
end; end;
@ -4306,7 +4349,7 @@ begin
if (AccEl.ClassType=TPasVariable) if (AccEl.ClassType=TPasVariable)
or ((AccEl.ClassType=TPasConst) and (not TPasConst(AccEl).IsConst)) then or ((AccEl.ClassType=TPasConst) and (not TPasConst(AccEl).IsConst)) then
begin begin
if (PropEl.Args.Count>0) or (IndexVal<>nil) then if (PropEl.Args.Count>0) then
RaiseXExpectedButYFound(20170216151852,'procedure',AccEl.ElementTypeName,PropEl.WriteAccessor); RaiseXExpectedButYFound(20170216151852,'procedure',AccEl.ElementTypeName,PropEl.WriteAccessor);
if not IsSameType(TPasVariable(AccEl).VarType,PropType,true) then if not IsSameType(TPasVariable(AccEl).VarType,PropType,true) then
RaiseIncompatibleType(20170216151855,nIncompatibleTypesGotExpected, RaiseIncompatibleType(20170216151855,nIncompatibleTypesGotExpected,
@ -11948,6 +11991,21 @@ begin
end; end;
end; end;
function TPasResolver.GetPasPropertyIndex(El: TPasProperty): TPasExpr;
// search the index expression of a property
begin
Result:=nil;
while El<>nil do
begin
if El.IndexExpr<>nil then
begin
Result:=El.IndexExpr;
exit;
end;
El:=GetPasPropertyAncestor(El);
end;
end;
function TPasResolver.GetPasPropertyStoredExpr(El: TPasProperty): TPasExpr; function TPasResolver.GetPasPropertyStoredExpr(El: TPasProperty): TPasExpr;
// search the stored expression of a property // search the stored expression of a property
begin begin

View File

@ -504,7 +504,6 @@ type
Procedure TestPropertyStoredAccessorFuncWrongResult; Procedure TestPropertyStoredAccessorFuncWrongResult;
Procedure TestPropertyStoredAccessorFuncWrongArgCount; Procedure TestPropertyStoredAccessorFuncWrongArgCount;
Procedure TestPropertyIndexSpec; Procedure TestPropertyIndexSpec;
Procedure TestPropertyIndexSpec_ReadAccessorVarFail;
Procedure TestPropertyIndexSpec_ReadAccessorWrongArgCount; Procedure TestPropertyIndexSpec_ReadAccessorWrongArgCount;
Procedure TestPropertyIndexSpec_ReadAccessorWrongIndexArgType; Procedure TestPropertyIndexSpec_ReadAccessorWrongIndexArgType;
Procedure TestPropertyDefaultValue; Procedure TestPropertyDefaultValue;
@ -8130,6 +8129,7 @@ begin
'type', 'type',
' TEnum = (red, blue);', ' TEnum = (red, blue);',
' TObject = class', ' TObject = class',
' FB: boolean;',
' function GetIntBool(Index: longint): boolean; virtual; abstract;', ' function GetIntBool(Index: longint): boolean; virtual; abstract;',
' procedure SetIntBool(Index: longint; b: boolean); virtual; abstract;', ' procedure SetIntBool(Index: longint; b: boolean); virtual; abstract;',
' function GetBoolBool(Index: boolean): boolean; virtual; abstract;', ' function GetBoolBool(Index: boolean): boolean; virtual; abstract;',
@ -8144,25 +8144,18 @@ begin
' property B4: boolean index CB read GetBoolBool write SetBoolBool stored GetBoolBool;', ' property B4: boolean index CB read GetBoolBool write SetBoolBool stored GetBoolBool;',
' property B5: boolean index red read GetEnumBool write SetEnumBool stored GetEnumBool;', ' property B5: boolean index red read GetEnumBool write SetEnumBool stored GetEnumBool;',
' property B6: boolean index TEnum.blue read GetEnumBool write SetEnumBool stored GetEnumBool;', ' property B6: boolean index TEnum.blue read GetEnumBool write SetEnumBool stored GetEnumBool;',
' property B7: boolean index 1 read GetIntBool write FB stored FB;',
' property I1[A: String]: boolean index 2 read GetStrIntBool write SetStrIntBool;', ' property I1[A: String]: boolean index 2 read GetStrIntBool write SetStrIntBool;',
' end;', ' end;',
' TBird = class',
' function GetIntBoolOvr(Index: longint): boolean; virtual; abstract;',
' property B1 index 3;',
' property B2 read GetIntBoolOvr;',
' end;',
'begin']); 'begin']);
ParseProgram; ParseProgram;
end; end;
procedure TTestResolver.TestPropertyIndexSpec_ReadAccessorVarFail;
begin
StartProgram(false);
Add([
'type',
' TObject = class',
' FB: boolean;',
' property B: boolean index 1 read FB;',
' end;',
'begin']);
CheckResolverException('function expected, but variable found',nXExpectedButYFound);
end;
procedure TTestResolver.TestPropertyIndexSpec_ReadAccessorWrongArgCount; procedure TTestResolver.TestPropertyIndexSpec_ReadAccessorWrongArgCount;
begin begin
StartProgram(false); StartProgram(false);