pgenutil.pas 20 KB

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