The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 2,317 other followers

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:

(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:

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”:

–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.

One Response to “The magic “procedure Touch(var …” construct”

  1. 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! ;)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: