pgenutil.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. {
  2. Copyright (c) 2011
  3. Contains different functions that are used in the context of
  4. parsing generics.
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit pgenutil;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. { common }
  23. cclasses,
  24. { symtable }
  25. symtype,symdef;
  26. procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string;parsedtype:tdef;symname:string);
  27. function parse_generic_parameters:TFPObjectList;
  28. procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
  29. implementation
  30. uses
  31. { common }
  32. cutils,fpccrc,
  33. { global }
  34. globals,globtype,tokens,verbose,
  35. { symtable }
  36. symconst,symbase,symsym,symtable,
  37. { modules }
  38. fmodule,
  39. { pass 1 }
  40. htypechk,
  41. node,nobj,nmem,
  42. { parser }
  43. scanner,
  44. pbase,pexpr,pdecsub,ptype;
  45. procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string;parsedtype:tdef;symname:string);
  46. var
  47. st : TSymtable;
  48. srsym : tsym;
  49. pt2 : tnode;
  50. found,
  51. first,
  52. err : boolean;
  53. i,
  54. gencount,crc : longint;
  55. genericdef : tstoreddef;
  56. generictype : ttypesym;
  57. genericdeflist : TFPObjectList;
  58. generictypelist : TFPObjectList;
  59. oldsymtablestack : tsymtablestack;
  60. oldextendeddefs : TFPHashObjectList;
  61. hmodule : tmodule;
  62. pu : tused_unit;
  63. prettyname,specializename : ansistring;
  64. ufinalspecializename,
  65. countstr,genname,ugenname,finalspecializename : string;
  66. vmtbuilder : TVMTBuilder;
  67. specializest : tsymtable;
  68. item : tobject;
  69. old_current_structdef : tabstractrecorddef;
  70. old_current_genericdef,old_current_specializedef : tstoreddef;
  71. tempst : tglobalsymtable;
  72. old_block_type: tblock_type;
  73. hashedid: thashedidstring;
  74. begin
  75. { retrieve generic def that we are going to replace }
  76. genericdef:=tstoreddef(tt);
  77. tt:=nil;
  78. { either symname must be given or genericdef needs to be valid }
  79. if (symname='') and
  80. (not assigned(genericdef) or
  81. not assigned(genericdef.typesym) or
  82. (genericdef.typesym.typ<>typesym)) then
  83. internalerror(2011042701);
  84. { Only parse the parameters for recovery or
  85. for recording in genericbuf }
  86. if parse_generic then
  87. begin
  88. if not try_to_consume(_LT) then
  89. consume(_LSHARPBRACKET);
  90. gencount:=0;
  91. repeat
  92. pt2:=factor(false,true);
  93. pt2.free;
  94. inc(gencount);
  95. until not try_to_consume(_COMMA);
  96. if not try_to_consume(_GT) then
  97. consume(_RSHARPBRACKET);
  98. { we need to return a def that can later pass some checks like
  99. whether it's an interface or not }
  100. if parse_generic and (not assigned(tt) or (tt.typ=undefineddef)) then
  101. begin
  102. if (symname='') and (df_generic in genericdef.defoptions) then
  103. { this happens in non-Delphi modes }
  104. tt:=genericdef
  105. else
  106. begin
  107. { find the corresponding generic symbol so that any checks
  108. done on the returned def will be handled correctly }
  109. str(gencount,countstr);
  110. if symname='' then
  111. genname:=ttypesym(genericdef.typesym).realname
  112. else
  113. genname:=symname;
  114. genname:=genname+'$'+countstr;
  115. ugenname:=upper(genname);
  116. if not searchsym(ugenname,srsym,st) or
  117. (srsym.typ<>typesym) then
  118. begin
  119. identifier_not_found(genname);
  120. exit;
  121. end;
  122. tt:=ttypesym(srsym).typedef;
  123. { this happens in non-Delphi modes if we encounter a
  124. specialization of the generic class or record we're
  125. currently parsing }
  126. if (tt.typ=errordef) and assigned(current_structdef) and
  127. (current_structdef.objname^=ugenname) then
  128. tt:=current_structdef;
  129. end;
  130. end;
  131. exit;
  132. end;
  133. if not assigned(parsedtype) and not try_to_consume(_LT) then
  134. consume(_LSHARPBRACKET);
  135. generictypelist:=TFPObjectList.create(false);
  136. genericdeflist:=TFPObjectList.Create(false);
  137. { Parse type parameters }
  138. err:=false;
  139. { set the block type to type, so that the parsed type are returned as
  140. ttypenode (e.g. classes are in non type-compatible blocks returned as
  141. tloadvmtaddrnode) }
  142. old_block_type:=block_type;
  143. { if parsedtype is set, then the first type identifer was already parsed
  144. (happens in inline specializations) and thus we only need to parse
  145. the remaining types and do as if the first one was already given }
  146. first:=not assigned(parsedtype);
  147. if assigned(parsedtype) then
  148. begin
  149. genericdeflist.Add(parsedtype);
  150. specializename:='$'+parsedtype.typename;
  151. prettyname:=parsedtype.typesym.prettyname;
  152. end
  153. else
  154. begin
  155. specializename:='';
  156. prettyname:='';
  157. end;
  158. while not (token in [_GT,_RSHARPBRACKET]) do
  159. begin
  160. { "first" is set to false at the end of the loop! }
  161. if not first then
  162. consume(_COMMA);
  163. block_type:=bt_type;
  164. pt2:=factor(false,true);
  165. if pt2.nodetype=typen then
  166. begin
  167. if df_generic in pt2.resultdef.defoptions then
  168. Message(parser_e_no_generics_as_params);
  169. genericdeflist.Add(pt2.resultdef);
  170. if not assigned(pt2.resultdef.typesym) then
  171. message(type_e_generics_cannot_reference_itself)
  172. else
  173. begin
  174. specializename:=specializename+'$'+pt2.resultdef.typename;
  175. if first then
  176. prettyname:=prettyname+pt2.resultdef.typesym.prettyname
  177. else
  178. prettyname:=prettyname+','+pt2.resultdef.typesym.prettyname;
  179. end;
  180. end
  181. else
  182. begin
  183. Message(type_e_type_id_expected);
  184. err:=true;
  185. end;
  186. pt2.free;
  187. first:=false;
  188. end;
  189. block_type:=old_block_type;
  190. if err then
  191. begin
  192. try_to_consume(_RSHARPBRACKET);
  193. exit;
  194. end;
  195. { search a generic with the given count of params }
  196. countstr:='';
  197. str(genericdeflist.Count,countstr);
  198. { use the name of the symbol as procvars return a user friendly version
  199. of the name }
  200. if symname='' then
  201. genname:=ttypesym(genericdef.typesym).realname
  202. else
  203. genname:=symname;
  204. { in case of non-Delphi mode the type name could already be a generic
  205. def (but maybe the wrong one) }
  206. if assigned(genericdef) and (df_generic in genericdef.defoptions) then
  207. begin
  208. { remove the type count suffix from the generic's name }
  209. for i:=Length(genname) downto 1 do
  210. if genname[i]='$' then
  211. begin
  212. genname:=copy(genname,1,i-1);
  213. break;
  214. end;
  215. end;
  216. genname:=genname+'$'+countstr;
  217. ugenname:=upper(genname);
  218. if assigned(genericdef) and (genericdef.owner.symtabletype in [objectsymtable,recordsymtable]) then
  219. begin
  220. if genericdef.owner.symtabletype = objectsymtable then
  221. found:=searchsym_in_class(tobjectdef(genericdef.owner.defowner),tobjectdef(genericdef.owner.defowner),ugenname,srsym,st,false)
  222. else
  223. found:=searchsym_in_record(tabstractrecorddef(genericdef.owner.defowner),ugenname,srsym,st);
  224. end
  225. else
  226. found:=searchsym(ugenname,srsym,st);
  227. if not found or (srsym.typ<>typesym) then
  228. begin
  229. identifier_not_found(genname);
  230. genericdeflist.Free;
  231. generictypelist.Free;
  232. exit;
  233. end;
  234. { we've found the correct def }
  235. genericdef:=tstoreddef(ttypesym(srsym).typedef);
  236. { build the new type's name }
  237. crc:=UpdateCrc32(0,specializename[1],length(specializename));
  238. finalspecializename:=genname+'$crc'+hexstr(crc,8);
  239. ufinalspecializename:=upper(finalspecializename);
  240. prettyname:=genericdef.typesym.prettyname+'<'+prettyname+'>';
  241. { select the symtable containing the params }
  242. case genericdef.typ of
  243. procdef:
  244. st:=genericdef.GetSymtable(gs_para);
  245. objectdef,
  246. recorddef:
  247. st:=genericdef.GetSymtable(gs_record);
  248. arraydef:
  249. st:=tarraydef(genericdef).symtable;
  250. procvardef:
  251. st:=genericdef.GetSymtable(gs_para);
  252. else
  253. internalerror(200511182);
  254. end;
  255. { build the list containing the types for the generic params }
  256. gencount:=0;
  257. for i:=0 to st.SymList.Count-1 do
  258. begin
  259. srsym:=tsym(st.SymList[i]);
  260. if sp_generic_para in srsym.symoptions then
  261. begin
  262. if gencount=genericdeflist.Count then
  263. internalerror(2011042702);
  264. generictype:=ttypesym.create(srsym.realname,tdef(genericdeflist[gencount]));
  265. generictypelist.add(generictype);
  266. inc(gencount);
  267. end;
  268. end;
  269. { Special case if we are referencing the current defined object }
  270. if assigned(current_structdef) and
  271. (current_structdef.objname^=ufinalspecializename) then
  272. tt:=current_structdef;
  273. { decide in which symtable to put the specialization }
  274. if current_module.is_unit and current_module.in_interface then
  275. specializest:=current_module.globalsymtable
  276. else
  277. specializest:=current_module.localsymtable;
  278. { Can we reuse an already specialized type? }
  279. if not assigned(tt) then
  280. begin
  281. hashedid.id:=ufinalspecializename;
  282. srsym:=tsym(specializest.findwithhash(hashedid));
  283. if assigned(srsym) then
  284. begin
  285. if srsym.typ<>typesym then
  286. internalerror(200710171);
  287. tt:=ttypesym(srsym).typedef;
  288. end
  289. else
  290. { the generic could have been specialized in the globalsymtable
  291. already, so search there as well }
  292. if (specializest<>current_module.globalsymtable) and assigned(current_module.globalsymtable) then
  293. begin
  294. srsym:=tsym(current_module.globalsymtable.findwithhash(hashedid));
  295. if assigned(srsym) then
  296. begin
  297. if srsym.typ<>typesym then
  298. internalerror(2011121101);
  299. tt:=ttypesym(srsym).typedef;
  300. end;
  301. end;
  302. end;
  303. if not assigned(tt) then
  304. begin
  305. { Setup symtablestack at definition time
  306. to get types right, however this is not perfect, we should probably record
  307. the resolved symbols }
  308. oldsymtablestack:=symtablestack;
  309. oldextendeddefs:=current_module.extendeddefs;
  310. current_module.extendeddefs:=TFPHashObjectList.create(true);
  311. symtablestack:=tdefawaresymtablestack.create;
  312. if not assigned(genericdef) then
  313. internalerror(200705151);
  314. hmodule:=find_module_from_symtable(genericdef.owner);
  315. if hmodule=nil then
  316. internalerror(200705152);
  317. pu:=tused_unit(hmodule.used_units.first);
  318. while assigned(pu) do
  319. begin
  320. if not assigned(pu.u.globalsymtable) then
  321. internalerror(200705153);
  322. symtablestack.push(pu.u.globalsymtable);
  323. pu:=tused_unit(pu.next);
  324. end;
  325. if assigned(hmodule.globalsymtable) then
  326. symtablestack.push(hmodule.globalsymtable);
  327. { push the localsymtable if needed }
  328. if (hmodule<>current_module) or not current_module.in_interface then
  329. symtablestack.push(hmodule.localsymtable);
  330. { push a temporary global symtable so that the specialization is
  331. added to the correct symtable; this symtable does not contain
  332. any other symbols, so that the type resolution can not be
  333. influenced by symbols in the current unit }
  334. tempst:=tspecializesymtable.create(current_module.modulename^,current_module.moduleid);
  335. symtablestack.push(tempst);
  336. { Reparse the original type definition }
  337. if not err then
  338. begin
  339. if parse_class_parent then
  340. begin
  341. old_current_structdef:=current_structdef;
  342. old_current_genericdef:=current_genericdef;
  343. old_current_specializedef:=current_specializedef;
  344. if genericdef.owner.symtabletype in [recordsymtable,objectsymtable] then
  345. current_structdef:=tabstractrecorddef(genericdef.owner.defowner)
  346. else
  347. current_structdef:=nil;
  348. current_genericdef:=nil;
  349. current_specializedef:=nil;
  350. end;
  351. { First a new typesym so we can reuse this specialization and
  352. references to this specialization can be handled }
  353. srsym:=ttypesym.create(finalspecializename,generrordef);
  354. specializest.insert(srsym);
  355. { specializations are declarations as such it is the wisest to
  356. declare set the blocktype to "type"; otherwise we'll
  357. experience unexpected side effects like the addition of
  358. classrefdefs if we have a generic that's derived from another
  359. generic }
  360. old_block_type:=block_type;
  361. block_type:=bt_type;
  362. if not assigned(genericdef.generictokenbuf) then
  363. internalerror(200511171);
  364. current_scanner.startreplaytokens(genericdef.generictokenbuf,
  365. genericdef.change_endian);
  366. read_named_type(tt,finalspecializename,genericdef,generictypelist,false);
  367. ttypesym(srsym).typedef:=tt;
  368. tt.typesym:=srsym;
  369. if _prettyname<>'' then
  370. ttypesym(tt.typesym).fprettyname:=_prettyname
  371. else
  372. ttypesym(tt.typesym).fprettyname:=prettyname;
  373. { Note regarding hint directives:
  374. There is no need to remove the flags for them from the
  375. specialized generic symbol, because hint directives that
  376. follow the specialization are handled by the code in
  377. pdecl.types_dec and added to the type symbol.
  378. E.g.: TFoo = TBar<Blubb> deprecated;
  379. Here the symbol TBar$1$Blubb will contain the
  380. "sp_hint_deprecated" flag while the TFoo symbol won't.}
  381. case tt.typ of
  382. { Build VMT indexes for classes and read hint directives }
  383. objectdef:
  384. begin
  385. try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
  386. consume(_SEMICOLON);
  387. vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt));
  388. vmtbuilder.generate_vmt;
  389. vmtbuilder.free;
  390. end;
  391. { handle params, calling convention, etc }
  392. procvardef:
  393. begin
  394. if not check_proc_directive(true) then
  395. begin
  396. try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg);
  397. consume(_SEMICOLON);
  398. end;
  399. parse_var_proc_directives(ttypesym(srsym));
  400. handle_calling_convention(tprocvardef(tt));
  401. if try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg) then
  402. consume(_SEMICOLON);
  403. end;
  404. else
  405. { parse hint directives for records and arrays }
  406. begin
  407. try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
  408. consume(_SEMICOLON);
  409. end;
  410. end;
  411. { Consume the semicolon if it is also recorded }
  412. try_to_consume(_SEMICOLON);
  413. block_type:=old_block_type;
  414. if parse_class_parent then
  415. begin
  416. current_structdef:=old_current_structdef;
  417. current_genericdef:=old_current_genericdef;
  418. current_specializedef:=old_current_specializedef;
  419. end;
  420. end;
  421. { extract all created symbols and defs from the temporary symtable
  422. and add them to the specializest }
  423. for i:=0 to tempst.SymList.Count-1 do
  424. begin
  425. item:=tempst.SymList.Items[i];
  426. specializest.SymList.Add(tempst.SymList.NameOfIndex(i),item);
  427. tsym(item).Owner:=specializest;
  428. tempst.SymList.Extract(item);
  429. end;
  430. for i:=0 to tempst.DefList.Count-1 do
  431. begin
  432. item:=tempst.DefList.Items[i];
  433. specializest.DefList.Add(item);
  434. tdef(item).owner:=specializest;
  435. tempst.DefList.Extract(item);
  436. end;
  437. tempst.free;
  438. { Restore symtablestack }
  439. current_module.extendeddefs.free;
  440. current_module.extendeddefs:=oldextendeddefs;
  441. symtablestack.free;
  442. symtablestack:=oldsymtablestack;
  443. end;
  444. if not (token in [_GT, _RSHARPBRACKET]) then
  445. begin
  446. consume(_RSHARPBRACKET);
  447. exit;
  448. end
  449. else
  450. consume(token);
  451. genericdeflist.free;
  452. generictypelist.free;
  453. if assigned(genericdef) then
  454. begin
  455. { check the hints of the found generic symbol }
  456. srsym:=genericdef.typesym;
  457. check_hints(srsym,srsym.symoptions,srsym.deprecatedmsg);
  458. end;
  459. end;
  460. function parse_generic_parameters:TFPObjectList;
  461. var
  462. generictype : ttypesym;
  463. begin
  464. result:=TFPObjectList.Create(false);
  465. repeat
  466. if token=_ID then
  467. begin
  468. generictype:=ttypesym.create(orgpattern,cundefinedtype);
  469. include(generictype.symoptions,sp_generic_para);
  470. result.add(generictype);
  471. end;
  472. consume(_ID);
  473. until not try_to_consume(_COMMA) ;
  474. end;
  475. procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
  476. var
  477. i: longint;
  478. generictype: ttypesym;
  479. st: tsymtable;
  480. begin
  481. def.genericdef:=genericdef;
  482. if not assigned(genericlist) then
  483. exit;
  484. case def.typ of
  485. recorddef,objectdef: st:=tabstractrecorddef(def).symtable;
  486. arraydef: st:=tarraydef(def).symtable;
  487. procvardef,procdef: st:=tabstractprocdef(def).parast;
  488. else
  489. internalerror(201101020);
  490. end;
  491. for i:=0 to genericlist.count-1 do
  492. begin
  493. generictype:=ttypesym(genericlist[i]);
  494. if generictype.typedef.typ=undefineddef then
  495. include(def.defoptions,df_generic)
  496. else
  497. include(def.defoptions,df_specialization);
  498. st.insert(generictype);
  499. end;
  500. end;
  501. end.