pgenutil.pas 24 KB

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