Why don’t I get the warning W1036 Variable “‘MyStrings’ might not have been initialized”…
Posted by jpluimers on 2019/01/21
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…
PS: after a discussion with Rudy Velthuis, trying to explain the [WayBack] Outside-In vs. Inside-Out Thinking | CustomerThink on the general managed versus non-managed behaviour of W1036, I have come up with this comment and the solution:
+Rudy Velthuis designing a warning thinking from the compiler to the programmer instead of thinking from the programmer to the compiler.The documentation on the warning never mentions it will not show for certain areas of types, so the programmer will think it always works.
From a compiler perspective, it is very clear it will not work, but from a programmer perspective, you have to know (and constantly remember) the cases it won’t. Computer programs are much better capable of “remembering” those cases, so it is their task to do that, not the programmer’s task.
A solution could be to have two hints for uninitialised variables: one for variables of unmanaged types (which will get random values when living on the stack) and one for variables of managed types (which will get default values).
rvelthuis said
But it should not show! The string is already initilaized, so showing it would be wrong.
The expectation that it should show at all is wrong, not the compiler. You don’t have to remember the cases when it won’t show. If it doesn’t show, there is nothing to show, period.
That is not a “customer-think vs. compiler-think” issue, nor is it an issue of “not showing for certain types”, it is simply a matter of not showing a hint that is wrong.
jpluimers said
And this perfectly describes the inside-out versus outside-in thinking.
Every time people that bump into this issue ask me why this is not shown for managed types, they have the expectation the warning will show for variables that have not been initialised by the programmer.
That point of view makes perfect sense for an outside-in perspective.
rvelthuis said
I don’t think it does.
I am on the outside (I am a user of the compiler) and don’t want a warning or hint when there is nothing to hint about. A compiler cannot and should not hint just because there are false expectations. I, an outsider, want the compiler to act sensibly, not apologetically. It is a tool, and I expect it to behave like one, not like a psychiater or a mindreader. So no, that is not inside-out thinking, that is my expectationm that tool behjaves like one and only issue an error/warning/hint when there actually is som,ethjing to hint about.
So tell those who complain about the lack of a hint that there is nothing to hint about because the string is already initialized. I don’t wan the compiler to tell the user something like “hey, you may not have changed the value of the string”, because that is up to the user. It would extremely annoy me and anyone who doesn’t want to change it unless some conditions apply. In other words: no hints where no hints are due. No false positives.