tcgenerics.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. unit TCGenerics;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, fpcunit, pastree, testregistry, pscanner, tctypeparser;
  6. Type
  7. { TTestGenerics - for resolver see unit tcresolvegenerics }
  8. TTestGenerics = Class(TBaseTestTypeParser)
  9. Published
  10. // generic types
  11. Procedure TestObjectGenerics;
  12. Procedure TestRecordGenerics;
  13. Procedure TestArrayGenerics;
  14. Procedure TestArrayGenericsDelphi;
  15. Procedure TestProcTypeGenerics;
  16. Procedure TestDeclarationDelphi;
  17. Procedure TestDeclarationFPC;
  18. Procedure TestDeclarationFPCNoSpaces;
  19. Procedure TestMethodImplementation;
  20. // generic constraints
  21. Procedure TestGenericConstraint;
  22. Procedure TestGenericInterfaceConstraint;
  23. Procedure TestDeclarationConstraint;
  24. // specialize type
  25. Procedure TestSpecializationDelphi;
  26. Procedure TestDeclarationDelphiSpecialize;
  27. Procedure TestInlineSpecializationInArgument;
  28. Procedure TestSpecializeNested;
  29. Procedure TestInlineSpecializeInStatement;
  30. Procedure TestInlineSpecializeInStatementDelphi;
  31. // generic functions
  32. Procedure TestGenericFunction_Program;
  33. Procedure TestGenericFunction_Unit;
  34. // generic method
  35. Procedure TestGenericMethod_Program;
  36. Procedure TestGenericMethod_OverloadDelphi;
  37. end;
  38. implementation
  39. procedure TTestGenerics.TestObjectGenerics;
  40. begin
  41. Add([
  42. 'Type',
  43. 'Generic TSomeClass<T> = Object',
  44. ' b : T;',
  45. 'end;',
  46. '']);
  47. ParseDeclarations;
  48. end;
  49. procedure TTestGenerics.TestRecordGenerics;
  50. begin
  51. Add([
  52. 'Type',
  53. ' Generic TSome<T> = Record',
  54. ' b : T;',
  55. ' end;',
  56. '']);
  57. ParseDeclarations;
  58. end;
  59. procedure TTestGenerics.TestArrayGenerics;
  60. begin
  61. Add([
  62. 'Type',
  63. ' Generic TSome<T> = array of T;',
  64. ' Generic TStatic<R,T> = array[R] of T;',
  65. '']);
  66. ParseDeclarations;
  67. end;
  68. procedure TTestGenerics.TestArrayGenericsDelphi;
  69. begin
  70. Add([
  71. '{$mode delphi}',
  72. 'Type',
  73. ' TSome<T> = array of T;',
  74. ' TStatic<R,T> = array[R] of T;',
  75. '']);
  76. ParseDeclarations;
  77. end;
  78. procedure TTestGenerics.TestProcTypeGenerics;
  79. begin
  80. Add([
  81. 'Type',
  82. ' Generic TSome<T> = procedure(v: T);',
  83. ' Generic TFunc<R,T> = function(b: R): T;',
  84. '']);
  85. ParseDeclarations;
  86. end;
  87. procedure TTestGenerics.TestDeclarationDelphi;
  88. Var
  89. T : TPasClassType;
  90. begin
  91. Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches ;
  92. Source.Add('Type');
  93. Source.Add(' TSomeClass<T,T2> = Class(TObject)');
  94. Source.Add(' b : T;');
  95. Source.Add(' b2 : T2;');
  96. Source.Add(' FItems: ^TArray<T>;');
  97. Source.Add(' type');
  98. Source.Add(' TDictionaryEnumerator = TDictionary<T, TEmptyRecord>.TKeyEnumerator;');
  99. Source.Add(' end;');
  100. ParseDeclarations;
  101. AssertNotNull('have generic definition',Declarations.Classes);
  102. AssertEquals('have generic definition',1,Declarations.Classes.Count);
  103. AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType);
  104. T:=TPasClassType(Declarations.Classes[0]);
  105. AssertNotNull('have generic templates',T.GenericTemplateTypes);
  106. AssertEquals('2 template types',2,T.GenericTemplateTypes.Count);
  107. AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent);
  108. AssertSame('Parent 1 is class',T,TPasElement(T.GenericTemplateTypes[1]).Parent);
  109. end;
  110. procedure TTestGenerics.TestDeclarationFPC;
  111. Var
  112. T : TPasClassType;
  113. begin
  114. Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches;
  115. Source.Add('Type');
  116. Source.Add(' TSomeClass<T;T2> = Class(TObject)');
  117. Source.Add(' b : T;');
  118. Source.Add(' b2 : T2;');
  119. Source.Add(' end;');
  120. ParseDeclarations;
  121. AssertNotNull('have generic definition',Declarations.Classes);
  122. AssertEquals('have generic definition',1,Declarations.Classes.Count);
  123. AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType);
  124. T:=TPasClassType(Declarations.Classes[0]);
  125. AssertNotNull('have generic templates',T.GenericTemplateTypes);
  126. AssertEquals('2 template types',2,T.GenericTemplateTypes.Count);
  127. AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent);
  128. AssertSame('Parent 1 is class',T,TPasElement(T.GenericTemplateTypes[1]).Parent);
  129. end;
  130. procedure TTestGenerics.TestDeclarationFPCNoSpaces;
  131. Var
  132. T : TPasClassType;
  133. begin
  134. Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches;
  135. Source.Add('Type');
  136. Source.Add(' TSomeClass<T;T2>=Class(TObject)');
  137. Source.Add(' b : T;');
  138. Source.Add(' b2 : T2;');
  139. Source.Add(' end;');
  140. ParseDeclarations;
  141. AssertNotNull('have generic definition',Declarations.Classes);
  142. AssertEquals('have generic definition',1,Declarations.Classes.Count);
  143. AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType);
  144. T:=TPasClassType(Declarations.Classes[0]);
  145. AssertNotNull('have generic templates',T.GenericTemplateTypes);
  146. AssertEquals('2 template types',2,T.GenericTemplateTypes.Count);
  147. AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent);
  148. AssertSame('Parent 1 is class',T,TPasElement(T.GenericTemplateTypes[1]).Parent);
  149. end;
  150. procedure TTestGenerics.TestMethodImplementation;
  151. begin
  152. With source do
  153. begin
  154. Add('unit afile;');
  155. Add('{$MODE DELPHI}');
  156. Add('interface');
  157. Add('type');
  158. Add(' TTest<T> = object');
  159. Add(' procedure foo(v:T);');
  160. Add(' procedure bar<Y>(v:T);');
  161. Add(' type');
  162. Add(' TSub = class');
  163. Add(' procedure DoIt<Y>(v:T);');
  164. Add(' end;');
  165. Add(' end;');
  166. Add('implementation');
  167. Add('procedure TTest<T>.foo;');
  168. Add('begin');
  169. Add('end;');
  170. Add('procedure TTest<T>.bar<Y>;');
  171. Add('begin');
  172. Add('end;');
  173. Add('procedure TTest<T>.TSub.DoIt<Y>;');
  174. Add('begin');
  175. Add('end;');
  176. end;
  177. ParseModule;
  178. end;
  179. procedure TTestGenerics.TestGenericConstraint;
  180. begin
  181. Add([
  182. 'Type',
  183. 'Generic TSomeClass<T: TObject> = class',
  184. ' b : T;',
  185. 'end;',
  186. 'Generic TBird<T: class> = class',
  187. ' c : specialize TBird<T>;',
  188. 'end;',
  189. 'Generic TEagle<T: record> = class',
  190. 'end;',
  191. 'Generic TEagle<T: constructor> = class',
  192. 'end;',
  193. '']);
  194. ParseDeclarations;
  195. end;
  196. procedure TTestGenerics.TestGenericInterfaceConstraint;
  197. begin
  198. Add([
  199. 'Type',
  200. 'TIntfA = interface end;',
  201. 'TIntfB = interface end;',
  202. 'TBird = class(TInterfacedObject,TIntfA,TIntfB) end;',
  203. 'Generic TAnt<T: TIntfA, TIntfB> = class',
  204. ' b: T;',
  205. ' c: specialize TAnt<T>;',
  206. 'end;',
  207. 'Generic TFly<T: TIntfA, TIntfB; S> = class',
  208. ' b: S;',
  209. ' c: specialize TFly<T>;',
  210. 'end;',
  211. '']);
  212. ParseDeclarations;
  213. end;
  214. procedure TTestGenerics.TestDeclarationConstraint;
  215. Var
  216. T : TPasClassType;
  217. begin
  218. Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches ;
  219. Source.Add('Type');
  220. Source.Add(' TSomeClass<T: T2> = Class(TObject)');
  221. Source.Add(' b : T;');
  222. Source.Add(' end;');
  223. ParseDeclarations;
  224. AssertNotNull('have generic definition',Declarations.Classes);
  225. AssertEquals('have generic definition',1,Declarations.Classes.Count);
  226. AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType);
  227. T:=TPasClassType(Declarations.Classes[0]);
  228. AssertNotNull('have generic templates',T.GenericTemplateTypes);
  229. AssertEquals('1 template types',1,T.GenericTemplateTypes.Count);
  230. AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent);
  231. AssertEquals('Type constraint is recorded','T2',TPasGenericTemplateType(T.GenericTemplateTypes[0]).TypeConstraint);
  232. end;
  233. procedure TTestGenerics.TestSpecializationDelphi;
  234. begin
  235. Add('{$mode delphi}');
  236. ParseType('TFPGList<integer>',TPasSpecializeType,'');
  237. end;
  238. procedure TTestGenerics.TestDeclarationDelphiSpecialize;
  239. Var
  240. T : TPasClassType;
  241. begin
  242. Scanner.CurrentModeSwitches:=[msDelphi]+Scanner.CurrentModeSwitches ;
  243. Source.Add('Type');
  244. Source.Add(' TSomeClass<T,T2> = Class(TSomeGeneric<Integer,Integer>)');
  245. Source.Add(' b : T;');
  246. Source.Add(' b2 : T2;');
  247. Source.Add(' end;');
  248. ParseDeclarations;
  249. AssertNotNull('have generic definition',Declarations.Classes);
  250. AssertEquals('have generic definition',1,Declarations.Classes.Count);
  251. AssertEquals('Pascal class',TPasClassType,TObject(Declarations.Classes[0]).ClassType);
  252. T:=TPasClassType(Declarations.Classes[0]);
  253. AssertEquals('Name is correct','TSomeClass',T.Name);
  254. AssertNotNull('have generic templates',T.GenericTemplateTypes);
  255. AssertEquals('2 template types',2,T.GenericTemplateTypes.Count);
  256. AssertSame('Parent 0 is class',T,TPasElement(T.GenericTemplateTypes[0]).Parent);
  257. AssertSame('Parent 1 is class',T,TPasElement(T.GenericTemplateTypes[1]).Parent);
  258. end;
  259. procedure TTestGenerics.TestInlineSpecializationInArgument;
  260. begin
  261. With source do
  262. begin
  263. Add('unit afile;');
  264. Add('{$MODE DELPHI}');
  265. Add('interface');
  266. Add('type');
  267. Add(' TFoo=class');
  268. Add(' procedure foo(var Node:TSomeGeneric<TBoundingBox>;const index:Integer);');
  269. Add(' end;');
  270. Add('implementation');
  271. end;
  272. ParseModule;
  273. end;
  274. procedure TTestGenerics.TestSpecializeNested;
  275. begin
  276. Add([
  277. 'Type',
  278. ' generic TSomeClass<A,B> = class(specialize TOther<A,specialize TAnother<B>>) end;',
  279. '']);
  280. ParseDeclarations;
  281. end;
  282. procedure TTestGenerics.TestInlineSpecializeInStatement;
  283. begin
  284. Add([
  285. '{$mode objfpc}',
  286. 'begin',
  287. ' vec:=specialize TVector<double>.create;',
  288. ' t:=specialize a<b>;',
  289. //' t:=specialize a<b.specialize c<d,e.f>>;',
  290. //' t:=a.specialize b<c>;',
  291. ' t:=specialize a<b>.c;',
  292. '']);
  293. ParseModule;
  294. end;
  295. procedure TTestGenerics.TestInlineSpecializeInStatementDelphi;
  296. begin
  297. Add([
  298. '{$mode delphi}',
  299. 'begin',
  300. ' vec:=TVector<double>.create;',
  301. ' b:=a<b;',
  302. ' t:=a<b.c<d,e.f>>;',
  303. ' t:=a.b<c>;',
  304. ' t:=a<b>.c;',
  305. // forbidden:' t:=a<b<c>.d>;',
  306. '']);
  307. ParseModule;
  308. end;
  309. procedure TTestGenerics.TestGenericFunction_Program;
  310. begin
  311. Add([
  312. 'generic function IfThen<T>(val:boolean;const iftrue:T; const iffalse:T) :T; inline; overload;',
  313. 'begin',
  314. 'end;',
  315. 'begin',
  316. ' specialize IfThen<word>(true,2,3);',
  317. '']);
  318. ParseModule;
  319. end;
  320. procedure TTestGenerics.TestGenericFunction_Unit;
  321. begin
  322. Add([
  323. 'unit afile;',
  324. 'interface',
  325. 'generic function Get<T>(val: T) :T;',
  326. 'implementation',
  327. 'generic function Get<T>(val: T) :T;',
  328. 'begin',
  329. 'end;',
  330. 'initialization',
  331. ' specialize GetIt<word>(2);',
  332. '']);
  333. ParseModule;
  334. end;
  335. procedure TTestGenerics.TestGenericMethod_Program;
  336. begin
  337. Add([
  338. '{$mode objfpc}',
  339. 'type',
  340. ' TObject = class',
  341. ' generic function Get<T>(val: T) :T;',
  342. ' type TBird = word;',
  343. ' generic procedure Fly<T>;',
  344. ' const C = 1;',
  345. ' generic procedure Run<T>;',
  346. ' end;',
  347. 'generic function TObject.Get<T>(val: T) :T;',
  348. 'begin',
  349. 'end;',
  350. 'begin',
  351. ' TObject.specialize GetIt<word>(2);',
  352. '']);
  353. ParseModule;
  354. end;
  355. procedure TTestGenerics.TestGenericMethod_OverloadDelphi;
  356. begin
  357. Add([
  358. '{$mode delphi}',
  359. 'type',
  360. ' TObject = class',
  361. ' procedure Fly<S>; overload;',
  362. ' procedure Fly<T>(val: T); overload;',
  363. ' end;',
  364. 'procedure TObject.Fly<S>;',
  365. 'begin',
  366. 'end;',
  367. 'procedure TObject.Fly<T>(val: word);',
  368. 'begin',
  369. 'end;',
  370. 'var o : TObject;',
  371. 'begin',
  372. ' o.Fly<word>;',
  373. ' o.Fly<word>();',
  374. ' o.Fly<longint>(3);',
  375. ' with o do begin',
  376. ' Fly<word>;',
  377. ' Fly<word>();',
  378. ' Fly<longint>(13);',
  379. ' end;',
  380. '']);
  381. ParseModule;
  382. end;
  383. initialization
  384. RegisterTest(TTestGenerics);
  385. end.