pgenutil.pas 24 KB

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