pgenutil.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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,pttmp : tnode;
  50. first,
  51. err : boolean;
  52. i,
  53. gencount : longint;
  54. sym : tsym;
  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. onlyparsepara : boolean;
  68. specializest : tsymtable;
  69. item : tobject;
  70. old_current_structdef : tabstractrecorddef;
  71. old_current_genericdef,old_current_specializedef : tstoreddef;
  72. tempst : tglobalsymtable;
  73. old_block_type: tblock_type;
  74. begin
  75. { retrieve generic def that we are going to replace }
  76. genericdef:=tstoreddef(tt);
  77. tt:=nil;
  78. onlyparsepara:=false;
  79. { either symname must be given or genericdef needs to be valid }
  80. if (symname='') and
  81. (not assigned(genericdef) or
  82. not assigned(genericdef.typesym) or
  83. (genericdef.typesym.typ<>typesym)) then
  84. internalerror(2011042701);
  85. { Only parse the parameters for recovery or
  86. for recording in genericbuf }
  87. if parse_generic then
  88. begin
  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. consume(_RSHARPBRACKET);
  97. { we need to return a def that can later pass some checks like
  98. whether it's an interface or not }
  99. if parse_generic and (not assigned(tt) or (tt.typ=undefineddef)) then
  100. begin
  101. if (symname='') and (df_generic in genericdef.defoptions) then
  102. { this happens in non-Delphi modes }
  103. tt:=genericdef
  104. else
  105. begin
  106. { find the corresponding generic symbol so that any checks
  107. done on the returned def will be handled correctly }
  108. str(gencount,countstr);
  109. if symname='' then
  110. genname:=ttypesym(genericdef.typesym).realname
  111. else
  112. genname:=symname;
  113. genname:=genname+'$'+countstr;
  114. ugenname:=upper(genname);
  115. if not searchsym(ugenname,srsym,st) or
  116. (srsym.typ<>typesym) then
  117. begin
  118. identifier_not_found(genname);
  119. exit;
  120. end;
  121. tt:=ttypesym(srsym).typedef;
  122. end;
  123. end;
  124. exit;
  125. end;
  126. if not assigned(parsedtype) and not try_to_consume(_LT) then
  127. consume(_LSHARPBRACKET);
  128. generictypelist:=TFPObjectList.create(false);
  129. genericdeflist:=TFPObjectList.Create(false);
  130. { Parse type parameters }
  131. err:=false;
  132. { set the block type to type, so that the parsed type are returned as
  133. ttypenode (e.g. classes are in non type-compatible blocks returned as
  134. tloadvmtaddrnode) }
  135. old_block_type:=block_type;
  136. { if parsedtype is set, then the first type identifer was already parsed
  137. (happens in inline specializations) and thus we only need to parse
  138. the remaining types and do as if the first one was already given }
  139. first:=not assigned(parsedtype);
  140. if assigned(parsedtype) then
  141. begin
  142. genericdeflist.Add(parsedtype);
  143. specializename:='$'+parsedtype.typesym.realname;
  144. prettyname:=parsedtype.typesym.prettyname;
  145. end
  146. else
  147. begin
  148. specializename:='';
  149. prettyname:='';
  150. end;
  151. while not (token in [_GT,_RSHARPBRACKET]) do
  152. begin
  153. { "first" is set to false at the end of the loop! }
  154. if not first then
  155. consume(_COMMA);
  156. block_type:=bt_type;
  157. pt2:=factor(false,true);
  158. if pt2.nodetype=typen then
  159. begin
  160. if df_generic in pt2.resultdef.defoptions then
  161. Message(parser_e_no_generics_as_params);
  162. genericdeflist.Add(pt2.resultdef);
  163. if not assigned(pt2.resultdef.typesym) then
  164. message(type_e_generics_cannot_reference_itself)
  165. else
  166. begin
  167. specializename:=specializename+'$'+pt2.resultdef.typesym.realname;
  168. if first then
  169. prettyname:=prettyname+pt2.resultdef.typesym.prettyname
  170. else
  171. prettyname:=prettyname+','+pt2.resultdef.typesym.prettyname;
  172. end;
  173. end
  174. else
  175. begin
  176. Message(type_e_type_id_expected);
  177. err:=true;
  178. end;
  179. pt2.free;
  180. first:=false;
  181. end;
  182. block_type:=old_block_type;
  183. if err then
  184. begin
  185. try_to_consume(_RSHARPBRACKET);
  186. exit;
  187. end;
  188. { search a generic with the given count of params }
  189. countstr:='';
  190. str(genericdeflist.Count,countstr);
  191. { use the name of the symbol as procvars return a user friendly version
  192. of the name }
  193. if symname='' then
  194. genname:=ttypesym(genericdef.typesym).realname
  195. else
  196. genname:=symname;
  197. { in case of non-Delphi mode the type name could already be a generic
  198. def (but maybe the wrong one) }
  199. if assigned(genericdef) and (df_generic in genericdef.defoptions) then
  200. begin
  201. { remove the type count suffix from the generic's name }
  202. for i:=Length(genname) downto 1 do
  203. if genname[i]='$' then
  204. begin
  205. genname:=copy(genname,1,i-1);
  206. break;
  207. end;
  208. end;
  209. genname:=genname+'$'+countstr;
  210. ugenname:=upper(genname);
  211. if not searchsym(ugenname,srsym,st)
  212. or (srsym.typ<>typesym) then
  213. begin
  214. identifier_not_found(genname);
  215. genericdeflist.Free;
  216. generictypelist.Free;
  217. exit;
  218. end;
  219. { we've found the correct def }
  220. genericdef:=tstoreddef(ttypesym(srsym).typedef);
  221. { build the new type's name }
  222. specializename:=genname+specializename;
  223. uspecializename:=upper(specializename);
  224. prettyname:=genericdef.typesym.prettyname+'<'+prettyname+'>';
  225. { select the symtable containing the params }
  226. case genericdef.typ of
  227. procdef:
  228. st:=genericdef.GetSymtable(gs_para);
  229. objectdef,
  230. recorddef:
  231. st:=genericdef.GetSymtable(gs_record);
  232. arraydef:
  233. st:=tarraydef(genericdef).symtable;
  234. procvardef:
  235. st:=genericdef.GetSymtable(gs_para);
  236. else
  237. internalerror(200511182);
  238. end;
  239. { build the list containing the types for the generic params }
  240. gencount:=0;
  241. for i:=0 to st.SymList.Count-1 do
  242. begin
  243. srsym:=tsym(st.SymList[i]);
  244. if sp_generic_para in srsym.symoptions then
  245. begin
  246. if gencount=genericdeflist.Count then
  247. internalerror(2011042702);
  248. generictype:=ttypesym.create(srsym.realname,tdef(genericdeflist[gencount]));
  249. generictypelist.add(generictype);
  250. inc(gencount);
  251. end;
  252. end;
  253. { Special case if we are referencing the current defined object }
  254. if assigned(current_structdef) and
  255. (current_structdef.objname^=uspecializename) then
  256. tt:=current_structdef;
  257. { decide in which symtable to put the specialization }
  258. if current_module.is_unit and current_module.in_interface then
  259. specializest:=current_module.globalsymtable
  260. else
  261. specializest:=current_module.localsymtable;
  262. { Can we reuse an already specialized type? }
  263. if not assigned(tt) then
  264. begin
  265. srsym:=tsym(specializest.find(uspecializename));
  266. if assigned(srsym) then
  267. begin
  268. if srsym.typ<>typesym then
  269. internalerror(200710171);
  270. tt:=ttypesym(srsym).typedef;
  271. end;
  272. end;
  273. if not assigned(tt) then
  274. begin
  275. { Setup symtablestack at definition time
  276. to get types right, however this is not perfect, we should probably record
  277. the resolved symbols }
  278. oldsymtablestack:=symtablestack;
  279. oldextendeddefs:=current_module.extendeddefs;
  280. current_module.extendeddefs:=TFPHashObjectList.create(true);
  281. symtablestack:=tdefawaresymtablestack.create;
  282. if not assigned(genericdef) then
  283. internalerror(200705151);
  284. hmodule:=find_module_from_symtable(genericdef.owner);
  285. if hmodule=nil then
  286. internalerror(200705152);
  287. pu:=tused_unit(hmodule.used_units.first);
  288. while assigned(pu) do
  289. begin
  290. if not assigned(pu.u.globalsymtable) then
  291. internalerror(200705153);
  292. symtablestack.push(pu.u.globalsymtable);
  293. pu:=tused_unit(pu.next);
  294. end;
  295. if assigned(hmodule.globalsymtable) then
  296. symtablestack.push(hmodule.globalsymtable);
  297. { push the localsymtable if needed }
  298. if (hmodule<>current_module) or not current_module.in_interface then
  299. symtablestack.push(current_module.localsymtable);
  300. { push a temporary global symtable so that the specialization is
  301. added to the correct symtable; this symtable does not contain
  302. any other symbols, so that the type resolution can not be
  303. influenced by symbols in the current unit }
  304. tempst:=tspecializesymtable.create(current_module.modulename^,current_module.moduleid);
  305. symtablestack.push(tempst);
  306. { Reparse the original type definition }
  307. if not err then
  308. begin
  309. if parse_class_parent then
  310. begin
  311. old_current_structdef:=current_structdef;
  312. old_current_genericdef:=current_genericdef;
  313. old_current_specializedef:=current_specializedef;
  314. if genericdef.owner.symtabletype in [recordsymtable,objectsymtable] then
  315. current_structdef:=tabstractrecorddef(genericdef.owner.defowner)
  316. else
  317. current_structdef:=nil;
  318. current_genericdef:=nil;
  319. current_specializedef:=nil;
  320. end;
  321. { First a new typesym so we can reuse this specialization and
  322. references to this specialization can be handled }
  323. srsym:=ttypesym.create(specializename,generrordef);
  324. specializest.insert(srsym);
  325. { specializations are declarations as such it is the wised to
  326. declare set the blocktype to "type"; otherwise we'll
  327. experience unexpected side effects like the addition of
  328. classrefdefs if we have a generic that's derived from another
  329. generic }
  330. old_block_type:=block_type;
  331. block_type:=bt_type;
  332. if not assigned(genericdef.generictokenbuf) then
  333. internalerror(200511171);
  334. current_scanner.startreplaytokens(genericdef.generictokenbuf,
  335. genericdef.change_endian);
  336. read_named_type(tt,specializename,genericdef,generictypelist,false);
  337. ttypesym(srsym).typedef:=tt;
  338. tt.typesym:=srsym;
  339. if _prettyname<>'' then
  340. ttypesym(tt.typesym).fprettyname:=_prettyname
  341. else
  342. ttypesym(tt.typesym).fprettyname:=prettyname;
  343. { Note regarding hint directives:
  344. There is no need to remove the flags for them from the
  345. specialized generic symbol, because hint directives that
  346. follow the specialization are handled by the code in
  347. pdecl.types_dec and added to the type symbol.
  348. E.g.: TFoo = TBar<Blubb> deprecated;
  349. Here the symbol TBar$1$Blubb will contain the
  350. "sp_hint_deprecated" flag while the TFoo symbol won't.}
  351. case tt.typ of
  352. { Build VMT indexes for classes and read hint directives }
  353. objectdef:
  354. begin
  355. try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
  356. consume(_SEMICOLON);
  357. vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt));
  358. vmtbuilder.generate_vmt;
  359. vmtbuilder.free;
  360. end;
  361. { handle params, calling convention, etc }
  362. procvardef:
  363. begin
  364. if not check_proc_directive(true) then
  365. begin
  366. try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg);
  367. consume(_SEMICOLON);
  368. end;
  369. parse_var_proc_directives(ttypesym(srsym));
  370. handle_calling_convention(tprocvardef(tt));
  371. if try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg) then
  372. consume(_SEMICOLON);
  373. end;
  374. else
  375. { parse hint directives for records and arrays }
  376. begin
  377. try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
  378. consume(_SEMICOLON);
  379. end;
  380. end;
  381. { Consume the semicolon if it is also recorded }
  382. try_to_consume(_SEMICOLON);
  383. block_type:=old_block_type;
  384. if parse_class_parent then
  385. begin
  386. current_structdef:=old_current_structdef;
  387. current_genericdef:=old_current_genericdef;
  388. current_specializedef:=old_current_specializedef;
  389. end;
  390. end;
  391. { extract all created symbols and defs from the temporary symtable
  392. and add them to the specializest }
  393. for i:=0 to tempst.SymList.Count-1 do begin
  394. item:=tempst.SymList.Items[i];
  395. specializest.SymList.Add(tempst.SymList.NameOfIndex(i),item);
  396. tsym(item).Owner:=specializest;
  397. tempst.SymList.Extract(item);
  398. end;
  399. for i:=0 to tempst.DefList.Count-1 do begin
  400. item:=tempst.DefList.Items[i];
  401. specializest.DefList.Add(item);
  402. tdef(item).owner:=specializest;
  403. tempst.DefList.Extract(item);
  404. end;
  405. tempst.free;
  406. { Restore symtablestack }
  407. current_module.extendeddefs.free;
  408. current_module.extendeddefs:=oldextendeddefs;
  409. symtablestack.free;
  410. symtablestack:=oldsymtablestack;
  411. end;
  412. if not (token in [_GT, _RSHARPBRACKET]) then
  413. begin
  414. consume(_RSHARPBRACKET);
  415. exit;
  416. end
  417. else
  418. consume(token);
  419. genericdeflist.free;
  420. generictypelist.free;
  421. if assigned(genericdef) then
  422. begin
  423. { check the hints of the found generic symbol }
  424. srsym:=genericdef.typesym;
  425. check_hints(srsym,srsym.symoptions,srsym.deprecatedmsg);
  426. end;
  427. end;
  428. function parse_generic_parameters:TFPObjectList;
  429. var
  430. generictype : ttypesym;
  431. begin
  432. result:=TFPObjectList.Create(false);
  433. repeat
  434. if token=_ID then
  435. begin
  436. generictype:=ttypesym.create(orgpattern,cundefinedtype);
  437. include(generictype.symoptions,sp_generic_para);
  438. result.add(generictype);
  439. end;
  440. consume(_ID);
  441. until not try_to_consume(_COMMA) ;
  442. end;
  443. procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
  444. var
  445. i: longint;
  446. generictype: ttypesym;
  447. st: tsymtable;
  448. begin
  449. def.genericdef:=genericdef;
  450. if not assigned(genericlist) then
  451. exit;
  452. case def.typ of
  453. recorddef,objectdef: st:=tabstractrecorddef(def).symtable;
  454. arraydef: st:=tarraydef(def).symtable;
  455. procvardef,procdef: st:=tabstractprocdef(def).parast;
  456. else
  457. internalerror(201101020);
  458. end;
  459. for i:=0 to genericlist.count-1 do
  460. begin
  461. generictype:=ttypesym(genericlist[i]);
  462. if generictype.typedef.typ=undefineddef then
  463. include(def.defoptions,df_generic)
  464. else
  465. include(def.defoptions,df_specialization);
  466. st.insert(generictype);
  467. end;
  468. end;
  469. end.