pgenutil.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  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,
  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 : 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 : ansistring;
  64. uspecializename,
  65. countstr,genname,ugenname,specializename : 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.typesym.realname;
  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.typesym.realname;
  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. specializename:=genname+specializename;
  238. uspecializename:=upper(specializename);
  239. prettyname:=genericdef.typesym.prettyname+'<'+prettyname+'>';
  240. { select the symtable containing the params }
  241. case genericdef.typ of
  242. procdef:
  243. st:=genericdef.GetSymtable(gs_para);
  244. objectdef,
  245. recorddef:
  246. st:=genericdef.GetSymtable(gs_record);
  247. arraydef:
  248. st:=tarraydef(genericdef).symtable;
  249. procvardef:
  250. st:=genericdef.GetSymtable(gs_para);
  251. else
  252. internalerror(200511182);
  253. end;
  254. { build the list containing the types for the generic params }
  255. gencount:=0;
  256. for i:=0 to st.SymList.Count-1 do
  257. begin
  258. srsym:=tsym(st.SymList[i]);
  259. if sp_generic_para in srsym.symoptions then
  260. begin
  261. if gencount=genericdeflist.Count then
  262. internalerror(2011042702);
  263. generictype:=ttypesym.create(srsym.realname,tdef(genericdeflist[gencount]));
  264. generictypelist.add(generictype);
  265. inc(gencount);
  266. end;
  267. end;
  268. { Special case if we are referencing the current defined object }
  269. if assigned(current_structdef) and
  270. (current_structdef.objname^=uspecializename) then
  271. tt:=current_structdef;
  272. { decide in which symtable to put the specialization }
  273. if current_module.is_unit and current_module.in_interface then
  274. specializest:=current_module.globalsymtable
  275. else
  276. specializest:=current_module.localsymtable;
  277. { Can we reuse an already specialized type? }
  278. if not assigned(tt) then
  279. begin
  280. hashedid.id:=uspecializename;
  281. srsym:=tsym(specializest.findwithhash(hashedid));
  282. if assigned(srsym) then
  283. begin
  284. if srsym.typ<>typesym then
  285. internalerror(200710171);
  286. tt:=ttypesym(srsym).typedef;
  287. end
  288. else
  289. { the generic could have been specialized in the globalsymtable
  290. already, so search there as well }
  291. if (specializest<>current_module.globalsymtable) and assigned(current_module.globalsymtable) then
  292. begin
  293. srsym:=tsym(current_module.globalsymtable.findwithhash(hashedid));
  294. if assigned(srsym) then
  295. begin
  296. if srsym.typ<>typesym then
  297. internalerror(2011121101);
  298. tt:=ttypesym(srsym).typedef;
  299. end;
  300. end;
  301. end;
  302. if not assigned(tt) then
  303. begin
  304. { Setup symtablestack at definition time
  305. to get types right, however this is not perfect, we should probably record
  306. the resolved symbols }
  307. oldsymtablestack:=symtablestack;
  308. oldextendeddefs:=current_module.extendeddefs;
  309. current_module.extendeddefs:=TFPHashObjectList.create(true);
  310. symtablestack:=tdefawaresymtablestack.create;
  311. if not assigned(genericdef) then
  312. internalerror(200705151);
  313. hmodule:=find_module_from_symtable(genericdef.owner);
  314. if hmodule=nil then
  315. internalerror(200705152);
  316. pu:=tused_unit(hmodule.used_units.first);
  317. while assigned(pu) do
  318. begin
  319. if not assigned(pu.u.globalsymtable) then
  320. internalerror(200705153);
  321. symtablestack.push(pu.u.globalsymtable);
  322. pu:=tused_unit(pu.next);
  323. end;
  324. if assigned(hmodule.globalsymtable) then
  325. symtablestack.push(hmodule.globalsymtable);
  326. { push the localsymtable if needed }
  327. if (hmodule<>current_module) or not current_module.in_interface then
  328. symtablestack.push(hmodule.localsymtable);
  329. { push a temporary global symtable so that the specialization is
  330. added to the correct symtable; this symtable does not contain
  331. any other symbols, so that the type resolution can not be
  332. influenced by symbols in the current unit }
  333. tempst:=tspecializesymtable.create(current_module.modulename^,current_module.moduleid);
  334. symtablestack.push(tempst);
  335. { Reparse the original type definition }
  336. if not err then
  337. begin
  338. if parse_class_parent then
  339. begin
  340. old_current_structdef:=current_structdef;
  341. old_current_genericdef:=current_genericdef;
  342. old_current_specializedef:=current_specializedef;
  343. if genericdef.owner.symtabletype in [recordsymtable,objectsymtable] then
  344. current_structdef:=tabstractrecorddef(genericdef.owner.defowner)
  345. else
  346. current_structdef:=nil;
  347. current_genericdef:=nil;
  348. current_specializedef:=nil;
  349. end;
  350. { First a new typesym so we can reuse this specialization and
  351. references to this specialization can be handled }
  352. srsym:=ttypesym.create(specializename,generrordef);
  353. specializest.insert(srsym);
  354. { specializations are declarations as such it is the wisest to
  355. declare set the blocktype to "type"; otherwise we'll
  356. experience unexpected side effects like the addition of
  357. classrefdefs if we have a generic that's derived from another
  358. generic }
  359. old_block_type:=block_type;
  360. block_type:=bt_type;
  361. if not assigned(genericdef.generictokenbuf) then
  362. internalerror(200511171);
  363. current_scanner.startreplaytokens(genericdef.generictokenbuf,
  364. genericdef.change_endian);
  365. read_named_type(tt,specializename,genericdef,generictypelist,false);
  366. ttypesym(srsym).typedef:=tt;
  367. tt.typesym:=srsym;
  368. if _prettyname<>'' then
  369. ttypesym(tt.typesym).fprettyname:=_prettyname
  370. else
  371. ttypesym(tt.typesym).fprettyname:=prettyname;
  372. { Note regarding hint directives:
  373. There is no need to remove the flags for them from the
  374. specialized generic symbol, because hint directives that
  375. follow the specialization are handled by the code in
  376. pdecl.types_dec and added to the type symbol.
  377. E.g.: TFoo = TBar<Blubb> deprecated;
  378. Here the symbol TBar$1$Blubb will contain the
  379. "sp_hint_deprecated" flag while the TFoo symbol won't.}
  380. case tt.typ of
  381. { Build VMT indexes for classes and read hint directives }
  382. objectdef:
  383. begin
  384. try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
  385. consume(_SEMICOLON);
  386. vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt));
  387. vmtbuilder.generate_vmt;
  388. vmtbuilder.free;
  389. end;
  390. { handle params, calling convention, etc }
  391. procvardef:
  392. begin
  393. if not check_proc_directive(true) then
  394. begin
  395. try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg);
  396. consume(_SEMICOLON);
  397. end;
  398. parse_var_proc_directives(ttypesym(srsym));
  399. handle_calling_convention(tprocvardef(tt));
  400. if try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg) then
  401. consume(_SEMICOLON);
  402. end;
  403. else
  404. { parse hint directives for records and arrays }
  405. begin
  406. try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
  407. consume(_SEMICOLON);
  408. end;
  409. end;
  410. { Consume the semicolon if it is also recorded }
  411. try_to_consume(_SEMICOLON);
  412. block_type:=old_block_type;
  413. if parse_class_parent then
  414. begin
  415. current_structdef:=old_current_structdef;
  416. current_genericdef:=old_current_genericdef;
  417. current_specializedef:=old_current_specializedef;
  418. end;
  419. end;
  420. { extract all created symbols and defs from the temporary symtable
  421. and add them to the specializest }
  422. for i:=0 to tempst.SymList.Count-1 do
  423. begin
  424. item:=tempst.SymList.Items[i];
  425. specializest.SymList.Add(tempst.SymList.NameOfIndex(i),item);
  426. tsym(item).Owner:=specializest;
  427. tempst.SymList.Extract(item);
  428. end;
  429. for i:=0 to tempst.DefList.Count-1 do
  430. begin
  431. item:=tempst.DefList.Items[i];
  432. specializest.DefList.Add(item);
  433. tdef(item).owner:=specializest;
  434. tempst.DefList.Extract(item);
  435. end;
  436. tempst.free;
  437. { Restore symtablestack }
  438. current_module.extendeddefs.free;
  439. current_module.extendeddefs:=oldextendeddefs;
  440. symtablestack.free;
  441. symtablestack:=oldsymtablestack;
  442. end;
  443. if not (token in [_GT, _RSHARPBRACKET]) then
  444. begin
  445. consume(_RSHARPBRACKET);
  446. exit;
  447. end
  448. else
  449. consume(token);
  450. genericdeflist.free;
  451. generictypelist.free;
  452. if assigned(genericdef) then
  453. begin
  454. { check the hints of the found generic symbol }
  455. srsym:=genericdef.typesym;
  456. check_hints(srsym,srsym.symoptions,srsym.deprecatedmsg);
  457. end;
  458. end;
  459. function parse_generic_parameters:TFPObjectList;
  460. var
  461. generictype : ttypesym;
  462. begin
  463. result:=TFPObjectList.Create(false);
  464. repeat
  465. if token=_ID then
  466. begin
  467. generictype:=ttypesym.create(orgpattern,cundefinedtype);
  468. include(generictype.symoptions,sp_generic_para);
  469. result.add(generictype);
  470. end;
  471. consume(_ID);
  472. until not try_to_consume(_COMMA) ;
  473. end;
  474. procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
  475. var
  476. i: longint;
  477. generictype: ttypesym;
  478. st: tsymtable;
  479. begin
  480. def.genericdef:=genericdef;
  481. if not assigned(genericlist) then
  482. exit;
  483. case def.typ of
  484. recorddef,objectdef: st:=tabstractrecorddef(def).symtable;
  485. arraydef: st:=tarraydef(def).symtable;
  486. procvardef,procdef: st:=tabstractprocdef(def).parast;
  487. else
  488. internalerror(201101020);
  489. end;
  490. for i:=0 to genericlist.count-1 do
  491. begin
  492. generictype:=ttypesym(genericlist[i]);
  493. if generictype.typedef.typ=undefineddef then
  494. include(def.defoptions,df_generic)
  495. else
  496. include(def.defoptions,df_specialization);
  497. st.insert(generictype);
  498. end;
  499. end;
  500. end.