optdfa.pas 34 KB

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