mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-12-10 09:27:18 +01:00
3018 lines
70 KiB
ObjectPascal
3018 lines
70 KiB
ObjectPascal
unit tcresolvegenerics;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, testregistry, tcresolver, PasResolveEval, PParser,
|
|
PScanner;
|
|
|
|
type
|
|
|
|
{ TTestResolveGenerics }
|
|
|
|
TTestResolveGenerics = Class(TCustomTestResolver)
|
|
Published
|
|
// generic types
|
|
procedure TestGen_MissingTemplateFail;
|
|
procedure TestGen_VarTypeWithoutSpecializeFail;
|
|
procedure TestGen_GenTypeWithWrongParamCountFail;
|
|
procedure TestGen_GenericNotFoundFail;
|
|
procedure TestGen_SameNameSameParamCountFail;
|
|
procedure TestGen_TypeAliasWithoutSpecializeFail;
|
|
procedure TestGen_TemplNameEqTypeNameFail; // type T<T>
|
|
|
|
// constraints
|
|
procedure TestGen_ConstraintStringFail;
|
|
procedure TestGen_ConstraintMultiClassFail;
|
|
procedure TestGen_ConstraintRecordExpectedFail;
|
|
procedure TestGen_ConstraintClassRecordFail;
|
|
procedure TestGen_ConstraintRecordClassFail;
|
|
procedure TestGen_ConstraintArrayFail;
|
|
procedure TestGen_ConstraintConstructor;
|
|
procedure TestGen_ConstraintUnit;
|
|
// ToDo: constraint T:Unit2.specialize TGen<word>
|
|
procedure TestGen_ConstraintSpecialize;
|
|
procedure TestGen_ConstraintTSpecializeWithT;
|
|
procedure TestGen_ConstraintTSpecializeAsTFail; // TBird<T; U: T<word>> and no T<>
|
|
procedure TestGen_ConstraintTSpecializeWithTFail; // TBird<T: TAnt<T>>
|
|
procedure TestGen_ConstraintSameNameFail; // TAnt<T:T>
|
|
procedure TestGen_ConstraintInheritedMissingRecordFail;
|
|
procedure TestGen_ConstraintInheritedMissingClassTypeFail;
|
|
procedure TestGen_ConstraintMultiParam;
|
|
procedure TestGen_ConstraintMultiParamClassMismatch;
|
|
procedure TestGen_ConstraintClassType_DotIsAsTypeCast;
|
|
procedure TestGen_ConstraintClassType_ForInT;
|
|
procedure TestGen_ConstraintClassType_IsAs;
|
|
|
|
// generic record
|
|
procedure TestGen_RecordLocalNameDuplicateFail;
|
|
procedure TestGen_Record;
|
|
procedure TestGen_RecordDelphi;
|
|
procedure TestGen_RecordNestedSpecialize_ClassRecord;
|
|
procedure TestGen_RecordNestedSpecialize_Self;
|
|
procedure TestGen_Record_SpecializeSelfInsideFail;
|
|
procedure TestGen_Record_ReferGenericSelfFail;
|
|
procedure TestGen_RecordAnoArray;
|
|
// ToDo: unitname.specialize TBird<word>.specialize TAnt<word>
|
|
|
|
// generic class
|
|
procedure TestGen_Class;
|
|
procedure TestGen_ClassDelphi;
|
|
procedure TestGen_ClassDelphi_TypeOverload;
|
|
procedure TestGen_ClassObjFPC;
|
|
procedure TestGen_ClassObjFPC_OverloadFail;
|
|
procedure TestGen_ClassObjFPC_OverloadOtherUnit;
|
|
procedure TestGen_ClassGenAncestorWithoutParamFail;
|
|
procedure TestGen_ClassForward;
|
|
procedure TestGen_ClassForwardConstraints;
|
|
procedure TestGen_ClassForwardConstraintNameMismatch;
|
|
procedure TestGen_ClassForwardConstraintKeywordMismatch;
|
|
procedure TestGen_ClassForwardConstraintTypeMismatch;
|
|
procedure TestGen_ClassForward_Circle;
|
|
procedure TestGen_Class_RedeclareInUnitImplFail;
|
|
procedure TestGen_Class_TypeOverloadInUnitImpl;
|
|
procedure TestGen_Class_MethodObjFPC;
|
|
procedure TestGen_Class_MethodOverride;
|
|
procedure TestGen_Class_MethodDelphi;
|
|
procedure TestGen_Class_MethodDelphiTypeParamMissing;
|
|
procedure TestGen_Class_MethodImplConstraintFail;
|
|
procedure TestGen_Class_MethodImplTypeParamNameMismatch;
|
|
procedure TestGen_Class_SpecializeSelfInside;
|
|
procedure TestGen_Class_AncestorTFail;
|
|
procedure TestGen_Class_GenAncestor;
|
|
procedure TestGen_Class_AncestorSelfFail;
|
|
procedure TestGen_ClassOfSpecializeFail;
|
|
// ToDo: UnitA.impl uses UnitB.intf uses UnitA.intf, UnitB has specialize of UnitA
|
|
procedure TestGen_Class_NestedType;
|
|
procedure TestGen_Class_NestedRecord;
|
|
procedure TestGen_Class_NestedClass;
|
|
procedure TestGen_Class_Enums_NotPropagating;
|
|
procedure TestGen_Class_Self;
|
|
procedure TestGen_Class_MemberTypeConstructor;
|
|
procedure TestGen_Class_AliasMemberType;
|
|
procedure TestGen_Class_AccessGenericMemberTypeFail;
|
|
procedure TestGen_Class_ReferenceTo;
|
|
procedure TestGen_Class_TwoSpecsAreNotRelatedWarn;
|
|
procedure TestGen_Class_List;
|
|
procedure TestGen_Class_Typecast;
|
|
// ToDo: different modeswitches at parse time and specialize time
|
|
|
|
// generic external class
|
|
procedure TestGen_ExtClass_Array;
|
|
procedure TestGen_ExtClass_VarargsOfType;
|
|
|
|
// generic interface
|
|
procedure TestGen_ClassInterface;
|
|
procedure TestGen_ClassInterface_Method;
|
|
|
|
// generic array
|
|
procedure TestGen_DynArray;
|
|
procedure TestGen_StaticArray;
|
|
procedure TestGen_Array_Anoynmous;
|
|
|
|
// generic procedure type
|
|
procedure TestGen_ProcType;
|
|
procedure TestGen_ProcType_AnonymousFunc_Delphi;
|
|
|
|
// pointer of generic
|
|
procedure TestGen_PointerDirectSpecializeFail;
|
|
|
|
// ToDo: helpers for generics
|
|
procedure TestGen_HelperForArray;
|
|
// ToDo: default class prop array helper: arr<b>[c]
|
|
|
|
// generic statements
|
|
procedure TestGen_LocalVar;
|
|
procedure TestGen_Statements;
|
|
procedure TestGen_InlineSpecializeExpr;
|
|
// ToDo: a.b<c>(d)
|
|
// ToDo: with a do b<c>
|
|
procedure TestGen_TryExcept;
|
|
procedure TestGen_Call;
|
|
procedure TestGen_NestedProc;
|
|
// ToDo: obj<b>[c]
|
|
|
|
// generic functions
|
|
procedure TestGenProc_Function;
|
|
procedure TestGenProc_FunctionDelphi;
|
|
procedure TestGenProc_OverloadDuplicate;
|
|
procedure TestGenProc_MissingTemplatesFail;
|
|
procedure TestGenProc_SpecializeNonGenericFail;
|
|
procedure TestGenProc_Forward;
|
|
procedure TestGenProc_External;
|
|
procedure TestGenProc_UnitIntf;
|
|
procedure TestGenProc_BackRef1Fail;
|
|
procedure TestGenProc_BackRef2Fail;
|
|
procedure TestGenProc_BackRef3Fail;
|
|
procedure TestGenProc_CallSelf;
|
|
procedure TestGenProc_CallSelfNoParams;
|
|
procedure TestGenProc_ForwardConstraints;
|
|
procedure TestGenProc_ForwardConstraintsRepeatFail;
|
|
procedure TestGenProc_ForwardTempNameMismatch;
|
|
procedure TestGenProc_ForwardOverload;
|
|
procedure TestGenProc_NestedFail;
|
|
procedure TestGenProc_TypeParamCntOverload;
|
|
procedure TestGenProc_TypeParamCntOverloadNoParams;
|
|
procedure TestGenProc_TypeParamWithDefaultParamDelphiFail;
|
|
procedure TestGenProc_ParamSpecWithT; // ToDo: Func<T>(Bird: TBird<T>)
|
|
// ToDo: NestedResultAssign
|
|
|
|
// generic function infer types
|
|
procedure TestGenProc_Infer_NeedExplicitFail;
|
|
procedure TestGenProc_Infer_Overload;
|
|
procedure TestGenProc_Infer_OverloadForward;
|
|
procedure TestGenProc_Infer_Var_Overload;
|
|
procedure TestGenProc_Infer_Widen;
|
|
procedure TestGenProc_Infer_DefaultValue;
|
|
procedure TestGenProc_Infer_DefaultValueMismatch;
|
|
procedure TestGenProc_Infer_ProcT;
|
|
procedure TestGenProc_Infer_Mismatch;
|
|
procedure TestGenProc_Infer_ArrayOfT;
|
|
procedure TestGenProc_Infer_PassAsArgDelphi;
|
|
procedure TestGenProc_Infer_PassAsArgObjFPC;
|
|
// ToDo procedure TestGenProc_Infer_ProcType;
|
|
|
|
// generic methods
|
|
procedure TestGenMethod_VirtualFail;
|
|
procedure TestGenMethod_PublishedFail;
|
|
procedure TestGenMethod_ClassInterfaceMethodFail;
|
|
procedure TestGenMethod_ClassConstructorFail;
|
|
procedure TestGenMethod_TemplNameDifferFail;
|
|
procedure TestGenMethod_ImplConstraintFail;
|
|
procedure TestGenMethod_NestedSelf;
|
|
procedure TestGenMethod_OverloadTypeParamCntObjFPC;
|
|
procedure TestGenMethod_OverloadTypeParamCntDelphi;
|
|
procedure TestGenMethod_OverloadArgs;
|
|
procedure TestGenMethod_TypeCastParam;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TTestResolveGenerics }
|
|
|
|
procedure TTestResolveGenerics.TestGen_MissingTemplateFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type generic g< > = array of word;',
|
|
'begin',
|
|
'']);
|
|
CheckParserException('Expected "Identifier"',nParserExpectTokenError);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_VarTypeWithoutSpecializeFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type generic TBird<T> = record end;',
|
|
'var b: TBird;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Generics without specialization cannot be used as a type for a variable',
|
|
nGenericsWithoutSpecializationAsType);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_GenTypeWithWrongParamCountFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type generic TBird<T> = record end;',
|
|
'var b: specialize TBird<word, byte>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('identifier not found "TBird<,>"',
|
|
nIdentifierNotFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_GenericNotFoundFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TBird = specialize TAnimal<word>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('identifier not found "TAnimal<>"',
|
|
nIdentifierNotFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_SameNameSameParamCountFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TBird<S,T> = record w: T; end;',
|
|
' TBird<X,Y> = record f: X; end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Duplicate identifier "TBird" at afile.pp(4,8)',
|
|
nDuplicateIdentifier);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_TypeAliasWithoutSpecializeFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TBird<T> = record w: T; end;',
|
|
' TBirdAlias = TBird;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Generics without specialization cannot be used as a type for a variable',
|
|
nGenericsWithoutSpecializationAsType);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_TemplNameEqTypeNameFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' generic TBird<TBird> = record v: T; end;',
|
|
'var r: specialize TBird<word>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Duplicate identifier "TBird" at afile.pp(4,16)',
|
|
nDuplicateIdentifier);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintStringFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type generic TRec<T:string> = record end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('"String" is not a valid constraint',
|
|
nXIsNotAValidConstraint);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintMultiClassFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird = class end;',
|
|
' TBear = class end;',
|
|
' generic TRec<T: TBird, TBear> = record end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('"TBird" constraint and "TBear" constraint cannot be specified together',
|
|
nConstraintXAndConstraintYCannotBeTogether);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintRecordExpectedFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' generic TBird<T:record> = record v: T; end;',
|
|
'var r: specialize TBird<word>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('record type expected, but Word found',
|
|
nXExpectedButYFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintClassRecordFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TRec = record end;',
|
|
' generic TBird<T:class> = record v: T; end;',
|
|
'var r: specialize TBird<TRec>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('class type expected, but TRec found',
|
|
nXExpectedButYFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintRecordClassFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T:record> = record v: T; end;',
|
|
'var r: specialize TBird<TObject>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('record type expected, but TObject found',
|
|
nXExpectedButYFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintArrayFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TArr = array of word;',
|
|
' generic TBird<T:TArr> = record v: T; end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('"array of Word" is not a valid constraint',
|
|
nXIsNotAValidConstraint);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintConstructor;
|
|
begin
|
|
StartProgram(true,[supTObject]);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' generic TBird<T:constructor> = class',
|
|
' o: T;',
|
|
' procedure Fly;',
|
|
' end;',
|
|
' TAnt = class end;',
|
|
'var a: specialize TBird<TAnt>;',
|
|
'procedure TBird.Fly;',
|
|
'begin',
|
|
' o:=T.Create;',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintUnit;
|
|
begin
|
|
AddModuleWithIntfImplSrc('unit1.pas',
|
|
LinesToStr([
|
|
'type',
|
|
' TBird = class b1: word; end;',
|
|
' generic TAnt<T> = class a1: T; end;',
|
|
'']),
|
|
LinesToStr([
|
|
'']));
|
|
StartProgram(true,[supTObject]);
|
|
Add([
|
|
'uses unit1;',
|
|
'type',
|
|
' generic TCat<T: unit1.TBird> = class v: T; end;',
|
|
' generic TFish<T: specialize TAnt<word>> = class v: T; end;',
|
|
' TEagle = class(unit1.TBird);',
|
|
' TRedAnt = specialize TAnt<word>;',
|
|
'var',
|
|
' eagle: TEagle;',
|
|
' redant: TRedAnt;',
|
|
' cat: specialize TCat<TEagle>;',
|
|
' fish: specialize TFish<TRedAnt>;',
|
|
'begin',
|
|
' cat.v:=eagle;',
|
|
' fish.v:=redant;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintSpecialize;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TAnt<S> = class m: S; end;',
|
|
' generic TBird<T:specialize TAnt<word>> = class',
|
|
' o: T;',
|
|
' end;',
|
|
' TFireAnt = class(specialize TAnt<word>) end;',
|
|
'var',
|
|
' a: specialize TBird<TFireAnt>;',
|
|
' f: TFireAnt;',
|
|
'begin',
|
|
' a.o:=f;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintTSpecializeWithT;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnt<S> = class m: S; end;',
|
|
' TBird<X; Y: TAnt<X>> = class',
|
|
' Ant: Y;',
|
|
' end;',
|
|
' TEagle<X; Y:X> = class',
|
|
' e: Y;',
|
|
' end;',
|
|
' TFireAnt<F> = class(TAnt<F>) end;',
|
|
' TAntWord = TAnt<word>;',
|
|
' TBirdAntWord = TBird<word, TAnt<word>>;',
|
|
'var',
|
|
' a: TAnt<word>;',
|
|
' b: TBird<word, TAntWord>;',
|
|
' c: TBird<TBirdAntWord, TAnt<TBirdAntWord>>;',
|
|
' f: TEagle<TAnt<boolean>, TFireAnt<boolean>>;',
|
|
' fb: TFireAnt<boolean>;',
|
|
'begin',
|
|
' b.Ant:=a;',
|
|
' f.e:=fb;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintTSpecializeAsTFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
// Note: would work if generic T<S> exists
|
|
' generic TBird<T; U: specialize T<word>> = record v: T; end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('identifier not found "T<>"',nIdentifierNotFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintTSpecializeWithTFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TAnt<S> = class v: S; end;',
|
|
' generic TBird<T: specialize TAnt<T>> = class v: T; end;',
|
|
' TEagle = specialize TBird<specialize TAnt<word>>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('identifier not found "T"',nIdentifierNotFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintSameNameFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' T = TObject;',
|
|
' generic TAnt<T:T> = record v: word; end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException(sTypeCycleFound,nTypeCycleFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintInheritedMissingRecordFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T: record> = class v: T; end;',
|
|
' generic TEagle<U> = class(specialize TBird<U>)',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Type parameter "U" is missing constraint "record"',
|
|
nTypeParamXIsMissingConstraintY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintInheritedMissingClassTypeFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnt = class end;',
|
|
' generic TBird<T: TAnt> = class v: T; end;',
|
|
' generic TEagle<U> = class(specialize TBird<U>)',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Type parameter "U" is not compatible with type "TAnt"',
|
|
nTypeParamXIsNotCompatibleWithY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintMultiParam;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnt = class end;',
|
|
' generic TBird<S,T: TAnt> = class',
|
|
' x: S;',
|
|
' y: T;',
|
|
' end;',
|
|
' TRedAnt = class(TAnt) end;',
|
|
' TEagle = specialize TBird<TRedAnt,TAnt>;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintMultiParamClassMismatch;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnt = class end;',
|
|
' TRedAnt = class(TAnt) end;',
|
|
' generic TBird<S,T: TRedAnt> = class',
|
|
' x: S;',
|
|
' y: T;',
|
|
' end;',
|
|
' TEagle = specialize TBird<TRedAnt,TAnt>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Incompatible types: got "TAnt" expected "TRedAnt"',
|
|
nIncompatibleTypesGotExpected);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintClassType_DotIsAsTypeCast;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnt = class',
|
|
' procedure Run; external; overload;',
|
|
' end;',
|
|
' TRedAnt = class(TAnt)',
|
|
' procedure Run(w: word); external; overload;',
|
|
' end;',
|
|
' generic TBird<T: TRedAnt> = class',
|
|
' y: T;',
|
|
' procedure Fly;',
|
|
' end;',
|
|
' TFireAnt = class(TRedAnt);',
|
|
' generic TEagle<U: TRedAnt> = class(specialize TBird<U>) end;',
|
|
' TRedEagle = specialize TEagle<TRedAnt>;',
|
|
'procedure TBird.Fly;',
|
|
'var f: TFireAnt;',
|
|
'begin',
|
|
' y.Run;',
|
|
' y.Run(3);',
|
|
' if y is TFireAnt then',
|
|
' f:=y as TFireAnt;',
|
|
' f:=TFireAnt(y);',
|
|
' y:=T(f);',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintClassType_ForInT;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TEnumerator<TItem> = class',
|
|
' FCurrent: TItem;',
|
|
' property Current: TItem read FCurrent;',
|
|
' function MoveNext: boolean;',
|
|
' end;',
|
|
' generic TAnt<U> = class',
|
|
' function GetEnumerator: specialize TEnumerator<U>;',
|
|
' end;',
|
|
' generic TBird<S; T: specialize TAnt<S>> = class',
|
|
' m: T;',
|
|
' procedure Fly;',
|
|
' end;',
|
|
'function TEnumerator.MoveNext: boolean;',
|
|
'begin',
|
|
'end;',
|
|
'function TAnt.GetEnumerator: specialize TEnumerator<U>;',
|
|
'begin',
|
|
'end;',
|
|
'procedure TBird.Fly;',
|
|
'var i: S;',
|
|
'begin',
|
|
' for i in m do ;',
|
|
'end;',
|
|
'var',
|
|
' a: specialize TAnt<word>;',
|
|
' w: word;',
|
|
' b: specialize TBird<word,specialize TAnt<word>>;',
|
|
'begin',
|
|
' for w in a do ;',
|
|
' for w in b.m do ;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ConstraintClassType_IsAs;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TAnt<U> = class',
|
|
' v: U;',
|
|
' function Run: U;',
|
|
' end;',
|
|
'function TAnt.Run: U;',
|
|
'var a: specialize TAnt<U>;',
|
|
'begin',
|
|
' if v is TObject then ;',
|
|
' if v is specialize TAnt<TObject> then',
|
|
' specialize TAnt<TObject>(v).v:=nil;',
|
|
' a:=v as specialize TAnt<U>;',
|
|
' if (v as specialize TAnt<TObject>).v=nil then ;',
|
|
' if nil=(v as specialize TAnt<TObject>).v then ;',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_RecordLocalNameDuplicateFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' generic TBird<T> = record T: word; end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Duplicate identifier "T" at afile.pp(4,18)',
|
|
nDuplicateIdentifier);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Record;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' {#Typ}T = word;',
|
|
' generic TRec<{#Templ}T> = record',
|
|
' {=Templ}v: T;',
|
|
' end;',
|
|
'var',
|
|
' r: specialize TRec<word>;',
|
|
' {=Typ}w: T;',
|
|
'begin',
|
|
' r.v:=w;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_RecordDelphi;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' {#Typ}T = word;',
|
|
' TRec<{#Templ}T> = record',
|
|
' {=Templ}v: T;',
|
|
' end;',
|
|
'var',
|
|
' r: TRec<word>;',
|
|
' {=Typ}w: T;',
|
|
'begin',
|
|
' r.v:=w;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_RecordNestedSpecialize_ClassRecord;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class v: T; end;',
|
|
' generic TFish<T:class> = record v: T; end;',
|
|
'var f: specialize TFish<specialize TBird<word>>;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_RecordNestedSpecialize_Self;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' generic TBird<T> = record v: T; end;',
|
|
'var',
|
|
' a: specialize TBird<specialize TBird<word>>;',
|
|
'begin',
|
|
' a.v.v:=3;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Record_SpecializeSelfInsideFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' generic TBird<T> = record',
|
|
' v: specialize TBird<word>;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('type "TBird<>" is not yet completely defined',
|
|
nTypeXIsNotYetCompletelyDefined);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Record_ReferGenericSelfFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'Type',
|
|
' TBird<T> = record',
|
|
' b: TBird<T>;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('type "TBird<>" is not yet completely defined',
|
|
nTypeXIsNotYetCompletelyDefined);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_RecordAnoArray;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' generic TBird<T> = record v: T; end;',
|
|
'var',
|
|
' a: specialize TBird<array of word>;',
|
|
' b: specialize TBird<array of word>;',
|
|
'begin',
|
|
' a:=b;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' {#Typ}T = word;',
|
|
' generic TBird<{#Templ}T> = class',
|
|
' {=Templ}v: T;',
|
|
' end;',
|
|
'var',
|
|
' b: specialize TBird<word>;',
|
|
' {=Typ}w: T;',
|
|
'begin',
|
|
' b.v:=w;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassDelphi;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' {#Typ}T = word;',
|
|
' TBird<{#Templ}T> = class',
|
|
' {=Templ}v: T;',
|
|
' end;',
|
|
'var',
|
|
' b: TBird<word>;',
|
|
' {=Typ}w: T;',
|
|
'begin',
|
|
' b.v:=w;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassDelphi_TypeOverload;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' {#a}TBird = word;',
|
|
' {#b}TBird<T> = class',
|
|
' v: T;',
|
|
' end;',
|
|
' {=b}TEagle = TBird<word>;',
|
|
'var',
|
|
' b: {@b}TBird<word>;',
|
|
' {=a}w: TBird;',
|
|
'begin',
|
|
' b.v:=w;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassObjFPC;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' v: TBird;',
|
|
' end;',
|
|
'var',
|
|
' b: specialize TBird<word>;',
|
|
'begin',
|
|
' b.v:=b;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassObjFPC_OverloadFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird = word;',
|
|
' generic TBird<T> = class',
|
|
' v: T;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Duplicate identifier "TBird" at afile.pp(5,8)',nDuplicateIdentifier);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassObjFPC_OverloadOtherUnit;
|
|
begin
|
|
AddModuleWithIntfImplSrc('unit1.pas',
|
|
LinesToStr([
|
|
'type',
|
|
' TBird = class b1: word; end;',
|
|
' generic TAnt<T> = class a1: T; end;',
|
|
'']),
|
|
LinesToStr([
|
|
'']));
|
|
AddModuleWithIntfImplSrc('unit2.pas',
|
|
LinesToStr([
|
|
'type',
|
|
' generic TBird<T> = class b2:T; end;',
|
|
' TAnt = class a2:word; end;',
|
|
'']),
|
|
LinesToStr([
|
|
'']));
|
|
StartProgram(true,[supTObject]);
|
|
Add([
|
|
'uses unit1, unit2;',
|
|
'var',
|
|
' b1: TBird;',
|
|
' b2: specialize TBird<word>;',
|
|
' a1: specialize TAnt<word>;',
|
|
' a2: TAnt;',
|
|
'begin',
|
|
' b1.b1:=1;',
|
|
' b2.b2:=2;',
|
|
' a1.a1:=3;',
|
|
' a2.a2:=4;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassGenAncestorWithoutParamFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class end;',
|
|
' generic TEagle<T> = class(TBird)',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Generics without specialization cannot be used as a type for a variable',
|
|
nGenericsWithoutSpecializationAsType);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassForward;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' {#Typ}T = word;',
|
|
' generic TBird<{#Templ_Forward}T> = class;',
|
|
' TRec = record',
|
|
' b: specialize TBird<T>;',
|
|
' end;',
|
|
' generic TBird<{#Templ}T> = class',
|
|
' {=Templ}v: T;',
|
|
' r: TRec;',
|
|
' end;',
|
|
'var',
|
|
' s: TRec;',
|
|
' {=Typ}w: T;',
|
|
'begin',
|
|
' s.b.v:=w;',
|
|
' s.b.r:=s;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassForwardConstraints;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnt = class end;',
|
|
' generic TBird<T: class; U; V: TAnt> = class;',
|
|
' TRec = record',
|
|
' b: specialize TBird<TAnt,word,TAnt>;',
|
|
' end;',
|
|
' generic TBird<T: class; U; V: TAnt> = class',
|
|
' i: U;',
|
|
' r: TRec;',
|
|
' end;',
|
|
'var',
|
|
' s: TRec;',
|
|
' w: word;',
|
|
'begin',
|
|
' s.b.i:=w;',
|
|
' s.b.r:=s;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassForwardConstraintNameMismatch;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class;',
|
|
' generic TBird<U> = class',
|
|
' i: U;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Declaration of "U" differs from previous declaration at afile.pp(5,18)',
|
|
nDeclOfXDiffersFromPrevAtY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassForwardConstraintKeywordMismatch;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T: class, constructor> = class;',
|
|
' generic TBird<U: class> = class',
|
|
' i: U;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Declaration of "U" differs from previous declaration at afile.pp(5,18)',
|
|
nDeclOfXDiffersFromPrevAtY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassForwardConstraintTypeMismatch;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnt = class end;',
|
|
' TFish = class end;',
|
|
' generic TBird<T: TAnt> = class;',
|
|
' generic TBird<T: TFish> = class',
|
|
' i: U;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Declaration of "T" differs from previous declaration at afile.pp(7,18)',
|
|
nDeclOfXDiffersFromPrevAtY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassForward_Circle;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TAnt<T> = class;',
|
|
' generic TFish<U> = class',
|
|
' private type AliasU = U;',
|
|
' var a: specialize TAnt<AliasU>;',
|
|
' Size: AliasU;',
|
|
' end;',
|
|
' generic TAnt<T> = class',
|
|
' private type AliasT = T;',
|
|
' var f: specialize TFish<AliasT>;',
|
|
' Speed: AliasT;',
|
|
' end;',
|
|
'var',
|
|
' WordFish: specialize TFish<word>;',
|
|
' BoolAnt: specialize TAnt<boolean>;',
|
|
' w: word;',
|
|
' b: boolean;',
|
|
'begin',
|
|
' WordFish.Size:=w;',
|
|
' WordFish.a.Speed:=w;',
|
|
' WordFish.a.f.Size:=w;',
|
|
' BoolAnt.Speed:=b;',
|
|
' BoolAnt.f.Size:=b;',
|
|
' BoolAnt.f.a.Speed:=b;',
|
|
'']);
|
|
ParseProgram;
|
|
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_TypeOverloadInUnitImpl;
|
|
begin
|
|
StartUnit(false);
|
|
Add([
|
|
'interface',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class v: T; end;',
|
|
'implementation',
|
|
'type generic TBird<T,U> = record x: T; y: U; end;',
|
|
'']);
|
|
ParseUnit;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_MethodObjFPC;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' {#Typ}T = word;',
|
|
' generic TBird<{#Templ}T> = class',
|
|
' function Fly(p:T): T; virtual; abstract;',
|
|
' function Run(p:T): T;',
|
|
' procedure Jump(p:T);',
|
|
' class procedure Go(p:T);',
|
|
' end;',
|
|
'function TBird.Run(p:T): T;',
|
|
'begin',
|
|
'end;',
|
|
'generic procedure TBird<T>.Jump(p:T);',
|
|
'begin',
|
|
'end;',
|
|
'generic class procedure TBird<T>.Go(p:T);',
|
|
'begin',
|
|
'end;',
|
|
'var',
|
|
' b: specialize TBird<word>;',
|
|
' {=Typ}w: T;',
|
|
'begin',
|
|
' w:=b.Fly(w);',
|
|
' w:=b.Run(w);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_MethodOverride;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' function Fly(p:T): T; virtual; abstract;',
|
|
' end;',
|
|
' generic TEagle<S> = class(specialize TBird<S>)',
|
|
' function Fly(p:S): S; override;',
|
|
' end;',
|
|
'function TEagle.Fly(p:S): S;',
|
|
'begin',
|
|
'end;',
|
|
'var',
|
|
' e: specialize TEagle<word>;',
|
|
' w: word;',
|
|
'begin',
|
|
' w:=e.Fly(w);',
|
|
'']);
|
|
ParseProgram;
|
|
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_MethodDelphiTypeParamMissing;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird<T> = class',
|
|
' function Run(p:T): T;',
|
|
' end;',
|
|
'function TBird.Run(p:T): T;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('TBird<> expected, but TBird found',nXExpectedButYFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_MethodImplConstraintFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird<T: record> = class',
|
|
' function Run(p:T): T;',
|
|
' end;',
|
|
'function TBird<T: record>.Run(p:T): T;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('illegal qualifier ":" after "T"',nIllegalQualifierAfter);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_MethodImplTypeParamNameMismatch;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird<T> = class',
|
|
' procedure DoIt;',
|
|
' end;',
|
|
'procedure TBird<S>.DoIt;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('T expected, but S found',nXExpectedButYFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_SpecializeSelfInside;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' e: T;',
|
|
' v: specialize TBird<boolean>;',
|
|
' end;',
|
|
'var',
|
|
' b: specialize TBird<word>;',
|
|
' w: word;',
|
|
'begin',
|
|
' b.e:=w;',
|
|
' if b.v.e then ;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_AncestorTFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird = class end;',
|
|
' generic TFish<T: TBird> = class(T)',
|
|
' v: T;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('class type expected, but T found',nXExpectedButYFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_GenAncestor;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' i: T;',
|
|
' end;',
|
|
' generic TEagle<T> = class(specialize TBird<T>)',
|
|
' j: T;',
|
|
' end;',
|
|
'var',
|
|
' e: specialize TEagle<word>;',
|
|
'begin',
|
|
' e.i:=e.j;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_AncestorSelfFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class(specialize TBird<word>)',
|
|
' e: T;',
|
|
' end;',
|
|
'var',
|
|
' b: specialize TBird<word>;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('type "TBird<>" is not yet completely defined',nTypeXIsNotYetCompletelyDefined);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassOfSpecializeFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' e: T;',
|
|
' end;',
|
|
' TBirdClass = class of specialize TBird<word>;',
|
|
'begin',
|
|
'']);
|
|
CheckParserException('Expected "Identifier" at token "specialize" in file afile.pp at line 8 column 25',nParserExpectTokenError);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_NestedType;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' public type',
|
|
' TArrayEvent = reference to procedure(El: T);',
|
|
' public',
|
|
' p: TArrayEvent;',
|
|
' end;',
|
|
' TBirdWord = specialize TBird<word>;',
|
|
'var',
|
|
' b: TBirdWord;',
|
|
'begin',
|
|
' b.p:=procedure(El: word) begin end;']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_NestedRecord;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'{$modeswitch advancedrecords}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' public type TWing = record',
|
|
' s: T;',
|
|
' function GetIt: T;',
|
|
' end;',
|
|
' public',
|
|
' w: TWing;',
|
|
' end;',
|
|
' TBirdWord = specialize TBird<word>;',
|
|
'function TBird.TWing.GetIt: T;',
|
|
'begin',
|
|
'end;',
|
|
'var',
|
|
' b: TBirdWord;',
|
|
' i: word;',
|
|
'begin',
|
|
' b.w.s:=i;',
|
|
' i:=b.w.GetIt;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_NestedClass;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' public type TWing = class',
|
|
' s: T;',
|
|
' function GetIt: T;',
|
|
' end;',
|
|
' public',
|
|
' w: TWing;',
|
|
' end;',
|
|
' TBirdWord = specialize TBird<word>;',
|
|
'function TBird.TWing.GetIt: T;',
|
|
'begin',
|
|
'end;',
|
|
'var',
|
|
' b: TBirdWord;',
|
|
' i: word;',
|
|
'begin',
|
|
' b.w.s:=3;',
|
|
' i:=b.w.GetIt;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_Enums_NotPropagating;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' public type',
|
|
' TEnum = (red, blue);',
|
|
' const',
|
|
' e = blue;',
|
|
' end;',
|
|
'const',
|
|
' r = red;',
|
|
'begin']);
|
|
CheckResolverException('identifier not found "red"',nIdentifierNotFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_Self;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class',
|
|
' end;',
|
|
' generic TAnimal<T> = class end;',
|
|
' generic TBird<T> = class(specialize TAnimal<T>)',
|
|
' function GetObj: TObject;',
|
|
' procedure Fly(Obj: TObject); virtual; abstract;',
|
|
' end;',
|
|
' TProc = procedure(Obj: TObject) of object;',
|
|
' TWordBird = specialize TBird<word>;',
|
|
'function TBird.GetObj: TObject;',
|
|
'var p: TProc;',
|
|
'begin',
|
|
' Result:=Self;',
|
|
' if Self.GetObj=Result then ;',
|
|
' Fly(Self);',
|
|
' p:=@Fly;',
|
|
' p(Self);',
|
|
'end;',
|
|
'begin']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_MemberTypeConstructor;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnimal<A> = class',
|
|
' end;',
|
|
' TAnt<L> = class',
|
|
' constructor Create(A: TAnimal<L>);',
|
|
' end;',
|
|
' TBird<T> = class(TAnimal<T>)',
|
|
' type TMyAnt = TAnt<T>;',
|
|
' function Fly: TMyAnt;',
|
|
' end;',
|
|
' TWordBird = TBird<word>;',
|
|
'constructor TAnt<L>.Create(A: TAnimal<L>);',
|
|
'begin',
|
|
'end;',
|
|
'function TBird<T>.Fly: TMyAnt;',
|
|
'begin',
|
|
' Result:=TMyAnt.Create(Self);',
|
|
'end;',
|
|
'begin']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_AliasMemberType;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'{$modeswitch externalclass}',
|
|
'type',
|
|
' TObject = class end;',
|
|
'',
|
|
' generic TBird<T> = class',
|
|
' public type',
|
|
' TRun = reference to function (aValue : T) : T;',
|
|
' end;',
|
|
' TBirdWord = specialize TBird<Word>;',
|
|
' TBirdWordRun = TBirdWord.TRun;',
|
|
'',
|
|
' generic TExt<T> = class external name ''Ext''',
|
|
' public type',
|
|
' TRun = reference to function (aValue : T) : T;',
|
|
' end;',
|
|
' TExtWord = specialize TExt<Word>;',
|
|
' TExtWordRun = TExtWord.TRun;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_AccessGenericMemberTypeFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
'',
|
|
' generic TBird<T> = class',
|
|
' public type',
|
|
' TRun = reference to function (aValue : T) : T;',
|
|
' end;',
|
|
' TBirdRun = TBird.TRun;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Generics without specialization cannot be used as a type for a reference',
|
|
nGenericsWithoutSpecializationAsType);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_ReferenceTo;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TGJSPromise<T> = class',
|
|
' public type',
|
|
' TGJSPromiseResolver = reference to function (aValue : T) : T;',
|
|
' TGJSPromiseExecutor = reference to procedure (resolve,reject : TGJSPromiseResolver);',
|
|
' public',
|
|
' constructor new(Executor : TGJSPromiseExecutor);',
|
|
' end;',
|
|
'constructor TGJSPromise.new(Executor : TGJSPromiseExecutor);',
|
|
'begin',
|
|
'end;',
|
|
'',
|
|
'type',
|
|
' TJSPromise = specialize TGJSPromise<Word>;',
|
|
' TJSPromiseResolver = reference to function (aValue : Word) : Word;',
|
|
'',
|
|
' TURLLoader = Class(TObject)',
|
|
' procedure dofetch(resolve, reject: TJSPromiseResolver); virtual; abstract;',
|
|
' Function fetch : TJSPromise;',
|
|
' end;',
|
|
'function TURLLoader.fetch : TJSPromise;',
|
|
'begin',
|
|
' Result:=TJSPromise.New(@Dofetch);',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_TwoSpecsAreNotRelatedWarn;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird<T> = class F: T; end;',
|
|
' TBirdWord = TBird<Word>;',
|
|
' TBirdChar = TBird<Char>;',
|
|
'var',
|
|
' w: TBirdWord;',
|
|
' c: TBirdChar;',
|
|
'begin',
|
|
' w:=TBirdWord(c);',
|
|
'']);
|
|
ParseProgram;
|
|
CheckResolverHint(mtWarning,nClassTypesAreNotRelatedXY,'Class types "TBird<System.Char>" and "TBird<System.Word>" are not related');
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_List;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TList<T> = class',
|
|
' strict private',
|
|
' FItems: array of T;',
|
|
' function GetItems(Index: longint): T;',
|
|
' procedure SetItems(Index: longint; Value: T);',
|
|
' public',
|
|
' procedure Alter(w: T);',
|
|
' property Items[Index: longint]: T read GetItems write SetItems; default;',
|
|
' end;',
|
|
' TWordList = specialize TList<word>;',
|
|
'function TList.GetItems(Index: longint): T;',
|
|
'begin',
|
|
' Result:=FItems[Index];',
|
|
'end;',
|
|
'procedure TList.SetItems(Index: longint; Value: T);',
|
|
'begin',
|
|
' FItems[Index]:=Value;',
|
|
'end;',
|
|
'procedure TList.Alter(w: T);',
|
|
'begin',
|
|
' SetLength(FItems,length(FItems)+1);',
|
|
' Insert(w,FItems,2);',
|
|
' Delete(FItems,2,3);',
|
|
'end;',
|
|
'var l: TWordList;',
|
|
' w: word;',
|
|
'begin',
|
|
' l[1]:=w;',
|
|
' w:=l[2];']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Class_Typecast;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TList<T> = class',
|
|
' end;',
|
|
' TEagle = class;',
|
|
' TBird = class',
|
|
' FLegs: TList<TBird>;',
|
|
' property Legs: TList<TBird> read FLegs write FLegs;',
|
|
' end;',
|
|
' TEagle = class(TBird)',
|
|
' end;',
|
|
'var',
|
|
' B: TBird;',
|
|
' List: TList<TEagle>;',
|
|
'begin',
|
|
' List:=TList<TEagle>(B.Legs);',
|
|
' TList<TEagle>(B.Legs):=List;',
|
|
'',
|
|
'']);
|
|
ParseProgram;
|
|
// FPC/pas2js: Class types "TList<afile.TBird>" and "TList<afile.TEagle>" are not related
|
|
// Delphi: no warning
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ExtClass_Array;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'{$ModeSwitch externalclass}',
|
|
'type',
|
|
' NativeInt = longint;',
|
|
' TJSGenArray<T> = Class external name ''Array''',
|
|
' private',
|
|
' function GetElements(Index: NativeInt): T; external name ''[]'';',
|
|
' procedure SetElements(Index: NativeInt; const AValue: T); external name ''[]'';',
|
|
' public',
|
|
' type TSelfType = TJSGenArray<T>;',
|
|
' TArrayEvent = reference to function(El: T; Arr: TSelfType): Boolean;',
|
|
' TArrayCallback = TArrayEvent;',
|
|
' public',
|
|
' FLength : NativeInt; external name ''length'';',
|
|
' constructor new; overload;',
|
|
' constructor new(aLength : NativeInt); overload;',
|
|
' class function _of() : TSelfType; varargs; external name ''of'';',
|
|
' function every(const aCallback: TArrayCallBack): boolean; overload;',
|
|
' function fill(aValue : T) : TSelfType; overload;',
|
|
' function fill(aValue : T; aStartIndex : NativeInt) : TSelfType; overload;',
|
|
' function fill(aValue : T; aStartIndex,aEndIndex : NativeInt) : TSelfType; overload;',
|
|
' property Length : NativeInt Read FLength Write FLength;',
|
|
' property Elements[Index: NativeInt]: T read GetElements write SetElements; default;',
|
|
' end;',
|
|
' TJSWordArray = TJSGenArray<word>;',
|
|
'var',
|
|
' wa: TJSWordArray;',
|
|
' w: word;',
|
|
'begin',
|
|
' wa:=TJSWordArray.new;',
|
|
' wa:=TJSWordArray.new(3);',
|
|
' wa:=TJSWordArray._of(4,5);',
|
|
' wa:=wa.fill(7);',
|
|
' wa:=wa.fill(7,8,9);',
|
|
' w:=wa.length;',
|
|
' wa.length:=10;',
|
|
' wa[11]:=w;',
|
|
' w:=wa[12];',
|
|
' wa.every(function(El: word; Arr: TJSWordArray): Boolean',
|
|
' begin',
|
|
' end',
|
|
' );',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ExtClass_VarargsOfType;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'{$modeswitch externalclass}',
|
|
'type',
|
|
' TJSObject = class external name ''Object''',
|
|
' end;',
|
|
' generic TGJSSet<T> = class external name ''Set''',
|
|
' constructor new(aElement1: T); varargs of T; overload;',
|
|
' function bind(thisArg: TJSObject): T; varargs of T;',
|
|
' end;',
|
|
' TJSWordSet = specialize TGJSSet<word>;',
|
|
'var',
|
|
' s: TJSWordSet;',
|
|
' w: word;',
|
|
'begin',
|
|
' s:=TJSWordSet.new(3);',
|
|
' s:=TJSWordSet.new(3,5);',
|
|
' w:=s.bind(nil);',
|
|
' w:=s.bind(nil,6);',
|
|
' w:=s.bind(nil,7,8);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassInterface;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' {$interfaces corba}',
|
|
' generic ICorbaIntf<T> = interface',
|
|
' procedure Fly(a: T);',
|
|
' end;',
|
|
' {$interfaces com}',
|
|
' IUnknown = interface',
|
|
' end;',
|
|
' IInterface = IUnknown;',
|
|
' generic IComIntf<T> = interface',
|
|
' procedure Run(b: T);',
|
|
' end;',
|
|
'begin']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ClassInterface_Method;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' {$interfaces corba}',
|
|
' generic IBird<T> = interface',
|
|
' procedure Fly(a: T);',
|
|
' end;',
|
|
' TObject = class end;',
|
|
' generic TBird<U> = class(specialize IBird<U>)',
|
|
' procedure Fly(a: U);',
|
|
' end;',
|
|
'procedure TBird.Fly(a: U);',
|
|
'begin',
|
|
'end;',
|
|
'var b: specialize IBird<word>;',
|
|
'begin',
|
|
' b.Fly(3);']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_DynArray;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' generic TArray<T> = array of T;',
|
|
' TWordArray = specialize TArray<word>;',
|
|
'var',
|
|
' a: specialize TArray<word>;',
|
|
' b: TWordArray;',
|
|
' w: word;',
|
|
'begin',
|
|
' a[1]:=2;',
|
|
' b[2]:=a[3]+b[4];',
|
|
' a:=b;',
|
|
' b:=a;',
|
|
' SetLength(a,5);',
|
|
' SetLength(b,6);',
|
|
' w:=length(a)+low(a)+high(a);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_StaticArray;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' generic TBird<T> = array[T] of word;',
|
|
' TByteBird = specialize TBird<byte>;',
|
|
'var',
|
|
' a: specialize TBird<byte>;',
|
|
' b: TByteBird;',
|
|
' i: byte;',
|
|
'begin',
|
|
' a[1]:=2;',
|
|
' b[2]:=a[3]+b[4];',
|
|
' a:=b;',
|
|
' b:=a;',
|
|
' i:=low(a);',
|
|
' i:=high(a);',
|
|
' for i in a do ;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Array_Anoynmous;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' generic TRec<T> = record',
|
|
' a: array of T;',
|
|
' end;',
|
|
' TWordRec = specialize TRec<word>;',
|
|
'var',
|
|
' a: specialize TRec<word>;',
|
|
' b: TWordRec;',
|
|
' w: word;',
|
|
'begin',
|
|
' a:=b;',
|
|
' a.a:=b.a;',
|
|
' a.a[1]:=2;',
|
|
' b.a[2]:=a.a[3]+b.a[4];',
|
|
' b:=a;',
|
|
' SetLength(a.a,5);',
|
|
' SetLength(b.a,6);',
|
|
' w:=length(a.a)+low(a.a)+high(a.a);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ProcType;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' generic TFunc<T> = function(v: T): T;',
|
|
' TWordFunc = specialize TFunc<word>;',
|
|
'function GetIt(w: word): word;',
|
|
'begin',
|
|
'end;',
|
|
'var',
|
|
' a: specialize TFunc<word>;',
|
|
' b: TWordFunc;',
|
|
' w: word;',
|
|
'begin',
|
|
' a:=nil;',
|
|
' b:=nil;',
|
|
' a:=b;',
|
|
' b:=a;',
|
|
' w:=a(w);',
|
|
' w:=b(w);',
|
|
' a:=@GetIt;',
|
|
' b:=@GetIt;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_ProcType_AnonymousFunc_Delphi;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class',
|
|
' end;',
|
|
' IInterface = interface',
|
|
' end;',
|
|
' Integer = longint;',
|
|
' IComparer<T> = interface',
|
|
' function Compare(const Left, Right: T): Integer; overload;',
|
|
' end;',
|
|
' TOnComparison<T> = function(const Left, Right: T): Integer of object;',
|
|
' TComparisonFunc<T> = reference to function(const Left, Right: T): Integer;',
|
|
' TComparer<T> = class(TObject, IComparer<T>)',
|
|
' public',
|
|
' function Compare(const Left, Right: T): Integer; overload;',
|
|
' class function Construct(const AComparison: TOnComparison<T>): IComparer<T>; overload;',
|
|
' class function Construct(const AComparison: TComparisonFunc<T>): IComparer<T>; overload;',
|
|
' end;',
|
|
'function TComparer<T>.Compare(const Left, Right: T): Integer; overload;',
|
|
'begin',
|
|
'end;',
|
|
'class function TComparer<T>.Construct(const AComparison: TOnComparison<T>): IComparer<T>;',
|
|
'begin',
|
|
'end;',
|
|
'class function TComparer<T>.Construct(const AComparison: TComparisonFunc<T>): IComparer<T>;',
|
|
'begin',
|
|
'end;',
|
|
'procedure Test;',
|
|
'var',
|
|
' aComparer : IComparer<Integer>;',
|
|
'begin',
|
|
' aComparer:=TComparer<Integer>.Construct(function (Const a,b : integer) : integer',
|
|
' begin',
|
|
' Result:=a-b;',
|
|
' end);',
|
|
'end;',
|
|
'begin',
|
|
' Test;']);
|
|
ParseModule;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_PointerDirectSpecializeFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' generic TRec<T> = record v: T; end;',
|
|
' PRec = ^specialize TRec<word>;',
|
|
'begin',
|
|
'']);
|
|
CheckParserException('Expected "Identifier or file"',nParserExpectTokenError);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_HelperForArray;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$ModeSwitch typehelpers}',
|
|
'type',
|
|
' generic TArr<T> = array[1..2] of T;',
|
|
' TWordArrHelper = type helper for specialize TArr<word>',
|
|
' procedure Fly(w: word);',
|
|
' end;',
|
|
'procedure TWordArrHelper.Fly(w: word);',
|
|
'begin',
|
|
'end;',
|
|
'var',
|
|
' a: specialize TArr<word>;',
|
|
'begin',
|
|
' a.Fly(3);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_LocalVar;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<{#Templ}T> = class',
|
|
' function Fly(p:T): T;',
|
|
' end;',
|
|
'function TBird.Fly(p:T): T;',
|
|
'var l: T;',
|
|
'begin',
|
|
' l:=p;',
|
|
' p:=l;',
|
|
' Result:=p;',
|
|
' Result:=l;',
|
|
' l:=Result;',
|
|
'end;',
|
|
'var',
|
|
' b: specialize TBird<word>;',
|
|
' w: word;',
|
|
'begin',
|
|
' w:=b.Fly(w);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Statements;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<{#Templ}T> = class',
|
|
' function Fly(p:T): T;',
|
|
' end;',
|
|
'function TBird.Fly(p:T): T;',
|
|
'var',
|
|
' v1,v2,v3:T;',
|
|
'begin',
|
|
' v1:=1;',
|
|
' v2:=v1+v1*v1+v1 div p;',
|
|
' v3:=-v1;',
|
|
' repeat',
|
|
' v1:=v1+1;',
|
|
' until v1>=5;',
|
|
' while v1>=0 do',
|
|
' v1:=v1-v2;',
|
|
' for v1:=v2 to v3 do v2:=v1;',
|
|
' if v1<v2 then v3:=v1 else v3:=v2;',
|
|
' if v1<v2 then else ;',
|
|
' case v1 of',
|
|
' 1: v3:=3;',
|
|
' end;',
|
|
'end;',
|
|
'var',
|
|
' b: specialize TBird<word>;',
|
|
'begin',
|
|
' b.Fly(2);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_InlineSpecializeExpr;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' constructor Create;',
|
|
' end;',
|
|
' generic TAnt<U> = class',
|
|
' constructor Create;',
|
|
' end;',
|
|
'constructor TBird.Create;',
|
|
'var',
|
|
' a: specialize TAnt<T>;',
|
|
' b: specialize TAnt<word>;',
|
|
'begin',
|
|
' a:=specialize TAnt<T>.create;',
|
|
' b:=specialize TAnt<word>.create;',
|
|
'end;',
|
|
'constructor TAnt.Create;',
|
|
'var',
|
|
' i: specialize TBird<U>;',
|
|
' j: specialize TBird<word>;',
|
|
' k: specialize TAnt<U>;',
|
|
'begin',
|
|
' i:=specialize TBird<U>.create;',
|
|
' j:=specialize TBird<word>.create;',
|
|
' k:=specialize TAnt<U>.create;',
|
|
'end;',
|
|
'var a: specialize TAnt<word>;',
|
|
'begin',
|
|
' a:=specialize TAnt<word>.create;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_TryExcept;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<{#Templ}T> = class',
|
|
' function Fly(p:T): T;',
|
|
' end;',
|
|
' Exception = class',
|
|
' end;',
|
|
' generic EMsg<T> = class',
|
|
' Msg: T;',
|
|
' end;',
|
|
'function TBird.Fly(p:T): T;',
|
|
'var',
|
|
' v1,v2,v3:T;',
|
|
'begin',
|
|
' try',
|
|
' finally',
|
|
' end;',
|
|
' try',
|
|
' v1:=v2;',
|
|
' finally',
|
|
' v2:=v1;',
|
|
' end;',
|
|
' try',
|
|
' except',
|
|
' on Exception do ;',
|
|
' on E: Exception do ;',
|
|
' on E: specialize EMsg<boolean> do E.Msg:=true;',
|
|
' on E: specialize EMsg<T> do E.Msg:=1;',
|
|
' end;',
|
|
'end;',
|
|
'var',
|
|
' b: specialize TBird<word>;',
|
|
'begin',
|
|
' b.Fly(2);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_Call;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' function Fly(p:T): T;',
|
|
' end;',
|
|
'procedure Run(b: boolean); overload;',
|
|
'begin end;',
|
|
'procedure Run(w: word); overload;',
|
|
'begin end;',
|
|
'function TBird.Fly(p:T): T;',
|
|
'begin',
|
|
' Run(p);',
|
|
' Run(Result);',
|
|
'end;',
|
|
'var',
|
|
' w: specialize TBird<word>;',
|
|
' b: specialize TBird<boolean>;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGen_NestedProc;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' generic TBird<T> = class',
|
|
' function Fly(p:T): T;',
|
|
' end;',
|
|
'function TBird.Fly(p:T): T;',
|
|
' function Run: T;',
|
|
' begin',
|
|
' Fly:=Result;',
|
|
' end;',
|
|
'begin',
|
|
' Run;',
|
|
'end;',
|
|
'var',
|
|
' w: specialize TBird<word>;',
|
|
' b: specialize TBird<boolean>;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Function;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function DoIt<T>(a: T): T;',
|
|
'var i: T;',
|
|
'begin',
|
|
' a:=i;',
|
|
' Result:=a;',
|
|
'end;',
|
|
'var w: word;',
|
|
'begin',
|
|
' w:=specialize DoIt<word>(3);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_FunctionDelphi;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'function DoIt<T>(a: T): T;',
|
|
'var i: T;',
|
|
'begin',
|
|
' a:=i;',
|
|
' Result:=a;',
|
|
'end;',
|
|
'var w: word;',
|
|
'begin',
|
|
' w:=DoIt<word>(3);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_OverloadDuplicate;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic procedure Fly<T>(a: T);',
|
|
'begin',
|
|
'end;',
|
|
'generic procedure Fly<T>(a: T);',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Duplicate identifier "Fly" at afile.pp(2,22)',nDuplicateIdentifier);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_MissingTemplatesFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic procedure Run;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckParserException('Expected "<"',nParserExpectTokenError);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_SpecializeNonGenericFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'procedure Run;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' specialize Run<word>();',
|
|
'']);
|
|
CheckResolverException('Run expected, but Run<> found',nXExpectedButYFound);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Forward;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic procedure Fly<T>(a: T); forward;',
|
|
'procedure Run;',
|
|
'begin',
|
|
' specialize Fly<word>(3);',
|
|
'end;',
|
|
'generic procedure Fly<T>(a: T);',
|
|
'var i: T;',
|
|
'begin',
|
|
' i:=a;',
|
|
'end;',
|
|
'begin',
|
|
' specialize Fly<boolean>(true);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_External;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function Fly<T>(a: T): T; external name ''flap'';',
|
|
'procedure Run;',
|
|
'begin',
|
|
' specialize Fly<word>(3);',
|
|
'end;',
|
|
'begin',
|
|
' specialize Fly<boolean>(true);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_UnitIntf;
|
|
begin
|
|
AddModuleWithIntfImplSrc('unit2.pas',
|
|
LinesToStr([
|
|
'generic function Fly<T>(a: T): T;',
|
|
'']),
|
|
LinesToStr([
|
|
'generic function Fly<T>(a: T): T;',
|
|
'var i: T;',
|
|
'begin',
|
|
' i:=a;',
|
|
'end;',
|
|
'']));
|
|
StartProgram(true);
|
|
Add([
|
|
'uses unit2;',
|
|
'var w: word;',
|
|
'begin',
|
|
' w:=specialize Fly<word>(3);',
|
|
' if specialize Fly<boolean>(false) then ;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_BackRef1Fail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function Fly<T>(a: Fly): T;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Wrong number of parameters specified for call to "function Fly<>(untyped)"',nWrongNumberOfParametersForCallTo);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_BackRef2Fail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function Fly<T>(a: Fly<word>): T;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Wrong number of parameters specified for call to "function Fly<>(untyped)"',nWrongNumberOfParametersForCallTo);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_BackRef3Fail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function Fly<T>(a: Fly<T>): T;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Wrong number of parameters specified for call to "function Fly<>(untyped)"',nWrongNumberOfParametersForCallTo);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_CallSelf;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function Fly<T>(a: T): T;',
|
|
' procedure Run;',
|
|
' begin',
|
|
' specialize Fly<T>(a);',
|
|
' specialize Fly<word>(3);',
|
|
' end;',
|
|
'begin',
|
|
' specialize Fly<T>(a);',
|
|
' specialize Fly<boolean>(true);',
|
|
'end;',
|
|
'begin',
|
|
' specialize Fly<string>(''fast'');',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_CallSelfNoParams;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function Fly<T>(a: T = 0): T;',
|
|
' procedure Run;',
|
|
' begin',
|
|
' specialize Fly<T>;',
|
|
' specialize Fly<word>;',
|
|
' end;',
|
|
'begin',
|
|
' specialize Fly<T>;',
|
|
' specialize Fly<byte>;',
|
|
'end;',
|
|
'begin',
|
|
' specialize Fly<shortint>;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_ForwardConstraints;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird = class end;',
|
|
'var b: TBird;',
|
|
'generic function Fly<T: class>(a: T): T; forward;',
|
|
'procedure Run;',
|
|
'begin',
|
|
' specialize Fly<TBird>(b);',
|
|
'end;',
|
|
'generic function Fly<T>(a: T): T;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' specialize Fly<TBird>(b);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_ForwardConstraintsRepeatFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class end;',
|
|
'generic function Fly<T: class>(a: T): T; forward;',
|
|
'generic function Fly<T: class>(a: T): T;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException(sImplMustNotRepeatConstraints,nImplMustNotRepeatConstraints);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_ForwardTempNameMismatch;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function Fly<T>(a: T): T; forward;',
|
|
'generic function Fly<B>(a: B): B;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Declaration of "Fly<B>" differs from previous declaration at afile.pp(2,23)',
|
|
nDeclOfXDiffersFromPrevAtY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_ForwardOverload;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic function {#FlyA}Fly<T>(a: T; b: boolean): T; forward; overload;',
|
|
'generic function {#FlyB}Fly<T>(a: T; w: word): T; forward; overload;',
|
|
'procedure {#FlyC}Fly; overload;',
|
|
'begin',
|
|
' specialize {@FlyA}Fly<longint>(1,true);',
|
|
' specialize {@FlyB}Fly<string>(''ABC'',3);',
|
|
'end;',
|
|
'generic function Fly<T>(a: T; b: boolean): T;',
|
|
'begin',
|
|
'end;',
|
|
'generic function Fly<T>(a: T; w: word): T;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_NestedFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'procedure Fly;',
|
|
' generic procedure Run<T>(a: T);',
|
|
' begin',
|
|
' end;',
|
|
'begin',
|
|
' Run<boolean>(true);',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Type parameters not allowed on nested procedure',nTypeParamsNotAllowedOnX);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_TypeParamCntOverload;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic procedure {#A}Run<T>(a: T);',
|
|
'begin',
|
|
'end;',
|
|
'generic procedure {#B}Run<M,N>(a: M);',
|
|
'begin',
|
|
' specialize {@A}Run<M>(a);',
|
|
' specialize {@B}Run<double,char>(1.3);',
|
|
'end;',
|
|
'begin',
|
|
' specialize {@A}Run<word>(3);',
|
|
' specialize {@B}Run<word,char>(4);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_TypeParamCntOverloadNoParams;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'generic procedure {#A}Run<T>;',
|
|
'begin',
|
|
'end;',
|
|
'generic procedure {#B}Run<M,N>;',
|
|
'begin',
|
|
' specialize {@A}Run<M>;',
|
|
' specialize {@A}Run<M>();',
|
|
' specialize {@B}Run<double,char>;',
|
|
' specialize {@B}Run<double,char>();',
|
|
'end;',
|
|
'begin',
|
|
' specialize {@A}Run<word>;',
|
|
' specialize {@A}Run<word>();',
|
|
' specialize {@B}Run<word,char>;',
|
|
' specialize {@B}Run<word,char>();',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_TypeParamWithDefaultParamDelphiFail;
|
|
begin
|
|
// delphi 10.3 does not allow default values for args with generic types
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'procedure {#A}Run<T>(a: T = 0); overload;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException(sParamOfThisTypeCannotHaveDefVal,nParamOfThisTypeCannotHaveDefVal);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_ParamSpecWithT;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TBird<T> = class v: T; end;',
|
|
' TAnt = class',
|
|
' procedure Func<T: class>(Bird: TBird<T>);',
|
|
' end;',
|
|
'procedure TAnt.Func<T>(Bird: TBird<T>);',
|
|
'begin',
|
|
'end;',
|
|
'var',
|
|
' Ant: TAnt;',
|
|
' Bird: TBird<TObject>;',
|
|
' BirdOfBird: TBird<TBird<TObject>>;',
|
|
'begin',
|
|
' Ant.Func<TObject>(Bird);',
|
|
' Ant.Func<TBird<TObject>>(BirdOfBird);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_NeedExplicitFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'function {#A}Run<S,T>(a: S): T; overload;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' {@A}Run(1);',
|
|
'']);
|
|
CheckResolverException('Could not infer generic type argument "T" for method "Run"',
|
|
nCouldNotInferTypeArgXForMethodY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_Overload;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'procedure {#A}Run<S>(a: S; b: boolean); overload;',
|
|
'begin',
|
|
'end;',
|
|
'procedure {#B}Run<T>(a: T; w: word); overload;',
|
|
'begin',
|
|
'end;',
|
|
'procedure {#C}Run<U>(a: U; b: U); overload;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' {@A}Run(1,true);', // non generic take precedence
|
|
' {@B}Run(2,word(3));', // non generic take precedence
|
|
' {@C}Run(''foo'',''bar'');',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_OverloadForward;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'procedure {#A}Run<S>(a: S; b: boolean); forward; overload;',
|
|
'procedure {#B}Run<T>(a: T; w: word); forward; overload;',
|
|
'procedure {#C}Run<U>(a: U; b: U); forward; overload;',
|
|
'procedure {#A2}Run<S>(a: S; b: boolean); overload;',
|
|
'begin',
|
|
' {@A}Run(1,true);', // non generic take precedence
|
|
' {@B}Run(2,word(3));', // non generic take precedence
|
|
' {@C}Run(''foo'',''bar'');',
|
|
'end;',
|
|
'procedure {#B2}Run<T>(a: T; w: word); overload;',
|
|
'begin',
|
|
'end;',
|
|
'procedure {#C2}Run<U>(a: U; b: U); overload;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' {@A}Run(1,true);', // non generic take precedence
|
|
' {@B}Run(2,word(3));', // non generic take precedence
|
|
' {@C}Run(''foo'',''bar'');',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_Var_Overload;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'procedure {#A}Run<S>(var a: S; var b: boolean); overload;',
|
|
'begin',
|
|
'end;',
|
|
'procedure {#B}Run<T>(var a: T; var w: word); overload;',
|
|
'begin',
|
|
'end;',
|
|
'procedure {#C}Run<U>(var a: U; var b: U); overload;',
|
|
'begin',
|
|
'end;',
|
|
'var',
|
|
' w: word;',
|
|
' b: boolean;',
|
|
' s: string;',
|
|
'begin',
|
|
' {@A}Run(w,b);',
|
|
' {@B}Run(s,w);',
|
|
' {@C}Run(s,s);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_Widen;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'procedure {#A}Run<S>(a: S; b: S);',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' {@A}Run(word(1),longint(2));',
|
|
' {@A}Run(int64(1),longint(2));',
|
|
' {@A}Run(boolean(false),wordbool(2));',
|
|
' {@A}Run(''a'',''foo'');',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_DefaultValue;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'{$modeswitch implicitfunctionspecialization}',
|
|
'generic procedure {#A}Run<S>(a: S = 2; b: S = 10); overload;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' {@A}Run(1,2);',
|
|
' {@A}Run(3);',
|
|
' {@A}Run();',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_DefaultValueMismatch;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'{$modeswitch implicitfunctionspecialization}',
|
|
'generic procedure {#A}Run<S>(a: S; b: S = 10); overload;',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' {@A}Run(false,true);',
|
|
'']);
|
|
CheckResolverException('Incompatible types: got "Longint" expected "Boolean"',
|
|
nIncompatibleTypesGotExpected);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_ProcT;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TProc<S> = reference to procedure(a: S);',
|
|
' TObject = class',
|
|
' procedure {#A}Run<T: class>(a: TProc<T>);',
|
|
' end;',
|
|
' TBird = class end;',
|
|
'procedure Tobject.Run<T>(a: TProc<T>);',
|
|
'begin',
|
|
'end;',
|
|
'var obj: TObject;',
|
|
'begin',
|
|
' obj.{@A}Run<TBird>(procedure(Bird: TBird) begin end);',
|
|
//' obj.{@A}Run(procedure(Bird: TBird) begin end);', // not supported by Delphi
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_Mismatch;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'procedure Run<T>(a: T; b: T);',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
' Run(1,true);',
|
|
'']);
|
|
CheckResolverException('Inferred type "T" from different arguments mismatch for method "Run"',
|
|
nInferredTypeXFromDiffArgsMismatchFromMethodY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_ArrayOfT;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'procedure Run<T>(a: array of T);',
|
|
'var b: T;',
|
|
'begin',
|
|
' b:=3;',
|
|
'end;',
|
|
'var Arr: array of byte;',
|
|
'begin',
|
|
' Run(Arr);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_PassAsArgDelphi;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'function Run<T>(a: T): T;',
|
|
'var b: T;',
|
|
'begin',
|
|
' Run(Run<word>(3));',
|
|
' Run(Run(4));',
|
|
'end;',
|
|
'begin',
|
|
' Run(Run<word>(5));',
|
|
' Run(Run(6));',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenProc_Infer_PassAsArgObjFPC;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode objfpc}',
|
|
'{$ModeSwitch implicitfunctionspecialization}',
|
|
'generic function Run<T>(a: T): T;',
|
|
'var b: T;',
|
|
'begin',
|
|
' Run(specialize Run<word>(3));',
|
|
' Run(Run(4));',
|
|
'end;',
|
|
'begin',
|
|
' Run(specialize Run<word>(5));',
|
|
' Run(Run(6));',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_VirtualFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class',
|
|
' generic procedure Run<T>(a: T); virtual; abstract;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('virtual, dynamic or message methods cannot have type parameters',
|
|
nXMethodsCannotHaveTypeParams);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_PublishedFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class',
|
|
' published',
|
|
' generic procedure Run<T>(a: T);',
|
|
' end;',
|
|
'generic procedure TObject.Run<T>(a: T);',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('published methods cannot have type parameters',
|
|
nXMethodsCannotHaveTypeParams);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_ClassInterfaceMethodFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' IUnknown = interface',
|
|
' generic procedure Run<T>(a: T); virtual; abstract;',
|
|
' end;',
|
|
'begin',
|
|
'']);
|
|
CheckParserException('generic is not allowed in interface',nParserXNotAllowedInY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_ClassConstructorFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class',
|
|
' generic class constructor Run<T>(a: T);',
|
|
' end;',
|
|
'generic class constructor TObject.Run<T>(a: T);',
|
|
'begin end;',
|
|
'begin',
|
|
'']);
|
|
CheckParserException('Expected "Procedure" or "Function" at token "constructor" in file afile.pp at line 4 column 19',
|
|
nParserExpectToken2Error);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_TemplNameDifferFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class',
|
|
' generic procedure Run<T>(a: T);',
|
|
' end;',
|
|
'generic procedure TObject.Run<S>(a: S);',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException('Declaration of "TObject.Run<S>" differs from previous declaration at afile.pp(4,28)',
|
|
nDeclOfXDiffersFromPrevAtY);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_ImplConstraintFail;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class',
|
|
' generic procedure Run<T>(a: T);',
|
|
' end;',
|
|
'generic procedure TObject.Run<T: class>(a: T);',
|
|
'begin',
|
|
'end;',
|
|
'begin',
|
|
'']);
|
|
CheckResolverException(sImplMustNotRepeatConstraints,nImplMustNotRepeatConstraints);
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_NestedSelf;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class',
|
|
' w: word;',
|
|
' generic function Fly<T>(a: T): T;',
|
|
' end;',
|
|
'generic function TObject.Fly<T>(a: T): T;',
|
|
' function Sub: T;',
|
|
' begin',
|
|
' Result:=w+a;',
|
|
' Result:=Self.w+a;',
|
|
//' specialize Fly<T> :=', not supported by FPC/Delphi
|
|
' end;',
|
|
'begin',
|
|
' Result:=Sub;',
|
|
' Result:=Self.w+Sub+a;',
|
|
'end;',
|
|
'var Obj: TObject;',
|
|
'begin',
|
|
' if Obj.specialize Fly<smallint>(3)=4 then ;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_OverloadTypeParamCntObjFPC;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class',
|
|
' generic procedure {#A}Run<T>(a: T);',
|
|
' generic procedure {#B}Run<M,N>(a: M);',
|
|
' end;',
|
|
'generic procedure TObject.Run<T>(a: T);',
|
|
'begin',
|
|
'end;',
|
|
'generic procedure TObject.Run<M,N>(a: M);',
|
|
'begin',
|
|
' specialize {@A}Run<M>(a);',
|
|
' specialize {@B}Run<double,char>(1.3);',
|
|
'end;',
|
|
'var obj: TObject;',
|
|
'begin',
|
|
' obj.specialize {@A}Run<word>(3);',
|
|
' obj.specialize {@B}Run<word,char>(4);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_OverloadTypeParamCntDelphi;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'type',
|
|
' TObject = class',
|
|
' procedure {#A}Run<T>(a: T); overload;',
|
|
' procedure {#B}Run<M,N>(a: M); overload;',
|
|
' end;',
|
|
'procedure TObject.Run<T>(a: T);',
|
|
'begin',
|
|
'end;',
|
|
'procedure TObject.Run<M,N>(a: M);',
|
|
'begin',
|
|
' {@A}Run<M>(a);',
|
|
' {@B}Run<double,char>(1.3);',
|
|
'end;',
|
|
'var obj: TObject;',
|
|
'begin',
|
|
' obj.{@A}Run<word>(3);',
|
|
' obj.{@B}Run<word,char>(4);',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_OverloadArgs;
|
|
begin
|
|
StartProgram(false);
|
|
Add([
|
|
'type',
|
|
' TObject = class',
|
|
' generic function {#A}Run<T>(a: boolean): T;',
|
|
' generic function {#B}Run<M>(a: word): M;',
|
|
' end;',
|
|
'generic function TObject.Run<T>(a: boolean): T;',
|
|
'begin',
|
|
'end;',
|
|
'generic function TObject.Run<M>(a: word): M;',
|
|
'begin',
|
|
' Result:=specialize Run<M>(a);',
|
|
' if specialize {@A}Run<string>(true)=''foo'' then ;',
|
|
' if specialize {@B}Run<byte>(3)=4 then ;',
|
|
'end;',
|
|
'var obj: TObject;',
|
|
'begin',
|
|
' if obj.specialize {@A}Run<string>(true)=''bar'' then ;',
|
|
' if obj.specialize {@B}Run<byte>(5)=6 then ;',
|
|
'']);
|
|
ParseProgram;
|
|
end;
|
|
|
|
procedure TTestResolveGenerics.TestGenMethod_TypeCastParam;
|
|
begin
|
|
StartUnit(false);
|
|
Add([
|
|
'{$mode delphi}',
|
|
'interface',
|
|
'type',
|
|
' TObject = class end;',
|
|
' TAnt = class end;',
|
|
' TArray<T> = array of T;',
|
|
' TBird = class',
|
|
' F: TArray<TObject>;',
|
|
' procedure Run<S>(a: TArray<S>);',
|
|
' end;',
|
|
'implementation',
|
|
'procedure TBird.Run<S>(a: TArray<S>);',
|
|
'begin',
|
|
' a:=TArray<S>(a);',
|
|
' F:=TArray<TObject>(a);',
|
|
'end;',
|
|
'var B: TBird;',
|
|
'initialization',
|
|
' B.Run<TAnt>(nil);',
|
|
'']);
|
|
ParseUnit;
|
|
end;
|
|
|
|
initialization
|
|
RegisterTests([TTestResolveGenerics]);
|
|
|
|
end.
|
|
|