Discussion:
Data flow analysis (dfa) and "case ... of"
Add Reply
Juha Manninen
2017-06-05 14:22:14 UTC
Reply
Permalink
Raw Message
Wiki tells about problems the dfa encounters:
http://wiki.freepascal.org/FPC_New_Features_3.0#Node_dfa_for_liveness_analysis

In this code if "b" is true in the first test, it will be true also in
the second test, thus using "i" is safe and valid.
...
if b then
i:=1;
writeln;
if b then
writeln(i);

I understand that complex code can have many variations of such
constructs and analysing them all is difficult.

However for "case ... of" construct with enum types the dfa could be
improved easily, at least I could imagine so.
There is plenty of code like this:

function TControlBorderSpacing.GetSpace(Kind: TAnchorKind): Integer;
begin
case Kind of
akLeft: Result:=Left;
akTop: Result:=Top;
akRight: Result:=Right;
akBottom: Result:=Bottom;
end;
end;

All the enum values are listed and the Result is set for sure, yet FPC
complains:
controls.pp(3721,1) Warning: Function result variable does not seem
to be initialized

If the compiler used the information about all enums being present, it
would reduce the number of dfa false positives in eg. Lazarus sources
dramatically.

Juha
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepa
Denis Kozlov
2017-06-05 16:46:59 UTC
Reply
Permalink
Raw Message
Result is undefined if caller passes an out of range value.

An example: TControlBorderSpacing.GetSpace(TAnchorKind(-1));

Result is indeed not initialized, so silencing this message may be harmful.

Denis
Post by Juha Manninen
http://wiki.freepascal.org/FPC_New_Features_3.0#Node_dfa_for_liveness_analysis
In this code if "b" is true in the first test, it will be true also in
the second test, thus using "i" is safe and valid.
...
if b then
i:=1;
writeln;
if b then
writeln(i);
I understand that complex code can have many variations of such
constructs and analysing them all is difficult.
However for "case ... of" construct with enum types the dfa could be
improved easily, at least I could imagine so.
function TControlBorderSpacing.GetSpace(Kind: TAnchorKind): Integer;
begin
case Kind of
akLeft: Result:=Left;
akTop: Result:=Top;
akRight: Result:=Right;
akBottom: Result:=Bottom;
end;
end;
All the enum values are listed and the Result is set for sure, yet FPC
controls.pp(3721,1) Warning: Function result variable does not seem
to be initialized
If the compiler used the information about all enums being present, it
would reduce the number of dfa false positives in eg. Lazarus sources
dramatically.
Juha
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal
Jonas Maebe
2017-06-05 16:51:53 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
Result is undefined if caller passes an out of range value.
That would be an error at the time this out-of-range value would be
converted to TAnchorKind. If range checking is off there or disabled via
an explicit type cast, then the result is undefined by definition. The
compiler can and does assume in various cases that a type only holds
values valid for that type.


Jonas

_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/
Juha Manninen
2017-06-05 17:07:09 UTC
Reply
Permalink
Raw Message
For some reason my reply went only to Denis first. Another try here...
Post by Denis Kozlov
Result is undefined if caller passes an out of range value.
An example: TControlBorderSpacing.GetSpace(TAnchorKind(-1));
Result is indeed not initialized, so silencing this message may be harmful.
Yes but that is an illegal situation. It is already trapped by range check -Cr.
The compiler can safely assume that such situation does not happen.
Actually the compiler already assumes that in other situations. I
remember a bug years ago in Lazarus, caused by an out of range enum
value. FPC had changed its behavior and the bug got triggered.

Juha
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fp
Denis Kozlov
2017-06-05 17:39:00 UTC
Reply
Permalink
Raw Message
I have just created a new GUI application, enabled range checking "-Cr"
and executed the following:

ShowMessage(IntToStr(Form1.BorderSpacing.GetSpace(TAnchorKind(-1))));

The displayed value is a semi-random integer. No compiler errors or
run-time errors.

P.S. Same behaviour for Lazarus 1.6.4 + FPC 3.0.2 and Lazarus 1.4.4 +
FPC 2.6.4.

Denis
Post by Jonas Maebe
Post by Denis Kozlov
Result is undefined if caller passes an out of range value.
That would be an error at the time this out-of-range value would be
converted to TAnchorKind. If range checking is off there or disabled
via an explicit type cast, then the result is undefined by definition.
The compiler can and does assume in various cases that a type only
holds values valid for that type.
Post by Denis Kozlov
Result is undefined if caller passes an out of range value.
An example: TControlBorderSpacing.GetSpace(TAnchorKind(-1));
Result is indeed not initialized, so silencing this message may be harmful.
Yes but that is an illegal situation. It is already trapped by range check -Cr.
The compiler can safely assume that such situation does not happen.
Actually the compiler already assumes that in other situations. I
remember a bug years ago in Lazarus, caused by an out of range enum
value. FPC had changed its behavior and the bug got triggered.
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/
Jonas Maebe
2017-06-05 17:59:53 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
I have just created a new GUI application, enabled range checking "-Cr"
ShowMessage(IntToStr(Form1.BorderSpacing.GetSpace(TAnchorKind(-1))));
The displayed value is a semi-random integer. No compiler errors or
run-time errors.
That is why I said "If range checking is off there or disabled via an
explicit type cast, then the result is undefined by definition." You use
an explicit typecast above.


Jonas
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi
Denis Kozlov
2017-06-05 18:37:18 UTC
Reply
Permalink
Raw Message
I just wanted to highlight that these cases as legal and I presume not
uncommon, particularly if values are deserialized and typecasted.

It seems that there is no way for compiler to know if the result has
indeed being initialized by reason of enumeration of valid values,
because one has also account for invalid values.

I think that compiler should always warn (as it does now) if result is
not initialized against all possible cases.

Denis
Post by Jonas Maebe
That is why I said "If range checking is off there or disabled via an
explicit type cast, then the result is undefined by definition." You
use an explicit typecast above.
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.
Sven Barth via fpc-devel
2017-06-05 18:43:03 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
I just wanted to highlight that these cases as legal and I presume not
uncommon, particularly if values are deserialized and typecasted.
They are legal in the sense that they result in undefined behavior and
in that case the compiler is free to act as it sees fit.

Regards,
Sven
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.o
Jonas Maebe
2017-06-05 18:49:33 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
Post by Jonas Maebe
That is why I said "If range checking is off there or disabled via an
explicit type cast, then the result is undefined by definition." You
use an explicit typecast above.
I just wanted to highlight that these cases as legal and I presume not
uncommon, particularly if values are deserialized and typecasted.
Then this de-serialisation code must perform range checking. Again: if
you assign an invalid value to a variable by typecasting, disabling
range checking, inline assembly, passing a pointer to C code and
overwriting the value there, or in any other way, Pascal code that works
with the resulting value has undefined behaviour. The program could
crash, silently fail, raise a random exception at some point, or do
anything else.
Post by Denis Kozlov
It seems that there is no way for compiler to know if the result has
indeed being initialized by reason of enumeration of valid values,
because one has also account for invalid values.
No, the compiler does not have to care about the fact that you may have
put invalid data in that variable by bypassing the type system. If you
do that, you are on your own. The compiler also uses type information to
perform optimizations (e.g. "high(TAnkorKind) < Kind" will be replaced
by "false" at compile time, because there is no valid program flow with
which this statement can become false).
Post by Denis Kozlov
I think that compiler should always warn (as it does now) if result is
not initialized against all possible cases.
The DFA simply does not take into account the result of case-statements
at all right now. If you have a "case bytevar of" with every possible
value from 0 to 255, you will still get the warning.


Jonas
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-d
Denis Kozlov
2017-06-05 19:44:27 UTC
Reply
Permalink
Raw Message
Post by Jonas Maebe
The DFA simply does not take into account the result of
case-statements at all right now. If you have a "case bytevar of" with
every possible value from 0 to 255, you will still get the warning.
They are legal in the sense that they result in undefined behavior and
in that case the compiler is free to act as it sees fit.
By covering all possible cases I meant including "else" condition in all
case statements, which either defaults to some acceptable value or
raises an exception to highlight an invalid value. This is just a
standard practice for me, so my code never produces "uninitialized
result" warnings, unless I actually made a mistake by not covering all
possible cases, in which case I would really like to see that warning no
matter what. I prefer to see a controlled exception instead of having an
invalid value producing a pseudo-valid semi-random result which may be
much more difficult to trace later.

Complier currently warns (cares) about such execution flows which are
triggered by invalid data, and I happen to find it useful :)

However, I do understand that compiler can rightfully not care about
invalid data, hence, choose not to issue the "uninitialized result"
warning if all valid values have been enumerated.

Denis
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.or
Florian Klämpfl
2017-06-06 15:06:28 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
That is why I said "If range checking is off there or disabled via an explicit type cast, then
the result is undefined by definition." You use an explicit typecast above.
I just wanted to highlight that these cases as legal and I presume not
uncommon, particularly if values are deserialized and typecasted.
Then this de-serialisation code must perform range checking. Again: if you assign an invalid value
to a variable by typecasting, disabling range checking, inline assembly, passing a pointer to C code
and overwriting the value there, or in any other way, Pascal code that works with the resulting
value has undefined behaviour. The program could crash, silently fail, raise a random exception at
some point, or do anything else.
For the record: this can already happen. If a jump table for a case statement is generated, the
compiler does not check the boundaries if the jump table covers the whole declared (!) range of the
case variable.

_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/
Juha Manninen
2017-06-05 21:11:58 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
I just wanted to highlight that these cases as legal and I presume not
uncommon, particularly if values are deserialized and typecasted.
No they are not legal. Why you write this nonsense?
Values outside the range dictated by the type system are always wrong.
No excuses, no exceptions.
For the purposes of DFA the compiler can always assume the values are in range.

- Later: -
Post by Denis Kozlov
Complier currently warns (cares) about such execution flows
which are triggered by invalid data, and I happen to find it useful :)
Again nonsense.
The range check -Cr warns about out-of-range data, yes, but this
thread is about DFA and not about range checks.
I feel you hijacked my DFA thread for your own purpose but I don't
really know what that purpose is.
Are you saying that typecasts should be forbidden to prevent range errors?
No, it would not prevent bugs in code.
Anyway, please start a new thread about range check issues.

Juha
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/m
Denis Kozlov
2017-06-06 23:04:54 UTC
Reply
Permalink
Raw Message
Juha,

Just to get this straight, I am talking about DFA and your proposal to
remove "uninitialized result" warning when all declared values of a set
have been enumerated in a case statement. I don't have any issues with
range check errors, they were brought up and became part of
conversation. Now, put aside range checks and focus on DFA and case
statements.

Consider the code fragment below. Currently, FCP 3.0.2 with -O3 shows
"uninitialized result" warning for Convert2 and Convert3 functions, but
not for Convert1. I find this perfect as is, and, of course, you can
rightfully disagree.

You suggest that only Convert3 function should raise "uninitialized
result" warning, while Convert1 and Convert2 should not. I find this
less useful, and, again, you can rightfully disagree, but it won't
change the fact that it is still less useful for me (and possibly others).

I won't indulge in any further debate. I voiced my opinion/concern and
will leave it at that. Feel free to ignore it.

----------------------------
type
TConvertType = (ctA, ctB);

function Convert1(Value: TConvertType): Integer;
begin
case Value of
ctA: Result := 1;
ctB: Result := 2;
else Result := 0;
end;
end;

function Convert2(Value: TConvertType): Integer;
begin
case Value of
ctA: Result := 1;
ctB: Result := 2;
end;
end;

function Convert3(Value: TConvertType): Integer;
begin
case Value of
ctA: Result := 1;
end;
end;
----------------------------
Post by Juha Manninen
Post by Denis Kozlov
I just wanted to highlight that these cases as legal and I presume not
uncommon, particularly if values are deserialized and typecasted.
No they are not legal. Why you write this nonsense?
Values outside the range dictated by the type system are always wrong.
No excuses, no exceptions.
For the purposes of DFA the compiler can always assume the values are in range.
- Later: -
Post by Denis Kozlov
Complier currently warns (cares) about such execution flows
which are triggered by invalid data, and I happen to find it useful :)
Again nonsense.
The range check -Cr warns about out-of-range data, yes, but this
thread is about DFA and not about range checks.
I feel you hijacked my DFA thread for your own purpose but I don't
really know what that purpose is.
Are you saying that typecasts should be forbidden to prevent range errors?
No, it would not prevent bugs in code.
Anyway, please start a new thread about range check issues.
Juha
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-de
Martin
2017-06-07 11:59:49 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
Consider the code fragment below. Currently, FCP 3.0.2 with -O3 shows
"uninitialized result" warning for Convert2 and Convert3 functions,
but not for Convert1. I find this perfect as is, and, of course, you
can rightfully disagree.
You suggest that only Convert3 function should raise "uninitialized
result" warning, while Convert1 and Convert2 should not. I find this
less useful, and, again, you can rightfully disagree, but it won't
change the fact that it is still less useful for me (and possibly others).
You did of course read Jonas' statement, that if you call
Convertn(TConvertType(-1))

then Convert1 and Convert2 may crash, depending on compiler version and
optimization level (this may also depend on how many elements
TConvertType has)

Btw, from Jonas statement follows, that the "else" part in Convert1 may
be silently removed by the compiler. (maybe now or maybe by future fpc
versions). This may then give an "unreachable code" warning)

The warning however is only useful for Convert2, if you speculate that
the "else" in Convert1 is used for TConvertType(-1).
If it isn't then you have a bigger problem, and that is not caught by
this warning anyway.
Post by Denis Kozlov
type
TConvertType = (ctA, ctB);
function Convert1(Value: TConvertType): Integer;
begin
case Value of
ctA: Result := 1;
ctB: Result := 2;
else Result := 0;
end;
end;
function Convert2(Value: TConvertType): Integer;
begin
case Value of
ctA: Result := 1;
ctB: Result := 2;
end;
end;
function Convert3(Value: TConvertType): Integer;
begin
case Value of
ctA: Result := 1;
end;
end;
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-
Juha Manninen
2017-06-07 14:18:04 UTC
Reply
Permalink
Raw Message
You suggest that only Convert3 function should raise "uninitialized result"
warning, while Convert1 and Convert2 should not. I find this less useful,
and, again, you can rightfully disagree, but it won't change the fact that
it is still less useful for me (and possibly others).
Sorry but this is not a matter of opinion. Your view is just flawed and wrong.
FPC developers tried to explain that your "else" can be optimized away
and the program may crash.
The compiler trusts that data in an enum variable is legal, within the range.
It should trust the same way when doing DFA. It is logical and consistent.

If you let your enum data be out of range then your code is seriously
buggy. Why would you write such buggy code?
What you must do is add sanity checks for code that initializes enum
data based on some other data.
For example you are streaming and get data as string:

type
TEnumType = (ctA, ctB);

function StrToEnum(s: string): TEnumType;
begin
case s of
"ctA": Result := ctA;
"ctB": Result := ctB;
else raise Exception.Create('Illegal TEnumType value!');
end;
end;

The same way if you get integer data from I/O, you must verify the
integer is within range _before_ typecasting it.

Logical and robust.

Juha
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-dev
Denis Kozlov
2017-06-08 00:22:08 UTC
Reply
Permalink
Raw Message
Post by Juha Manninen
The compiler trusts that data in an enum variable is legal, within the range.
It should trust the same way when doing DFA. It is logical and consistent.
I understand your reasons now and agree that DFA should cover only
logical (according to the compiler) scenarios.
Post by Juha Manninen
What you must do is add sanity checks for code that initializes enum
data based on some other data.
.....
The same way if you get integer data from I/O, you must verify the
integer is within range _before_ typecasting it.
Sanity checks is exactly what I do, but in both directions (see example
below).

I can't control what values other users/developers may supply, nor I
restrict the use of different compiler versions and build flags. It is
because "TConvertType(-1)" and the likes are an acceptable statement for
compiler, I make it my responsibility to handle all possible input
values, no matter how illogical and invalid they may be. This is a
design pattern that I follow.

If the 'else' condition is optimized away in certain compiler versions
and/or by certain complier optimization flags, that is no problem, at
least not for me. Then, as Martin highlighted, "unreachable code"
warning may be appropriate.

Currently, TConvertType(X) doesn't produce any errors where X is out of
declared range of TConvertType. So I shall continue to account for
potential invalid values, even if my attempts may be optimized away in
some circumstances. I might be trying to account for an illegal or
undefined behaviour, but I do nonetheless, if it improves the chances of
catching errors earlier.

type
TConvertType = (ctA, ctB);

function StrToConvert(const Value: String): TConvertType;
begin
case Value of
'A': Result := ctA;
'B': Result := ctB;
else raise Exception.CreateFmt('Invalid convert value string (%s)',
[Value]);
end;
end;

function Convert(Value: TConvertType): Integer;
begin
case Value of
ctA: Result := 1;
ctB: Result := 2;
else raise Exception.CreateFmt('Invalid convert value (%d)',
[Ord(Value)]);
end;
end;
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/li
Juha Manninen
2017-06-09 06:20:11 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
I can't control what values other users/developers may supply, nor I
restrict the use of different compiler versions and build flags.
Compiler flags have nothing to do with the fundamental issue here.
If other developers supply buggy code to you, then they have to fix it
obviously. This holds true for any bugs.
Post by Denis Kozlov
It is because "TConvertType(-1)" and the likes are an acceptable statement for
compiler, I make it my responsibility to handle all possible input values,
no matter how illogical and invalid they may be. This is a design pattern
that I follow.
You have good intentions but somehow your logic slipped to a wrong track.
You must prevent invalid data entering an enum variable in the first place.
So, if you get integer data from eg. stream or user input and convert
to enum, you must ensure the integer is within range _before_
typecasting it.
Like:
if (i<0) or (i>Integer(High(TConvertType))) then
raise Exception.CreateFmt('Invalid convert value (%d)', [i]);
ctvar := TConvertType(i);

Typecast is always a "serious" thing. It means the original type is
wrong and must be forced to something else.
Obviously you need a sanity check there!
There is only a limited number of places where you set an enum
variable. It is rather easy to make sure the value is sane. After that
you don't need to worry about it, the value is valid always.

Your extra checks for enum values are like a desperate band-aid for a
bug that happened already elsewhere. No, a bug must be fixed where it
is.

Juha
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.free
Juha Manninen
2017-06-05 18:14:29 UTC
Reply
Permalink
Raw Message
Post by Denis Kozlov
I have just created a new GUI application, enabled range checking "-Cr"
ShowMessage(IntToStr(Form1.BorderSpacing.GetSpace(TAnchorKind(-1))));
The displayed value is a semi-random integer. No compiler errors or
run-time errors.
The typecast suppresses a range error in your code.
However the LCL method GetSpace should give an error if it is compiled with
-Cr.
So, build Lazarus + LCL with profile "Debug IDE" and try again.

Juha
Martok
2017-06-28 19:03:21 UTC
Reply
Permalink
Raw Message
Interestingly, I just ran into "bad" code generation with exactly the properties
discussed in this thread.

Take a function like this:

function SignatureSubpacketTypeToStr(const X: TSignatureSubpacketType): String;
begin
case X of
sstReserved00 : Result:= 'Reserved00';
sstReserved01 : Result:= 'Reserved01';
sstCreationTime : Result:= 'CreationTime';
....

Because every declared element is covered, the generated code for it ends up
being a computed goto:

0x10047c4c <+28>: mov -0x4(%ebp),%al
0x10047c4f <+31>: and $0xff,%eax
0x10047c54 <+36>: jmp *0x10071d08(,%eax,4)

Which is perfectly fine if X is guaranteed to be in range of the elements the
case statement matches to. If it is not, as it may be with invalid input data
(as read from a file), that jump goes somewhere undefined - and most
importantly, not into any else statement.

So, while we have code that looks like Result is always properly initialized,
what we get instead is code that doesn't actually work. And no kind of DFA could
detect that, except also range-checking everything.

Just thought I'd share that, as a less synthetic example than some discussed here.


Regards,
Martok

_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-de
Martin
2017-06-29 12:26:46 UTC
Reply
Permalink
Raw Message
Post by Martok
Interestingly, I just ran into "bad" code generation with exactly the properties
discussed in this thread.
Because every declared element is covered, the generated code for it ends up
and most importantly, not into any else statement.
No, it is not bad code generation.
It is exactly the expected result. At least that is what I understood
from previous mails in this thread, specifically (in the context of
Post by Martok
Post by Jonas Maebe
That is why I said "If range checking is off there or disabled via an
explicit type cast, then the result is undefined by definition."
"undefined result" does include the possibility of a crash.

This is the same as accessing an
Array[enum] of foo;
if enum is out of range, then it may crash, or behave in any undefined way.

The only thing that could be added here is, that if dfa detects that all
enums are used in a case statement, and yet there is an "else", then it
should warn about "unreachable code".

_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listi
Martok
2017-07-01 14:45:43 UTC
Reply
Permalink
Raw Message
The attitude displayed over on #32079 is, quite frankly, terrifying. Apparently
a language which from the beginning has intrinsics for reading and writing files
must never be used for doing so, or wild things may happen /and that's okay/.

Implying that input should already be sanitized on a bug about something that
breaks input sanitation code (but only sometimes) is just... wow.

If anybody wants it, here's the patch I'll be rolling on the windows snapshots
from now on.


Have a good weekend,
Martok
Tomas Hajny
2017-07-01 23:25:23 UTC
Reply
Permalink
Raw Message
On Sat, July 1, 2017 16:45, Martok wrote:


Hi,
Post by Martok
The attitude displayed over on #32079 is, quite frankly, terrifying.
Apparently a language which from the beginning has intrinsics for reading
and writing files must never be used for doing so, or wild things may
happen /and that's okay/.
Implying that input should already be sanitized on a bug about something
that breaks input sanitation code (but only sometimes) is just... wow.
If anybody wants it, here's the patch I'll be rolling on the windows
snapshots from now on.
I agree that it might be useful to extend the documentation of case ... of
as suggested in one of the comments.

I also agree to Jonas that it should not be a problem of the compiler to
perform these checks. However, there's one thing that worries me - I
assumed that the respective checks should be performed when reading the
input from a file and an error condition should be raised when
encountering a value not fitting the respective type. Unfortunately, this
is apparently not the case, because the following test program happily
runs even when the test file contains a value outside the TEnum range:

type
TEnum = (one, two, three);

var
F: file of TEnum;
E: TEnum;

begin
{$I+}
{$R+}
Assign (F, 'testfile.tst');
Reset (F, 1);
while not (Eof (F)) do
begin
Read (F, E);
WriteLn (Ord (E));
end;
Close (F);
end.

Interestingly, the error is caught if I try to write out the read value
back to console (i.e. add WriteLn (E) after the WriteLn line). I believe
that the same error should already happen while reading...

Tomas


_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.free

Marco van de Voort
2017-06-29 12:30:25 UTC
Reply
Permalink
Raw Message
Post by Martin
Post by Martok
Interestingly, I just ran into "bad" code generation with exactly the properties
discussed in this thread.
Because every declared element is covered, the generated code for it ends up
and most importantly, not into any else statement.
No, it is not bad code generation.
It is exactly the expected result. At least that is what I understood
from previous mails in this thread, specifically (in the context of
Afaik intermediate values in a sparse enum are not rangechecked at all? They
fall within the range (lowestvalue..highestvalue), but are not valid anyway.
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freep
Loading...