optdfa.pas 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. {
  2. DFA
  3. Copyright (c) 2007 by Florian Klaempfl
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. { $define DEBUG_DFA}
  18. { $define EXTDEBUG_DFA}
  19. { this unit implements routines to perform dfa }
  20. unit optdfa;
  21. {$i fpcdefs.inc}
  22. interface
  23. uses
  24. node,optutils;
  25. type
  26. TDFABuilder = class
  27. protected
  28. procedure CreateLifeInfo(node : tnode;map : TIndexedNodeSet);
  29. public
  30. resultnode : tnode;
  31. nodemap : TIndexedNodeSet;
  32. { reset all dfa info, this is required before creating dfa info
  33. if the tree has been changed without updating dfa }
  34. procedure resetdfainfo(node : tnode);
  35. procedure createdfainfo(node : tnode);
  36. destructor destroy;override;
  37. end;
  38. procedure CheckAndWarn(code : tnode;nodetosearch : tnode);
  39. implementation
  40. uses
  41. globtype,globals,
  42. verbose,
  43. cpuinfo,
  44. symconst,symdef,symsym,
  45. defutil,
  46. procinfo,
  47. nutils,htypechk,
  48. nbas,nflw,ncon,ninl,ncal,nset,nld,nadd,
  49. optbase;
  50. (*
  51. function initnodes(var n:tnode; arg: pointer) : foreachnoderesult;
  52. begin
  53. { node worth to add? }
  54. if (node_complexity(n)>1) and (tstoreddef(n.resultdef).is_intregable or tstoreddef(n.resultdef).is_fpuregable) then
  55. begin
  56. plists(arg)^.nodelist.Add(n);
  57. plists(arg)^.locationlist.Add(@n);
  58. result:=fen_false;
  59. end
  60. else
  61. result:=fen_norecurse_false;
  62. end;
  63. *)
  64. {
  65. x:=f; read: [f]
  66. while x do read: []
  67. a:=b; read: [a,b,d] def: [a] life: read*def=[a]
  68. c:=d; read: [a,d] def: [a,c] life: read*def=[a]
  69. e:=a; read: [a] def: [a,c,e] life: read*def=[a]
  70. function f(b,d,x : type) : type;
  71. begin
  72. while x do alive: b,d,x
  73. begin
  74. a:=b; alive: b,d,x
  75. c:=d; alive: a,d,x
  76. e:=a+c; alive: a,c,x
  77. dec(x); alive: c,e,x
  78. end;
  79. result:=c+e; alive: c,e
  80. end; alive: result
  81. }
  82. type
  83. tdfainfo = record
  84. use : PDFASet;
  85. def : PDFASet;
  86. map : TIndexedNodeSet
  87. end;
  88. pdfainfo = ^tdfainfo;
  89. function AddDefUse(var n: tnode; arg: pointer): foreachnoderesult;
  90. begin
  91. case n.nodetype of
  92. tempcreaten:
  93. begin
  94. if assigned(ttempcreatenode(n).tempinfo^.tempinitcode) then
  95. begin
  96. pdfainfo(arg)^.map.Add(n);
  97. DFASetInclude(pdfainfo(arg)^.def^,n.optinfo^.index);
  98. end;
  99. end;
  100. temprefn,
  101. loadn:
  102. begin
  103. pdfainfo(arg)^.map.Add(n);
  104. if nf_modify in n.flags then
  105. begin
  106. DFASetInclude(pdfainfo(arg)^.use^,n.optinfo^.index);
  107. DFASetInclude(pdfainfo(arg)^.def^,n.optinfo^.index)
  108. end
  109. else if nf_write in n.flags then
  110. DFASetInclude(pdfainfo(arg)^.def^,n.optinfo^.index)
  111. else
  112. DFASetInclude(pdfainfo(arg)^.use^,n.optinfo^.index);
  113. end;
  114. end;
  115. result:=fen_false;
  116. end;
  117. function ResetProcessing(var n: tnode; arg: pointer): foreachnoderesult;
  118. begin
  119. exclude(n.flags,nf_processing);
  120. { dfa works only on normalized trees, so do not recurse into expressions, because
  121. ResetProcessing eats a signififcant amount of time of CheckAndWarn
  122. the following set contains (hopefully) most of the expression nodes }
  123. if n.nodetype in [calln,inlinen,assignn,callparan,andn,addn,orn,subn,muln,divn,slashn,notn,equaln,unequaln,gtn,ltn,lten,gten,loadn,
  124. typeconvn,vecn,subscriptn,addrn,derefn] then
  125. result:=fen_norecurse_false
  126. else
  127. result:=fen_false;
  128. end;
  129. function ResetDFA(var n: tnode; arg: pointer): foreachnoderesult;
  130. begin
  131. if assigned(n.optinfo) then
  132. begin
  133. with n.optinfo^ do
  134. begin
  135. life:=nil;
  136. def:=nil;
  137. use:=nil;
  138. defsum:=nil;
  139. end;
  140. end;
  141. result:=fen_false;
  142. end;
  143. procedure TDFABuilder.CreateLifeInfo(node : tnode;map : TIndexedNodeSet);
  144. var
  145. changed : boolean;
  146. procedure CreateInfo(node : tnode);
  147. { update life entry of a node with l, set changed if this changes
  148. life info for the node
  149. }
  150. procedure updatelifeinfo(n : tnode;l : TDFASet);
  151. var
  152. b : boolean;
  153. begin
  154. b:=DFASetNotEqual(l,n.optinfo^.life);
  155. {$ifdef DEBUG_DFA}
  156. if not(changed) and b then
  157. begin
  158. writeln('Another DFA pass caused by: ',nodetype2str[n.nodetype],'(',n.fileinfo.line,',',n.fileinfo.column,')');
  159. write(' Life info set was: ');PrintDFASet(Output,n.optinfo^.life);writeln;
  160. write(' Life info set will be: ');PrintDFASet(Output,l);writeln;
  161. end;
  162. {$endif DEBUG_DFA}
  163. changed:=changed or b;
  164. n.optinfo^.life:=l;
  165. end;
  166. procedure calclife(n : tnode);
  167. var
  168. l : TDFASet;
  169. begin
  170. if assigned(n.successor) then
  171. begin
  172. { ensure we can access optinfo }
  173. DFASetDiff(l,n.successor.optinfo^.life,n.optinfo^.def);
  174. DFASetIncludeSet(l,n.optinfo^.use);
  175. DFASetIncludeSet(l,n.optinfo^.life);
  176. end
  177. else
  178. begin
  179. l:=n.optinfo^.use;
  180. DFASetIncludeSet(l,n.optinfo^.life);
  181. end;
  182. updatelifeinfo(n,l);
  183. end;
  184. var
  185. dfainfo : tdfainfo;
  186. l : TDFASet;
  187. save: TDFASet;
  188. i : longint;
  189. counteruse_after_loop : boolean;
  190. begin
  191. if node=nil then
  192. exit;
  193. { ensure we've already optinfo set }
  194. node.allocoptinfo;
  195. if nf_processing in node.flags then
  196. exit;
  197. include(node.flags,nf_processing);
  198. if assigned(node.successor) then
  199. CreateInfo(node.successor);
  200. {$ifdef EXTDEBUG_DFA}
  201. writeln('Handling: ',nodetype2str[node.nodetype],'(',node.fileinfo.line,',',node.fileinfo.column,')');
  202. {$endif EXTDEBUG_DFA}
  203. { life:=succesorlive-definition+use }
  204. case node.nodetype of
  205. whilerepeatn:
  206. begin
  207. { analyze the loop condition }
  208. if not(assigned(node.optinfo^.def)) and
  209. not(assigned(node.optinfo^.use)) then
  210. begin
  211. dfainfo.use:[email protected]^.use;
  212. dfainfo.def:[email protected]^.def;
  213. dfainfo.map:=map;
  214. foreachnodestatic(pm_postprocess,twhilerepeatnode(node).left,@AddDefUse,@dfainfo);
  215. end;
  216. { NB: this node should typically have empty def set }
  217. if assigned(node.successor) then
  218. DFASetDiff(l,node.successor.optinfo^.life,node.optinfo^.def)
  219. else if assigned(resultnode) then
  220. DFASetDiff(l,resultnode.optinfo^.life,node.optinfo^.def)
  221. else
  222. l:=nil;
  223. { for repeat..until, node use set in included at the end of loop }
  224. if not (lnf_testatbegin in twhilerepeatnode(node).loopflags) then
  225. DFASetIncludeSet(l,node.optinfo^.use);
  226. DFASetIncludeSet(l,node.optinfo^.life);
  227. save:=node.optinfo^.life;
  228. { to process body correctly, we need life info in place (because
  229. whilerepeatnode is successor of its body). }
  230. node.optinfo^.life:=l;
  231. { now process the body }
  232. CreateInfo(twhilerepeatnode(node).right);
  233. { restore, to prevent infinite recursion via changed flag }
  234. node.optinfo^.life:=save;
  235. { for while loops, node use set is included at the beginning of loop }
  236. l:=twhilerepeatnode(node).right.optinfo^.life;
  237. if lnf_testatbegin in twhilerepeatnode(node).loopflags then
  238. DFASetIncludeSet(l,node.optinfo^.use);
  239. UpdateLifeInfo(node,l);
  240. { ... and a second iteration for fast convergence }
  241. CreateInfo(twhilerepeatnode(node).right);
  242. end;
  243. forn:
  244. begin
  245. {
  246. left: loopvar
  247. right: from
  248. t1: to
  249. t2: body
  250. }
  251. node.allocoptinfo;
  252. tfornode(node).loopiteration.allocoptinfo;
  253. if not(assigned(node.optinfo^.def)) and
  254. not(assigned(node.optinfo^.use)) then
  255. begin
  256. dfainfo.use:[email protected]^.use;
  257. dfainfo.def:[email protected]^.def;
  258. dfainfo.map:=map;
  259. foreachnodestatic(pm_postprocess,tfornode(node).left,@AddDefUse,@dfainfo);
  260. foreachnodestatic(pm_postprocess,tfornode(node).right,@AddDefUse,@dfainfo);
  261. foreachnodestatic(pm_postprocess,tfornode(node).t1,@AddDefUse,@dfainfo);
  262. end;
  263. { create life for the body }
  264. CreateInfo(tfornode(node).t2);
  265. { is the counter living after the loop?
  266. if left is a record element, it might not be tracked by dfa, so
  267. optinfo might not be assigned
  268. }
  269. counteruse_after_loop:=assigned(tfornode(node).left.optinfo) and assigned(node.successor) and
  270. DFASetIn(node.successor.optinfo^.life,tfornode(node).left.optinfo^.index);
  271. { if yes, then we should warn }
  272. { !!!!!! }
  273. { first update the dummy node }
  274. { get the life of the loop block }
  275. l:=copy(tfornode(node).t2.optinfo^.life);
  276. { take care of the sucessor }
  277. if assigned(node.successor) then
  278. DFASetIncludeSet(l,node.successor.optinfo^.life);
  279. { the counter variable is living as well inside the for loop
  280. if left is a record element, it might not be tracked by dfa, so
  281. optinfo might not be assigned
  282. }
  283. if assigned(tfornode(node).left.optinfo) then
  284. DFASetInclude(l,tfornode(node).left.optinfo^.index);
  285. { force block node life info }
  286. UpdateLifeInfo(tfornode(node).loopiteration,l);
  287. { now update the for node itself }
  288. { get the life of the loop block }
  289. l:=copy(tfornode(node).t2.optinfo^.life);
  290. { take care of the sucessor as it's possible that we don't have one execution of the body }
  291. if (not(tfornode(node).right.nodetype=ordconstn) or not(tfornode(node).t1.nodetype=ordconstn)) and
  292. assigned(node.successor) then
  293. DFASetIncludeSet(l,node.successor.optinfo^.life);
  294. {
  295. the counter variable is not living at the entry of the for node
  296. if left is a record element, it might not be tracked by dfa, so
  297. optinfo might not be assigned
  298. }
  299. if assigned(tfornode(node).left.optinfo) then
  300. DFASetExclude(l,tfornode(node).left.optinfo^.index);
  301. { ... but it could be that left/right use it, so do this after
  302. removing the def of the counter variable }
  303. DFASetIncludeSet(l,node.optinfo^.use);
  304. UpdateLifeInfo(node,l);
  305. { ... and a second iteration for fast convergence }
  306. CreateInfo(tfornode(node).t2);
  307. end;
  308. temprefn,
  309. loadn,
  310. typeconvn,
  311. assignn:
  312. begin
  313. if not(assigned(node.optinfo^.def)) and
  314. not(assigned(node.optinfo^.use)) then
  315. begin
  316. dfainfo.use:[email protected]^.use;
  317. dfainfo.def:[email protected]^.def;
  318. dfainfo.map:=map;
  319. foreachnodestatic(pm_postprocess,node,@AddDefUse,@dfainfo);
  320. end;
  321. calclife(node);
  322. end;
  323. statementn:
  324. begin
  325. { nested statement }
  326. CreateInfo(tstatementnode(node).statement);
  327. { inherit info }
  328. node.optinfo^.life:=tstatementnode(node).statement.optinfo^.life;
  329. end;
  330. blockn:
  331. begin
  332. CreateInfo(tblocknode(node).statements);
  333. { ensure that we don't remove life info }
  334. l:=node.optinfo^.life;
  335. if assigned(node.successor) then
  336. DFASetIncludeSet(l,node.successor.optinfo^.life);
  337. UpdateLifeInfo(node,l);
  338. end;
  339. ifn:
  340. begin
  341. { get information from cond. expression }
  342. if not(assigned(node.optinfo^.def)) and
  343. not(assigned(node.optinfo^.use)) then
  344. begin
  345. dfainfo.use:[email protected]^.use;
  346. dfainfo.def:[email protected]^.def;
  347. dfainfo.map:=map;
  348. foreachnodestatic(pm_postprocess,tifnode(node).left,@AddDefUse,@dfainfo);
  349. end;
  350. { create life info for then and else node }
  351. CreateInfo(tifnode(node).right);
  352. CreateInfo(tifnode(node).t1);
  353. { ensure that we don't remove life info }
  354. l:=node.optinfo^.life;
  355. { get life info from then branch }
  356. if assigned(tifnode(node).right) then
  357. DFASetIncludeSet(l,tifnode(node).right.optinfo^.life);
  358. { get life info from else branch }
  359. if assigned(tifnode(node).t1) then
  360. DFASetIncludeSet(l,tifnode(node).t1.optinfo^.life)
  361. else if assigned(node.successor) then
  362. DFASetIncludeSet(l,node.successor.optinfo^.life);
  363. { remove def info from the cond. expression }
  364. DFASetExcludeSet(l,tifnode(node).optinfo^.def);
  365. { add use info from the cond. expression }
  366. DFASetIncludeSet(l,tifnode(node).optinfo^.use);
  367. { finally, update the life info of the node }
  368. UpdateLifeInfo(node,l);
  369. end;
  370. casen:
  371. begin
  372. { get information from "case" expression }
  373. if not(assigned(node.optinfo^.def)) and
  374. not(assigned(node.optinfo^.use)) then
  375. begin
  376. dfainfo.use:[email protected]^.use;
  377. dfainfo.def:[email protected]^.def;
  378. dfainfo.map:=map;
  379. foreachnodestatic(pm_postprocess,tcasenode(node).left,@AddDefUse,@dfainfo);
  380. end;
  381. { create life info for block and else nodes }
  382. for i:=0 to tcasenode(node).blocks.count-1 do
  383. CreateInfo(pcaseblock(tcasenode(node).blocks[i])^.statement);
  384. CreateInfo(tcasenode(node).elseblock);
  385. { ensure that we don't remove life info }
  386. l:=node.optinfo^.life;
  387. { get life info from case branches }
  388. for i:=0 to tcasenode(node).blocks.count-1 do
  389. DFASetIncludeSet(l,pcaseblock(tcasenode(node).blocks[i])^.statement.optinfo^.life);
  390. { get life info from else branch or the succesor }
  391. if assigned(tcasenode(node).elseblock) then
  392. DFASetIncludeSet(l,tcasenode(node).elseblock.optinfo^.life)
  393. else if assigned(node.successor) then
  394. DFASetIncludeSet(l,node.successor.optinfo^.life);
  395. { add use info from the "case" expression }
  396. DFASetIncludeSet(l,tcasenode(node).optinfo^.use);
  397. { finally, update the life info of the node }
  398. UpdateLifeInfo(node,l);
  399. end;
  400. exitn:
  401. begin
  402. if not(is_void(current_procinfo.procdef.returndef)) then
  403. begin
  404. if not(assigned(node.optinfo^.def)) and
  405. not(assigned(node.optinfo^.use)) then
  406. begin
  407. if assigned(texitnode(node).left) then
  408. begin
  409. node.optinfo^.def:=resultnode.optinfo^.def;
  410. dfainfo.use:[email protected]^.use;
  411. dfainfo.def:[email protected]^.def;
  412. dfainfo.map:=map;
  413. foreachnodestatic(pm_postprocess,texitnode(node).left,@AddDefUse,@dfainfo);
  414. calclife(node);
  415. end
  416. else
  417. begin
  418. { get info from faked resultnode }
  419. node.optinfo^.use:=resultnode.optinfo^.use;
  420. node.optinfo^.life:=node.optinfo^.use;
  421. changed:=true;
  422. end;
  423. end;
  424. end;
  425. end;
  426. {$ifdef JVM}
  427. { all other platforms except jvm translate raise nodes into call nodes during pass_1 }
  428. raisen,
  429. {$endif JVM}
  430. asn,
  431. inlinen,
  432. calln:
  433. begin
  434. if not(assigned(node.optinfo^.def)) and
  435. not(assigned(node.optinfo^.use)) then
  436. begin
  437. dfainfo.use:[email protected]^.use;
  438. dfainfo.def:[email protected]^.def;
  439. dfainfo.map:=map;
  440. foreachnodestatic(pm_postprocess,node,@AddDefUse,@dfainfo);
  441. end;
  442. calclife(node);
  443. end;
  444. labeln:
  445. begin
  446. calclife(node);
  447. if assigned(tlabelnode(node).left) then
  448. begin
  449. l:=node.optinfo^.life;
  450. DFASetIncludeSet(l,tlabelnode(node).optinfo^.life);
  451. UpdateLifeInfo(node,l);
  452. end;
  453. end;
  454. tempcreaten,
  455. tempdeleten,
  456. nothingn,
  457. continuen,
  458. goton,
  459. breakn:
  460. begin
  461. calclife(node);
  462. end;
  463. else
  464. internalerror(2007050502);
  465. end;
  466. end;
  467. var
  468. runs : integer;
  469. begin
  470. runs:=0;
  471. repeat
  472. inc(runs);
  473. changed:=false;
  474. CreateInfo(node);
  475. foreachnodestatic(pm_postprocess,node,@ResetProcessing,nil);
  476. { the result node is not reached by foreachnodestatic }
  477. exclude(resultnode.flags,nf_processing);
  478. {$ifdef DEBUG_DFA}
  479. PrintIndexedNodeSet(output,map);
  480. PrintDFAInfo(output,node);
  481. {$endif DEBUG_DFA}
  482. until not(changed);
  483. {$ifdef DEBUG_DFA}
  484. writeln('DFA solver iterations: ',runs);
  485. {$endif DEBUG_DFA}
  486. end;
  487. { reset all dfa info, this is required before creating dfa info
  488. if the tree has been changed without updating dfa }
  489. procedure TDFABuilder.resetdfainfo(node : tnode);
  490. begin
  491. foreachnodestatic(pm_postprocess,node,@ResetDFA,nil);
  492. end;
  493. procedure TDFABuilder.createdfainfo(node : tnode);
  494. var
  495. dfarec : tdfainfo;
  496. begin
  497. if not(assigned(nodemap)) then
  498. nodemap:=TIndexedNodeSet.Create;
  499. { create a fake node using the result which will be the last node }
  500. if not(is_void(current_procinfo.procdef.returndef)) then
  501. begin
  502. if current_procinfo.procdef.proctypeoption=potype_constructor then
  503. resultnode:=load_self_node
  504. else
  505. resultnode:=load_result_node;
  506. resultnode.allocoptinfo;
  507. dfarec.use:[email protected]^.use;
  508. dfarec.def:[email protected]^.def;
  509. dfarec.map:=nodemap;
  510. AddDefUse(resultnode,@dfarec);
  511. resultnode.optinfo^.life:=resultnode.optinfo^.use;
  512. end
  513. else
  514. begin
  515. resultnode:=cnothingnode.create;
  516. resultnode.allocoptinfo;
  517. end;
  518. { add controll flow information }
  519. SetNodeSucessors(node,resultnode);
  520. { now, collect life information }
  521. CreateLifeInfo(node,nodemap);
  522. end;
  523. destructor TDFABuilder.Destroy;
  524. begin
  525. Resultnode.free;
  526. nodemap.free;
  527. inherited destroy;
  528. end;
  529. var
  530. { we have to pass the address of SearchNode in a call inside of SearchNode:
  531. @SearchNode does not work because the compiler thinks we take the address of the result
  532. so store the address from outside }
  533. SearchNodeProcPointer : function(var n: tnode; arg: pointer): foreachnoderesult;
  534. type
  535. { helper structure to be able to pass more than one variable to the iterator function }
  536. TSearchNodeInfo = record
  537. nodetosearch : tnode;
  538. { this contains a list of all file locations where a warning was thrown already,
  539. the same location might appear multiple times because nodes might have been copied }
  540. warnedfilelocs : array of tfileposinfo;
  541. end;
  542. PSearchNodeInfo = ^TSearchNodeInfo;
  543. { searches for a given node n and warns if the node is found as being uninitialized. If a node is
  544. found, searching is stopped so each call issues only one warning/hint }
  545. function SearchNode(var n: tnode; arg: pointer): foreachnoderesult;
  546. function WarnedForLocation(f : tfileposinfo) : boolean;
  547. var
  548. i : longint;
  549. begin
  550. result:=true;
  551. for i:=0 to high(PSearchNodeInfo(arg)^.warnedfilelocs) do
  552. with PSearchNodeInfo(arg)^.warnedfilelocs[i] do
  553. begin
  554. if (f.column=column) and (f.fileindex=fileindex) and (f.line=line) and (f.moduleindex=moduleindex) then
  555. exit;
  556. end;
  557. result:=false;
  558. end;
  559. procedure AddFilepos(const f : tfileposinfo);
  560. begin
  561. Setlength(PSearchNodeInfo(arg)^.warnedfilelocs,length(PSearchNodeInfo(arg)^.warnedfilelocs)+1);
  562. PSearchNodeInfo(arg)^.warnedfilelocs[high(PSearchNodeInfo(arg)^.warnedfilelocs)]:=f;
  563. end;
  564. var
  565. varsym : tabstractnormalvarsym;
  566. methodpointer,
  567. hpt : tnode;
  568. begin
  569. result:=fen_false;
  570. case n.nodetype of
  571. callparan:
  572. begin
  573. { do not warn about variables passed by var, just issue a hint, this
  574. is a workaround for old code e.g. using fillchar }
  575. if assigned(tcallparanode(n).parasym) and (tcallparanode(n).parasym.varspez in [vs_var,vs_out]) then
  576. begin
  577. hpt:=tcallparanode(n).left;
  578. while assigned(hpt) and (hpt.nodetype in [subscriptn,vecn,typeconvn]) do
  579. hpt:=tunarynode(hpt).left;
  580. if assigned(hpt) and (hpt.nodetype=loadn) and not(WarnedForLocation(hpt.fileinfo)) and
  581. { warn only on the current symtable level }
  582. (((tabstractnormalvarsym(tloadnode(hpt).symtableentry).owner=current_procinfo.procdef.localst) and
  583. (current_procinfo.procdef.localst.symtablelevel=tabstractnormalvarsym(tloadnode(hpt).symtableentry).owner.symtablelevel)
  584. ) or
  585. ((tabstractnormalvarsym(tloadnode(hpt).symtableentry).owner=current_procinfo.procdef.parast) and
  586. (current_procinfo.procdef.parast.symtablelevel=tabstractnormalvarsym(tloadnode(hpt).symtableentry).owner.symtablelevel)
  587. )
  588. ) and
  589. PSearchNodeInfo(arg)^.nodetosearch.isequal(hpt) then
  590. begin
  591. { issue only a hint for var, when encountering the node passed as out, we need only to stop searching }
  592. if tcallparanode(n).parasym.varspez=vs_var then
  593. UninitializedVariableMessage(hpt.fileinfo,false,
  594. tloadnode(hpt).symtable.symtabletype=localsymtable,
  595. is_managed_type(tloadnode(hpt).resultdef),
  596. tloadnode(hpt).symtableentry.RealName);
  597. AddFilepos(hpt.fileinfo);
  598. result:=fen_norecurse_true;
  599. end
  600. end;
  601. end;
  602. orn,
  603. andn:
  604. begin
  605. { take care of short boolean evaluation: if the expression to be search is found in left,
  606. we do not need to search right }
  607. if foreachnodestatic(pm_postprocess,taddnode(n).left,SearchNodeProcPointer,arg) or
  608. foreachnodestatic(pm_postprocess,taddnode(n).right,SearchNodeProcPointer,arg) then
  609. result:=fen_norecurse_true
  610. else
  611. result:=fen_norecurse_false;
  612. end;
  613. calln:
  614. begin
  615. methodpointer:=tcallnode(n).methodpointer;
  616. if assigned(methodpointer) and (methodpointer.nodetype<>typen) then
  617. begin
  618. { Remove all postfix operators }
  619. hpt:=methodpointer;
  620. while assigned(hpt) and (hpt.nodetype in [subscriptn,vecn]) do
  621. hpt:=tunarynode(hpt).left;
  622. { skip (absolute and other simple) type conversions -- only now,
  623. because the checks above have to take type conversions into
  624. e.g. class reference types account }
  625. hpt:=actualtargetnode(@hpt)^;
  626. { R.Init then R will be initialized by the constructor,
  627. Also allow it for simple loads }
  628. if (tcallnode(n).procdefinition.proctypeoption=potype_constructor) or
  629. (PSearchNodeInfo(arg)^.nodetosearch.isequal(hpt) and
  630. (((methodpointer.resultdef.typ=objectdef) and
  631. not(oo_has_virtual in tobjectdef(methodpointer.resultdef).objectoptions)) or
  632. (methodpointer.resultdef.typ=recorddef)
  633. )
  634. ) then
  635. begin
  636. { don't warn about the method pointer }
  637. AddFilepos(hpt.fileinfo);
  638. if not(foreachnodestatic(pm_postprocess,tcallnode(n).left,SearchNodeProcPointer,arg)) then
  639. foreachnodestatic(pm_postprocess,tcallnode(n).right,SearchNodeProcPointer,arg);
  640. result:=fen_norecurse_true
  641. end;
  642. end;
  643. end;
  644. loadn:
  645. begin
  646. if (tloadnode(n).symtableentry.typ in [localvarsym,paravarsym,staticvarsym]) and
  647. PSearchNodeInfo(arg)^.nodetosearch.isequal(n) and ((nf_modify in n.flags) or not(nf_write in n.flags)) then
  648. begin
  649. varsym:=tabstractnormalvarsym(tloadnode(n).symtableentry);
  650. { Give warning/note for living locals, result and parameters, but only about the current
  651. symtables }
  652. if assigned(varsym.owner) and
  653. (((varsym.owner=current_procinfo.procdef.localst) and
  654. (current_procinfo.procdef.localst.symtablelevel=varsym.owner.symtablelevel)
  655. ) or
  656. ((varsym.owner=current_procinfo.procdef.parast) and
  657. (varsym.typ=paravarsym) and
  658. (current_procinfo.procdef.parast.symtablelevel=varsym.owner.symtablelevel) and
  659. { all parameters except out parameters are initialized by the caller }
  660. (tparavarsym(varsym).varspez=vs_out)
  661. ) or
  662. ((vo_is_funcret in varsym.varoptions) and
  663. (current_procinfo.procdef.parast.symtablelevel=varsym.owner.symtablelevel)
  664. )
  665. ) and
  666. not(vo_is_external in varsym.varoptions) then
  667. begin
  668. if (vo_is_funcret in varsym.varoptions) and not(WarnedForLocation(n.fileinfo)) then
  669. begin
  670. if is_managed_type(varsym.vardef) then
  671. MessagePos(n.fileinfo,sym_w_managed_function_result_uninitialized)
  672. else
  673. MessagePos(n.fileinfo,sym_w_function_result_uninitialized);
  674. AddFilepos(n.fileinfo);
  675. result:=fen_norecurse_true;
  676. end
  677. else
  678. begin
  679. { typed consts are initialized, further, warn only once per location }
  680. if not (vo_is_typed_const in varsym.varoptions) and not(WarnedForLocation(n.fileinfo)) then
  681. begin
  682. UninitializedVariableMessage(n.fileinfo,true,varsym.typ=localvarsym,is_managed_type(varsym.vardef),varsym.realname);
  683. AddFilepos(n.fileinfo);
  684. result:=fen_norecurse_true;
  685. end;
  686. end;
  687. end
  688. {$ifdef dummy}
  689. { if a the variable we are looking for is passed as a var parameter, we stop searching }
  690. else if assigned(varsym.owner) and
  691. (varsym.owner=current_procinfo.procdef.parast) and
  692. (varsym.typ=paravarsym) and
  693. (current_procinfo.procdef.parast.symtablelevel=varsym.owner.symtablelevel) and
  694. (tparavarsym(varsym).varspez=vs_var) then
  695. result:=fen_norecurse_true;
  696. {$endif dummy}
  697. end;
  698. end;
  699. end;
  700. end;
  701. procedure CheckAndWarn(code : tnode;nodetosearch : tnode);
  702. var
  703. SearchNodeInfo : TSearchNodeInfo;
  704. function DoCheck(node : tnode) : boolean;
  705. var
  706. i : longint;
  707. touchesnode : Boolean;
  708. procedure MaybeDoCheck(n : tnode);inline;
  709. begin
  710. Result:=Result or DoCheck(n);
  711. end;
  712. procedure MaybeSearchIn(n : tnode);
  713. begin
  714. if touchesnode then
  715. Result:=Result or foreachnodestatic(pm_postprocess,n,@SearchNode,@SearchNodeInfo);
  716. end;
  717. begin
  718. result:=false;
  719. if node=nil then
  720. exit;
  721. if nf_processing in node.flags then
  722. exit;
  723. include(node.flags,nf_processing);
  724. if not(DFASetIn(node.optinfo^.life,nodetosearch.optinfo^.index)) then
  725. exit;
  726. { we do not need this info always, so try to safe some time here, CheckAndWarn
  727. takes a lot of time anyways }
  728. if not(node.nodetype in [statementn,blockn]) then
  729. touchesnode:=DFASetIn(node.optinfo^.use,nodetosearch.optinfo^.index) or
  730. DFASetIn(node.optinfo^.def,nodetosearch.optinfo^.index)
  731. else
  732. touchesnode:=false;
  733. case node.nodetype of
  734. whilerepeatn:
  735. begin
  736. MaybeSearchIn(twhilerepeatnode(node).left);
  737. MaybeDoCheck(twhilerepeatnode(node).right);
  738. end;
  739. forn:
  740. begin
  741. MaybeSearchIn(tfornode(node).right);
  742. MaybeSearchIn(tfornode(node).t1);
  743. MaybeDoCheck(tfornode(node).t2);
  744. end;
  745. statementn:
  746. MaybeDoCheck(tstatementnode(node).statement);
  747. blockn:
  748. MaybeDoCheck(tblocknode(node).statements);
  749. ifn:
  750. begin
  751. MaybeSearchIn(tifnode(node).left);
  752. MaybeDoCheck(tifnode(node).right);
  753. MaybeDoCheck(tifnode(node).t1);
  754. end;
  755. casen:
  756. begin
  757. MaybeSearchIn(tcasenode(node).left);
  758. for i:=0 to tcasenode(node).blocks.count-1 do
  759. MaybeDoCheck(pcaseblock(tcasenode(node).blocks[i])^.statement);
  760. MaybeDoCheck(tcasenode(node).elseblock);
  761. end;
  762. labeln:
  763. MaybeDoCheck(tlabelnode(node).left);
  764. { we are aware of the following nodes so if new node types are added to the compiler
  765. and pop up in the search, the ie below kicks in as a reminder }
  766. exitn:
  767. begin
  768. MaybeSearchIn(texitnode(node).left);
  769. { exit uses the resultnode implicitly, so searching for a matching node is
  770. useless, if we reach the exit node and found the living node not in left, then
  771. it can be only the resultnode }
  772. if not(Result) and not(is_void(current_procinfo.procdef.returndef)) and
  773. not(assigned(texitnode(node).resultexpr)) and
  774. { don't warn about constructors }
  775. not(current_procinfo.procdef.proctypeoption in [potype_class_constructor,potype_constructor]) then
  776. begin
  777. if is_managed_type(current_procinfo.procdef.returndef) then
  778. MessagePos(node.fileinfo,sym_w_managed_function_result_uninitialized)
  779. else
  780. MessagePos(node.fileinfo,sym_w_function_result_uninitialized);
  781. Setlength(SearchNodeInfo.warnedfilelocs,length(SearchNodeInfo.warnedfilelocs)+1);
  782. SearchNodeInfo.warnedfilelocs[high(SearchNodeInfo.warnedfilelocs)]:=node.fileinfo;
  783. end
  784. end;
  785. { could be the implicitly generated load node for the result }
  786. {$ifdef JVM}
  787. { all other platforms except jvm translate raise nodes into call nodes during pass_1 }
  788. raisen,
  789. {$endif JVM}
  790. loadn,
  791. assignn,
  792. calln,
  793. temprefn,
  794. typeconvn,
  795. inlinen,
  796. tempcreaten,
  797. tempdeleten:
  798. MaybeSearchIn(node);
  799. nothingn,
  800. continuen,
  801. goton,
  802. breakn:
  803. ;
  804. else
  805. internalerror(2013111301);
  806. end;
  807. { if already a warning has been issued, then stop }
  808. if Result then
  809. exit;
  810. if assigned(node.successor) then
  811. MaybeDoCheck(node.successor);
  812. end;
  813. begin
  814. SearchNodeInfo.nodetosearch:=nodetosearch;
  815. DoCheck(code);
  816. foreachnodestatic(pm_postprocess,code,@ResetProcessing,nil);
  817. end;
  818. begin
  819. SearchNodeProcPointer:=@SearchNode;
  820. end.