Guess the output of this program.
Is this three Delphi bugs for the price of one?
Compiler mentions:
[dcc32 Hint] WeirdCaseConstruct.dpr(11): H2077 Value assigned to ‘Handled’ never used
1. If statement inside case – after ELSE option?
2. Handled is actually used
3. else Test(4) is never called
#wtf XE7.1 (32-bit, Windows)
program WeirdCaseConstruct;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
procedure Test(const Value: Integer);
var
Handled: Boolean;
begin
Handled := True; // <- Hinted
case Value of
1: writeln(‘One’);
2: writeln(‘Two’);
else Handled := False;
if not Handled
then Writeln(Value, ‘ is unhandled’)
else Test(4);
end;
end;
begin
Test(1);
Test(3);
Write(‘Press Enter’);
Readln;
end.
I had to format it to differently to know what it will do:
procedure Test(const Value: Integer);
var
Handled: Boolean;
begin
Handled := True;
case Value of
1: writeln(‘One’);
2: writeln(‘Two’);
else
Handled := False;
if not Handled then
writeln(Value, ‘ is unhandled’)
else
Test(4);
end;
end;
Output should be:
One
3 is unhandled
Press Enter
PS.
I don’t see a bug.
LikeLike
Let me reformat and re-emphasize the weirdness…
How can the statement in italics be valid inside a case statement?
program WeirdCaseConstruct;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
procedure Test(const Value: Integer);
var
Handled: Boolean;
begin
Handled := True; // <- Hinted
case Value of
1: writeln(‘One’);
2: writeln(‘Two’);
else begin
Handled := False;
end;
if not Handled
_then Writeln(Value, ‘ is unhandled’)_
else Test(4);
end;
end;
begin
Test(1);
Test(3);
Write(‘Press Enter’);
Readln;
end.
LikeLike
After a refresh I’ve seen that you updated your question:
The Value “True” that is assigned to Handled is never used. This is correct. (Its the value, not the variable!)
LikeLike
The Handled = True is used for case 1 & 2.
LikeLike
As for case:
There is no “begin” after the else in a case construct, but this is an implicit block.
LikeLike
Lars Fosdal No it’s not.
case .. of
..
else
// implicit begin end block here
end.
LikeLike
Why doesn’t the else work?
LikeLike
The code as it should have been written…
No hint, else works.
program WeirdCaseConstruct;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
procedure Test(const Value: Integer);
var
Handled: Boolean;
begin
Handled := True;
case Value of
1: writeln(‘One’);
2: writeln(‘Two’);
else begin
Handled := False;
end;
end;
if not Handled // <- Outside case
then Writeln(Value, ‘ is unhandled’)
else Test(4);
end;
begin
Test(1);
Test(3);
Write(‘Press Enter’);
Readln;
end.
LikeLike
Lars Fosdal Like I said, there is an implicit begin end block for the “else” in a case statement.
It’s different from it then else …
That’s the way it is.
LikeLike
That still doesn’t explain why the code placed there only partially works. The else Test(4) is eliminated.
LikeLike
It is eliminated because it can’t be reached. Your formating is plaing tricks with you since the else of a case behaves differently.
LikeLike
Test(4) is eliminated because the compiler can prove that Handled = False when Handled is tested. This is an undocumented “feature” of language that has been present for as long as I can remember. I bet it’s found in old TP.
Update Actually, it is now documented: http://docwiki.embarcadero.com/RADStudio/en/Declarations_and_Statements#Case_Statements
LikeLike
That doesn’t make sense, IMO.
if condition
then DoA
else DoB
why would the DoB be unreachable?
LikeLike
Your program is equivalent to this one:
{$APPTYPE CONSOLE}
procedure Test(const Value: Integer);
var
Handled: Boolean;
begin
Handled := True; // <- Hinted
case Value of
1:
writeln(‘One’);
2:
writeln(‘Two’);
else
begin
Handled := False;
if not Handled then
writeln(Value, ‘ is unhandled’)
else
Test(4);
end;
end;
end;
begin
Test(1);
Test(3);
Write(‘Press Enter’);
Readln;
end.
LikeLike
David Heffernan – Yet the output from the original post is
One
3 is unhandled
Press Enter
LikeLike
If you’d asked this on SO, then we could have produced a nice Q&A that would have been curated for anybody else in the future who comes across this issue. As it stands, the value here will be lost.
LikeLike
OK, now I get it – I still find it mindblowing…
LikeLike
I guess it just goes to show that you should really mind the hints in your code, not only the warnings. Perhaps a warning about code being eliminated would be more appropriate in this case.
LikeLike
Actually, this is now documented (http://docwiki.embarcadero.com/RADStudio/en/Declarations_and_Statements#Case_Statements). It did not use to be. The docs now say:
A case statement can have a final else clause:
case selectorExpression of
caseList1: statement1;
…
caselistn: statementn;
else
statements;
end
where statements is a semicolon-delimited sequence of statements.
LikeLike
I don’t think I’ve ever used a case else without begin/end – unless it was a single line.
I am still appalled…
LikeLike
The block nature of the else clause was evident in the TP 1.0 manual. No exposition was given to the topic, but the example in the manual had two lines of code in the else block. And as the code was well formatted, the intention was apparent.
LikeLike
Lars Fosdal Why be appalled. Where is the ambiguity? There is none. The real problem is the whole single/compound statement. It was “fixed” in Modula-2. Too late for us though.
LikeLike
Lars Fosdal Not else, but there are several other clauses which behave the same: “repeat”, “finally”, “except” for example.
LikeLike
The ambiguity certainly is/was there in my head.
LikeLike
Asbjørn Heid – My error was to assume that the else in case, behaved like else in if. The other ones you mention are block defining in their own right. I never was aware that the case else behaved that way. Now, I feel silly – and annoyed.
LikeLike
Lars Fosdal they are few exceptions for the usage of begin/end to declare a block of instructions: “repeat … until”, “try … except…end”, “try ..finally…end”, and the “case else … end”.
LikeLike
Lars Fosdal Even in try except end one can have an else without a begin/end and it behaves just like the case statement. 😉
try
except
on E: EOSError do
Writeln(E.Message);
else
Write(‘Exception’);
Writeln(‘ unknown’);
end;
LikeLike
Lars Fosdal So it’s a head case? 🙂
LikeLike
Martin Wienold – good point.
Wonder why they didn’t opt for
try
exception E of
EOSError: …;
else
end;
Bill Meyer – Clearly, a head case.
LikeLike
David Heffernan I thougt this was in very old documentation as well, but it isn’t. Thanks!
LikeLike
Cool. I’ve always been overly verbose in except on, and especially repeat (a syntactically begin-end-eye-opener). Never thought about the case else the same way.
It’s the same thing apparently. I do not think anyone would mind (except when sitting on loads of legacy code) if begin-end was enforced in these cases.
LikeLike