Historically, [WayBack] W1036
: Variable '%s' might not have been initialized (Delphi)
has had a lot of gotchas.
The most important still is that it never show for managed types (strings, interfaces, etc).
Usually W1036
shows for non-managed types, but Dan Hacker has found a new case where it does not in [WayBack] Why don’t I get the warning W1036 Variable “‘MyStrings’ might not have been initialized’ if the StringsToDo parameter in DoSomething is defined as a var… – Dan Hacker – Google+
He noticed it in Delphi XE and 10.2 Tokyo.
I tested it with the Win32 compiler in XE8.
This warns:
program W1036; {$APPTYPE CONSOLE} uses System.SysUtils, System.Classes; procedure DoSomething(var StringsToDo: TStringList); // <-------- this line makes the difference begin //do nothing end; procedure Test; var MyStrings : TStringList; begin MyStrings.Free; {W1036 warning if StringsToDo param is NOT var} DoSomething(MyStrings); end; begin end.
This does not warn:
program W1036; {$APPTYPE CONSOLE} uses System.SysUtils, System.Classes; procedure DoSomething(StringsToDo: TStringList); // <-------- this line makes the difference begin //do nothing end; procedure Test; var MyStrings : TStringList; begin MyStrings.Free; {W1036 warning if StringsToDo param is NOT var} DoSomething(MyStrings); end; begin end.
initially, I misread his report and reacted wrongly on the cause (but rightly on the suggested use case of var
and of TStringList
):
Because a var parameter means that the caller should pass in an initialised parameter. Otherwise it should be an
out
parameter, not avar
parameter.
var
parameter for reference types likeTStrings
(only pass asTStringList
if it deliberately is not compatible withTStrings
) is a risky thing anyway, because the called method can overwrite it and then the caller has to notice the change, then decide what to do with the previous value (likely free it).
I later correct myself:
Looking better, I think it is a compiler issue.The only difference between nothing and var is the loading of the parameter as a pointer:
with
var
//W1036.dpr.16: MyStrings.Free; {W1036 warning if StringsToDo param is NOT var}
//004CD924 8B45FC mov eax,[ebp-$04]
//004CD927 E8B8A6F3FF call TObject.Free
//Project1.dpr.17: DoSomething(MyStrings);
//004CD92C 8D45FC lea eax,[ebp-$04]
//004CD92F E8E0FFFFFF call DoSomethingwithout
var
//W1036.dpr.16: MyStrings.Free; {W1036 warning if StringsToDo param is NOT var}
//004CD924 8B45FC mov eax,[ebp-$04]
//004CD927 E8B8A6F3FF call TObject.Free
//Project1.dpr.17: DoSomething(MyStrings);
//004CD92C 8B45FC mov eax,[ebp-$04]
//004CD92F E8E0FFFFFF call DoSomething
So:
- with a
var
, a pointer to the instance ofMyStrings
(obtained by alea
instruction) gets pushed on the stack - without a
var
, the instance ofMyStrings
(obtained by amov
instruction) gets pushed on the stack.
For the difference metween mov
and lea
, and the use of []
brackets, see:
- [WayBack] assembly – What is the difference between MOV and LEA? – Stack Overflow
- [WayBack] assembly – What do the brackets mean in x86 asm? – Stack Overflow:
Given the following
code:L1 db "word", 0
mov al, [L1]
mov eax, L1
What do the brackets ([L1]) represent?
–jeroen
Source: Why don’t I get the warning W1036 Variable “‘MyStrings’ might not have been i…