4

If I compile the below code it gives a warning on line 1:

value assigned to lTime never used*

But if I remove that line, I get a different warning on line 2:

variable lTime might not have been initialized

Is the compiler missing something, or am I?

procedure TFormWebServices.RemoveOldReports; var TSR : TSearchRec; I : Integer; lCutOff, lTime : Int64; TSToDelete: TStringList; S,Msg : String; E : Exception; begin lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440); I := FindFirst(FReportDir + '*.pdf',0,TSR); TSToDelete := TStringList.Create; while I = 0 do begin if (TSR.Attr and faDirectory) = 0 then begin lTime := lCutOff; // Line 1 try lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1)); except on E:Exception do lTime := lCutOff; end; if lTime < lCutOff then // Line 2 TSToDelete.Add(TSR.Name); end; I := FindNext(TSR); end; 

This is not a dupe of Why is the Compiler warning that variable may not be initialized?, because I assign lTime in the exception as well.

5
  • 1
    Possible duplicate of Why is the Compiler warning that variable may not be initialized? Commented Sep 2, 2016 at 13:04
  • 1
    It looks like a duplicate indeed. Commented Sep 2, 2016 at 13:06
  • Really, though, there's no need to use exception handling here. The RTL provides a suite of TryStrTo[x] methods for exactly this purpose - much more efficient than letting the exception handler deal with it. TryStrToInt64 Documentation Commented Sep 2, 2016 at 13:07
  • 1
    Just save yourself some trouble and use either TryStrToInt64, or StrToInt64Def. Exceptions are expensive (and annoying while debugging) Commented Sep 2, 2016 at 13:08
  • 1
    On a side note, if you would remove line 1 AND remove the "on E:Exception do" in the exception block, you won't get a warning. Commented Sep 2, 2016 at 13:40

2 Answers 2

7
lTime := lCutOff; try lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1)); except on E:Exception do lTime := lCutOff; end; if lTime < lCutOff then TSToDelete.Add(TSR.Name); 

The compiler is correct to warn about this code. When you assign lTime := lCutOff on the first line, the value written in that assignment is never read.

However, when you remove the code things are less clear cut.

try lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1)); except on E:Exception do lTime := lCutOff; end; if lTime < lCutOff then TSToDelete.Add(TSR.Name); 

There are two scenarios to consider: an exception is not raised inside the try/except block, or an exception is raised there.

If no exception is raised then it is simple, lTime is assigned the result of StrToInt64 and is therefore initialized before being read.

In an exception is raised, then lTime is not initialized inside the block. What happens next?

  • If the exception derives from Exception (not compulsory) then it is caught and lTime is initialized.
  • If the exception does not derive from Exception then it is not caught and lTime is never read.

Therefore, the compiler could infer all of that and therefore not issue an uninitialized variable warning. However, the compiler does not do flow analysis of that complexity, sadly. I believe that its logic runs like this:

  1. An exception is raised before lTime is initialized, then
  2. The exception could be caught, in which case
  3. The variable lTime is then read, and still might be uninitialized.

In step 2 it ought to be able to realise that lTime is initialized but it simply does not perform such analysis. So whilst one could argue that the compiler could do better, you just have to accept this as a limitation of its analysis algorithms.

Having understood that we are left with the task of finding a way to write the code and avoid the warnings. We don't want to suppress the warnings, we just need to find a way to write the code so that it is both correct and free of warnings.

The way forward, in my view, is to recognise that exceptions are the wrong tool to use here. It is normal behaviour for this conversion to fail. You should write the code using conversion functions that don't raise an exception when they fail. For instance you could use one of the following options:

if TryStrToInt64(..., lTime) then if lTime < lCutOff then .... else lTime := lCutoff; 

Or:

lTime := StrToInt64Def(..., lCutoff); if lTime < lCutOff then .... 
Sign up to request clarification or add additional context in comments.

1 Comment

Delphi XE compiler does not generate the false warning ''variable .. might not have been initialized' on this code.
-1

This is correct behaviour. The line 1 value will never be used since in the try/except you assign a new value without using the previous one, hence the warning.

if you remove the line you try to use the lTime variable outside a try/except block which may fail without actually setting a value to lTime

procedure TFormWebServices.RemoveOldReports; var TSR : TSearchRec; I : Integer; lCutOff, lTime : Int64; TSToDelete: TStringList; S,Msg : String; E : Exception; begin lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440); I := FindFirst(FReportDir + '*.pdf',0,TSR); TSToDelete := TStringList.Create; while I = 0 do begin if (TSR.Attr and faDirectory) = 0 then begin lTime := lCutOff; // Line 1 try lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1)); except on E:Exception do lTime := lCutOff; end; if lTime < lCutOff then // Line 2 TSToDelete.Add(TSR.Name); end; I := FindNext(TSR); end; 

You could do this

procedure TFormWebServices.RemoveOldReports; var TSR : TSearchRec; I : Integer; lCutOff, lTime : Int64; TSToDelete: TStringList; S,Msg : String; E : Exception; begin lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440); I := FindFirst(FReportDir + '*.pdf',0,TSR); TSToDelete := TStringList.Create; while I = 0 do begin if (TSR.Attr and faDirectory) = 0 then begin lTime := StrToInt64Def(Copy(TSR.Name,1,pos('.',TSR.Name)-1), lCutOff); if lTime < lCutOff then TSToDelete.Add(TSR.Name); end; I := FindNext(TSR); end; 

7 Comments

Thanks, I did not know there was also a StrToInt64Def
I disagree with paragraph 2. There is actually no circumstance, when line 1 is removed, where lTime can be read before it has been initialized.
The line 2 warning only occurs when line 1 is removed. Why is that incorrect?
As I said, there is no scenario whereby lTime can be accessed before it has been initialized. Or can you prove me wrong by demonstrating such a scenario?
@DavidHeffernan lTime could be accessed without being initialized in the situation where whatever is raised is not an Exception. (i.e. raise TObject.create). Now, we all know that won't happen in StrToInt64Def, but it is still a situation the compiler need to account for.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.