tcgenerics.pas 11 KB

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