mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 01:09:06 +02:00
fcl-passrc: resolver: implemented overriding property with index specifier
git-svn-id: trunk@37382 -
This commit is contained in:
parent
99a3855e6b
commit
6148ba8c31
@ -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 }
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user