pgenutil.pas 25 KB

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