The magic “procedure Touch(var …” construct
Posted by jpluimers on 2021/02/16
procedure Touch(var Argument);
begin
end;
I included the above code in my blog a long time ago (2014 in fact: Delphi: always watch the compiler Warnings), but never got around to explain the why and how I got it, why it works and why it will likely work forever.
Background
Ever since the early Delphi days, there are three hints about “never used” of which the second often gets in the way during debugging:
- [WayBack] H2077: Value assigned to ‘%s’ never used
- [WayBack] H2164: Variable ‘%s’ is declared but never used in ‘%s’
- [WayBack] H2219: Private symbol ‘%s’ declared but never used
(note that these %s
only hold for non-managed types, which I also addressed in Why don’t I get the warning W1036 Variable “‘MyStrings’ might not have been initialized”… and Delphi 10.3 Rio got released; I’ll wait a while hoping to see more positive comments).
Usually the compiler is right, but sometimes it is not: [WayBack] Check your compiler warnings and hints. They may still be errors. | Shiftkey Software Blog
So once every while, you need this workaround:
Solution
The solution is to have a method with one untyped var
parameter (a var
parameter without any type: this way you can pass any field or variable to it) that just does nothing. Often I included only at the place I need it as this single line fragment: procedure Touch(var Argument); begin end;
.
Former Delphi compiler engineer and Borland Chief Schientist Danny Thorpe handed this solution, I think it was during or around his famous BorCon99 in Philadelphi (and later BorCon2005 in San Jose) Reading Tea Leaves: The Fine Art of Debugging talk. The talk is not-online, but luckily there are notes and a StackOverflow post:
- [WayBack] ANN: BDN News – October2005 – DeXter and the Developer Conference(s) 2005
- WayBack: Conferences » 2005 DevCon » IDE Abstract; Reading Tea Leaves: The Fine Art of Debugging
- [WayBack] c++ – My multithread program works slowly or appear deadlock on dual core machine, please help – Stack Overflow
- [WayBack] multithreading – Real World Examples of read-write in concurrent software – Stack Overflow
- [WayBack] c – Symbol eliminated by linker (Delphi) – Stack Overflow
- [WayBack] Thoughts from BorCon04 – Brad Abrams
- [WayBack] We Be Geeks, Ya – Danny Thorpe
The session had seemingly simple things like this [WayBack] Shenoy At Work: Set Next Statement in Delphi? with the picture on the right.
Voiding the solution
I’ve seen teams making this method inline
, but that voids it. Usually they do not see it as they already resolved the “never used” problem in another way.
Why it still works
The solution is counting on the compiler not optimising this construct away. It does when marked as inline
, but is not yet smart enough for non-inline.
The compiler work progresses very slowly for many reasons, embarcadero usually goes for feature-ticks instead of optimisation ticks (remember the floating point optimisations that never came?), this particular optimisation is a corner case, and is used in production code so a solution can break.
I think that’s the list of reasons the compiler likely will never be changed to optimise this construct and void it.
Example
The example code uses Touch
on FileMode
so it is easier to get the disassembly (it’s a very safe global variable to use, as it has been available for like forever, see “FileMode” “Turbo Pascal 3”):
begin Touch(FileMode); // this variable will always exist; it has been since around Turbo Pascal 3 end. TouchDemoProject.dpr.44: Touch(FileMode); // 0040A0FC A134B84000 mov eax,[$0040b834] 0040A101 E83EF0FFFF call Touch TouchDemoProject.dpr.45: end. 0040A106 E801B9FFFF call @Halt0 procedure Touch(var arg); begin end; TouchDemoProject.dpr.12: begin 00409144 55 push ebp 00409145 8BEC mov ebp,esp 00409147 51 push ecx 00409148 8945FC mov [ebp-$04],eax TouchDemoProject.dpr.13: end; 0040914B 59 pop ecx 0040914C 5D pop ebp 0040914D C3 ret
Examples of these from the Delphi 2007 documentation
(they have been around longer in PDF and Microsoft Help format, but the Delphi 2007 was the are the earliest product having HTML documentation that still can be archived in the WayBack machine)
- H2077: Value assigned to ‘%s’ never used
program Produce; (*$HINTS ON*) procedure Simple; var I: Integer; begin I := 42; (*<-- Hint message here*) end; procedure Propagate; var I: Integer; K: Integer; begin I := 0; (*<-- Hint message here*) Inc(I); (*<-- Hint message here*) K := 42; while K > 0 do begin if Odd(K) then Inc(I); (*<-- Hint message here*) Dec(K); end; end; procedure TryFinally; var I: Integer; begin I := 0; (*<-- Hint message here*) try I := 42; finally Writeln('Reached finally'); end; Writeln(I); (*Will always write 42 - if an exception happened, we wouldn't get here*) end; begin end.
- H2164: Variable ‘%s’ is declared but never used in ‘%s’
program Produce; (*$HINTS ON*) procedure Local; var I: Integer; begin end; begin end.
- H2219: Private symbol ‘%s’ declared but never used
program Produce; type Base = class private FVar: Integer; procedure Init; end; procedure Base.Init; begin end; begin end.
Sidenote
C++ Builder has an extra warnings W8004, W8057 and W8080, which you can find more on when searching for BCC32 “W8004” , BCC32 “W8080” , and BCC32 “W8057” or these posts:
Other uses
Some links I found searching for “procedure Touch(var”:
- [WayBack] Sample Application: OutlookTest3.dpr
- [WayBack] Debugging Session
- [WayBack] delphi – Läuft der Delphi-Compiler? – Switch-Case
- [WayBack] fmOutlookTest2.pas – Free Open Source Codes – CodeForge.com
- [Achive.is] Google Translate: 200 points —> multi-threaded call the same function security problem <—
- [Archive.is] Google Translate: Follow-up of the number-breaking program
–jeroen
Example source code:
program TouchDemoProject; {$APPTYPE CONSOLE} {$R *.res} /// <summary> /// Call this method to get rid of H2077, H2164 and H2219 "never used" hints. /// Do NOT mark this method `inline;` as that will give you the hints back. /// </summary> procedure Touch(var arg); begin end; procedure H2077(); var X: Integer; begin X := 1; Touch(X); // prevents "[dcc32 Hint] ...: H2077 Value assigned to 'X' never used" end; procedure H2164(); var X: Integer; begin Touch(X); // prevents "[dcc32 Hint] ...: H2164 Variable 'X' is declared but never used in 'H2164'" end; type TH2219 = class private // not strict private, as that will prevent the `Touch` workaround X: Integer; // without `Touch` workaround: "[dcc32 Hint] ...: H2219 Private symbol 'X' declared but never used" end; procedure H2219(const X: Integer); var H2219: TH2219; begin Touch(H2219.X); // prevents "[dcc32 Hint] ...: H2077 Value assigned to 'X' never used" end; begin Touch(FileMode); // this variable will always exist; it has been since around Turbo Pascal 3 end.
abouchez said
On FPC you can use {%H-} just where the warning is defined, and it won’t appear at compilation time.
But FPC has so many false positive hints, that it was a mandatory directive! ;)