View Issue Details
|ID||Project||Category||View Status||Date Submitted||Last Update|
|0024318||FPC||Compiler||public||2013-04-24 18:50||2018-01-07 19:53|
|Reporter||Feliks Kluzniak||Assigned To||Jonas Maebe|
|Platform||MacBook Pro||OS||Mac OS X|
|Fixed in Version||3.1.1|
|Summary||0024318: Bad code is generated for "for" clauses if range variables are involved.|
|Description||Given variables "high", "low" and "ttop", all of the same integer range type, the loop beginning with|
"for high := low to ttop - 1 do"
iterates even when "low" is initially greater than "ttop - 1".
Looks as if unsigned arithmetic is used, incorrectly.
The same behaviour is observed in several modes: FPC, TP, ISO.
|Steps To Reproduce||See the attached file. The body of the loop should not be entered at all, instead the iterations go on and on...|
This is extracted from a larger program that was written in 1983 and ran with several implementations of the original "Wirth/Jensen" Pascal, then with Turbo Pascal and some others. With Free Pascal it fails.
|Tags||No tags attached.|
|Fixed in Revision||37934|
range.p (313 bytes)
||All compilers I tested (FPC, TP, GPC) give a range check error for the code if range checking is turned on so I suspect it just worked by accident.|
||The behaviour of code that contains range errors is indeed undefined in FPC.|
May I respectfully suggest that you have not given this issue sufficient attention? There is no range error here: none of the range variables is assigned a value out of range. (In the object code it may be assigned such a value as a result of overly simplistic translation, but that's not the programmer's problem.)
A range variable is just a normal integer, except that it shouldn't be assigned a value out of range. Comparison with a value that is out of range is quite legal. If your implementation treats the expression "0 <= -1" as true, then it is simply broken.
If the compilers you tested give range errors, then they are incorrect. I can assure you that in the days when we had many real implementations of Pascal this code worked correctly with all the possible checks turned on.
This is hardly an obscure corner of the language. Range types are quite natural to use for the indices of an array. If you implement, say, a stack in an array, then the idiom whose essence is used in the example is the obvious one for, say, printing out the contents of the stack.
From the ISO Extended Pascal standard document (http://www.standardpascal.org/iso10206.pdf ):
The initial-value and the final-value of a sequence-iteration of an iteration-clause of a for-statement shall be of a type compatible with the type of the control-variable of the for-statement. The initial-value and the final-value shall be assignment-compatible with the type possessed by the control-variable if the statement of the for-statement is executed.
The definition of "assignment-compatible" is (6.4.6):
A value of type T2 shall be designated assignment-compatible with a type T1 if any of the following
six statements is true:
a) T1 and T2 are the same type, and that type is permissible as the component-type of a le-type
NOTE | Because T1 and T2 are types, rather than type-denoters, the restriction on the bindability
of component-types of le-types does not apply here.
b) T1 is the real-type and T2 is the integer-type.
c) T1 is the complex-type and T2 is either the integer-type or the real-type.
d) T1 and T2 are compatible ordinal-types, and the value of type T2 is in the closed interval
specified by the type T1.
e) T1 and T2 are compatible set-types, and all the members of the value of type T2 are in the
closed interval speci ed by the base-type of T1.
f) T1 and T2 are compatible, T1 is a string-type or the char-type, and the length of the value of
T2 is less than or equal to the capacity of T1 (see 184.108.40.206).
Point d) applies here (ordinal types), and hence the final-value of the for-statement must be a valid value as far as the type of the control-variable is concerned.
||I think it becomes even more clear from the equivalent code sample given in the ext. pascal document which introduces temp1 and temp2 and which states below the code samples that temp1 and temp2 should have the same range type as the counter variable.|
Thank you for the careful argument. However, I believe you have not interpreted the standard quite correctly, and here are my reasons.
As you quote: "The initial-value and the final-value shall be assignment-compatible with the type possessed by the control-variable ***if the statement of the for-statement is executed***." [stress added]
But this is exactly the point: in my example the statement of the for-statement should not be executed. So what applies here is the previous phrase: "shall be of a type compatible with the type of the control-variable". Compatible, not assignment-compatible.
Now, type compatibility is defined in Sec. 6.4.5: "Types T1 and T2 shall be designated compatible if ... (b) T1 and T2 are ordinal types and have the same range-type (see 220.127.116.11)."
So we look at 18.104.22.168: "The _range-type_ of an ordinal-type that is a subrange-type shall be the host-type (see 22.214.171.124) of the subrange-type."
So we look at 126.96.36.199: "The subrange-bounds shall be of compatible ordinal-types, and the range-type (see 188.8.131.52) of the ordinal-types shall be designated the _host-type_ of the subrange-type."
Notice that we are now interested in the range-type not of the subrange-type itself, but of the ordinal type(s) from which we have taken the bounds with which it was defined. In my example this is the integer type. (Different subrange types may have the same host type: the distinction here is between subranges of integers, subranges of char, subranges of some particular enumerated type.)
And indeed, in 184.108.40.206 we read: "the _range-type_ of an ordinal-type that is not a subrange-type shall be the ordinal-type."
This stands to reason. The host type of a range type such as "ttx = 0 .. 800" is the integer type and therefore type "ttx" is compatible with type "integer" (though not assignment-compatible). Therefore my example is legal: comparison with the final value _before_ entering the loop is a comparison between values of compatible types, and should fail.
So much for the standard. If you look at the example as programmers, you will see that it would be most unnatural if one were not able to write such a loop, and the fact that the loop does iterate cannot be called anything but highly surprising.
I submit that this is indeed an error in the compiler.
The compatibility clause means that the compiler must not give a compile time error if your control-variable's type is 0..800, but the initial-value and/or final-value are e.g. of type integer.
The assignment of the initial-value to the control-variable and the check whether the control-variable is smaller than or equal to the final-value are part of the for-statement (there is no other statement these operations could be part of). The loop body is not the only part of the for-statement. The for-statement gets executed as soon as the control flow of the program reaches it, and as soon as that is the case all the "executed" conditions must be true for the code to be valid.
See also Florian's reference to the equivalent code for the for-statement in the standard document, which may make this even clearer.
It is quite clear that the "statement of the for-statement" mentioned by 220.127.116.11.2 refers to the "statement" in the grammar rule of 18.104.22.168.1:
for-statement = 'for' control-variable iteration-clause 'do' statement .
If this were not so, then the phrase "if the statement of the for-statement is executed" would make no sense.
As for the "equivalent code", it only supports my claim. The variables "temp1" and "temp2" are said to be of "the range-type of the type possessed by the variable v": in other words, not of the type of "v", but of the corresponding range-type, which is "integer", as I demonstrated in my last entry.
The compiler is incorrect.
Even if this applies, for FPC, the range-type is not integer but an unsigned type because it knows unsigned host-types which is not part of the iso standard.
Maybe I just remove the iso switch again to avoid wasting my time with discussions about 30 years old code and compilers.
The unsigned issue is not the point.
If ttlow is set to -1 to force range-type of ttx to integer,
and the for loop becomes
for high := low to ttop - 2 do
the range check error is triggered
So high is enforced to be assignment compatible with the upper limit even if the for loop body is never entered.
Gentlemen, this is my last message on the subject.
One of you says: "to avoid wasting my time with discussions about 30 years old code and compilers".
I was labouring under the misapprehension that this project is about saving a beautiful language that is on the verge of dying out, and that people who attend to the error reports are actually interested both in Pascal and in compilers. My apologies for being mistaken.
The remark beginning with "The unsigned issue is not the point" is very hard to understand. If ttlow were set to -1, then of course it should trigger a range check error. The whole point is that it wasn't. And no, unsigned arithmetic does not enter the picture: not a part of the original Pascal, and --- as you mention --- not part of ISO.
So the conclusions are as follows:
(1) The program is legal with respect to the ISO standard.
(2) The behaviour of the generated code is very unexpected.
(3) The compiler does not fully support subrange types.
All this does not seem to bother you. This is, of course, your privilege.
Thank you for the time spent on this. I wish you both a happy and fruitful life.
In ISO mode, the compiler now evaluates the lower and upper bound of the loop using the range-type as defined by the ISO standard.
|2013-04-24 18:50||Feliks Kluzniak||New Issue|
|2013-04-24 18:50||Feliks Kluzniak||File Added: range.p|
|2013-04-24 19:20||Florian||Note Added: 0067136|
|2013-04-25 10:20||Jonas Maebe||Note Added: 0067144|
|2013-04-25 10:20||Jonas Maebe||Status||new => resolved|
|2013-04-25 10:20||Jonas Maebe||Resolution||open => no change required|
|2013-04-25 10:20||Jonas Maebe||Assigned To||=> Jonas Maebe|
|2013-04-25 13:50||Feliks Kluzniak||Note Added: 0067151|
|2013-04-25 13:50||Feliks Kluzniak||Status||resolved => feedback|
|2013-04-25 13:50||Feliks Kluzniak||Resolution||no change required => reopened|
|2013-04-25 14:24||Jonas Maebe||Note Added: 0067152|
|2013-04-25 14:24||Jonas Maebe||Status||feedback => resolved|
|2013-04-25 14:24||Jonas Maebe||Resolution||reopened => no change required|
|2013-04-25 18:24||Florian||Note Added: 0067157|
|2013-04-25 23:27||Jonas Maebe||Note Edited: 0067152||View Revisions|
|2013-04-25 23:51||Feliks Kluzniak||Note Added: 0067164|
|2013-04-25 23:51||Feliks Kluzniak||Status||resolved => feedback|
|2013-04-25 23:51||Feliks Kluzniak||Resolution||no change required => reopened|
|2013-04-26 09:51||Jonas Maebe||Note Added: 0067167|
|2013-04-26 09:51||Jonas Maebe||Status||feedback => resolved|
|2013-04-26 09:51||Jonas Maebe||Resolution||reopened => no change required|
|2013-04-26 19:24||Feliks Kluzniak||Note Added: 0067177|
|2013-04-26 19:24||Feliks Kluzniak||Status||resolved => feedback|
|2013-04-26 19:24||Feliks Kluzniak||Resolution||no change required => reopened|
|2013-04-26 19:27||Feliks Kluzniak||Note Edited: 0067177||View Revisions|
|2013-04-26 19:53||Florian||Note Added: 0067178|
|2013-04-27 13:58||Maurice Lombardi||Note Added: 0067181|
|2013-04-28 00:11||Feliks Kluzniak||Note Added: 0067196|
|2013-04-28 00:11||Feliks Kluzniak||Status||feedback => assigned|
|2018-01-07 19:52||Jonas Maebe||Fixed in Revision||=> 37934|
|2018-01-07 19:52||Jonas Maebe||Note Added: 0105462|
|2018-01-07 19:52||Jonas Maebe||Status||assigned => resolved|
|2018-01-07 19:52||Jonas Maebe||Fixed in Version||=> 3.1.1|
|2018-01-07 19:52||Jonas Maebe||Resolution||reopened => fixed|
|2018-01-07 19:53||Jonas Maebe||Note Edited: 0105462||View Revisions|