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