I have started testing the new for..in code.
The very first test I tried found a bug ;-)
---
const S = 'abc';
var ch: Char;
begin
for ch in S do Writeln(ch);
end.
---Mailing lists / fpc-devel / 2009-11
Testing for..in feature
46 messages · 12 people · –
- PPaul Ishenin 14
- AAlexander Klenin 10
- MMarco van de Voort 6
- FFlorian Klaempfl 4
- MMichael Van Canneyt 4
- VVinzent Höfler 2
- +6 more
Reply by email Subscribe to this list Your subscription
Read-only archive. Use j and k to move between messages.
Another minor problem:
I have started testing the new for..in code. The very first test I tried found a bug ;-)…
Alexander Klenin wrote 10 lines
I suppose you refer to the fact that the compiler should never allow this ;) Or do you want this to actually work?? An untyped const is fine with me and I use it for years, but should it be allowed for for..in?
Thaddy wrote 14 lines
I refer to the fact that iteration starts from position 0 instead of 1. I think this is just a minor glitch in ShortString vs AnsiString detection.
Or do you want this to actually work?? An untyped const is fine with me and I use it for years, but should it be allowed for for..in?
Do not see a reason why not.
Another minor problem: begin for ch in S do Writeln(ch); end. Results in: t.pp(2,10)…
Another bug:
---
var
ch: Char;
begin
for ch in [] do Writeln(ch);
end.
---=> AV in compiler
I have started testing the new for..in code. The very first test I tried found a bug ;-)…
Yet another bug:
---
type T = (a1, b1=5);
var
ch: T;
begin
for ch in T do Writeln(ch);
end.
---This leads to runtime error.
Note that
---
type T = (a1, b1=5);
begin
Writeln(Succ(a1));
end.
---leads to compile-time error
(although I think it should work, but that is a separate topic).
I have started testing the new for..in code. The very first test I tried found a bug ;-)…
Alexander Klenin wroteI have started testing the new for..in code. The very first test I tried found a bug ;-)
I would be nice to have all your tests in .pas/.pp files. Can you attached them all to some bug report?
Best regards,
Paul Ishenin.
Paul Ishenin wrote 8 lines
See http://bugs.freepascal.org/view.php?id=14990
Yet another bug: --- type T = (a1, b1=5); var ch: T; begin for ch in T do Writeln(ch);…
Alexander Klenin wrote 10 lines
This is caused by the problem in the for-to loop:
for ch := Low(T) to High(T) do
WriteLn(ch)How should I solve the problem in the for-in loop?
I tried the next way:
ch := Low(T);
while ch <= High(T) do
begin
WriteLn(ch);
inc(ch);But it fails the same way.
Best regards,
Paul Ishenin.
Show quoted text 26 lines
You can use the rtti generated for those enums.
This is caused by the problem in the for-to loop: for ch := Low(T) to High(T) do…
Paul Ishenin wrote 28 lines
ch := succ(ch);OTOH, I am not sure -- obviously either Succ or Inc is buggy, but which one? Maybe runtime instead of compile-time error is actually correct?
You can use the rtti generated for those enums.
Florian Klaempfl wroteYou can use the rtti generated for those enums.
For me it is still the question if for-to loop should work:
for ch := Low(T) to High(T) do
WriteLn(ch)Best regards,
Paul Ishenin.
This is caused by the problem in the for-to loop: for ch := Low(T) to High(T) do…
In our previous episode, Paul Ishenin said:
> Yet another bug: > > ---
{$apptype console}Show quoted text 6 lines
CodeGear Delphi for Win32 compiler version 20.0
Copyright (c) 1983,2008 CodeGear
testx.dpr(6) Error: E2029 '(' expected but 'DO' found testx.dpr(7) Error: E2430 for-in statement cannot operate on collection type 'T'
Since I suspected that D2009 doesn't work on sparse enums, I tried, and the above is the result.
Marco van de Voort wrote 20 lines
Does it work on not sparse enums?
Vincent
Vincent Snijders wroteDoes it work on not sparse enums?
Those are "dense" enums? :-)
Micha
In our previous episode, Micha Nelissen said:
> Does it work on not sparse enums? Those are "dense" enums? :-)
"true" enums.
Does it work on not sparse enums? Vincent
Vincent Snijders wrote 6 lines
Delphi compiler does not support for-in loop for types (enums, integer, ...). This is fpc extension :)
Best regards,
Paul Ishenin.
Does it work on not sparse enums? Vincent
In our previous episode, Vincent Snijders said:
yy> >>> end.
Show quoted text 11 lines
Good point. Same error.
In the help only strings,arrays, sets and collections (and in D2009 a records are explicely named) are described, not enums.
Delphi compiler does not support for-in loop for types (enums, integer, ...). This is fpc…
In our previous episode, Paul Ishenin said:
Show quoted text 7 lines
Do you have unit tests for multidimensional arrays. I saw quite funky examples (involving dummy variables) in the D2009 help for that.
Marco van de Voort wroteDo you have unit tests for multidimensional arrays. I saw quite funky examples (involving dummy variables) in the D2009 help for that.
No. I know delphi allows to enumerate multidimensional arrays but fpc traverses only the first dimension.
Best regards,
Paul Ishenin.
ch := succ(ch); OTOH, I am not sure -- obviously either Succ or Inc is buggy, but which…
In our previous episode, Alexander Klenin said:
Show quoted text 9 lines
Sparse enums are simply a kludge. They were needed because they exist in the Windows API, and afaik for that only. IOW for easy translation of Windows types.
I wouldn't assume that all rules for enums apply to sparse ones too.
Marco van de Voort wroteSparse enums are simply a kludge. They were needed because they exist in the Windows API, and afaik for that only. IOW for easy translation of Windows types.
They were added to Delphi in version 6. I though they added them to support Qt and the CLX, which relied on Qt as its underlying base toolkit. They were common in C/C++ before that though.
The problem I always had with Pascal supporting them was that a Pascal enum was designed to represents the position in a sequence and not necessarily an underlying integral value - where as a sparse enum is simply a grouping for a bunch of numerical values at the whim of the developer.
In our previous episode, Matt Emson said:
Show quoted text 5 lines
Well, anyway, foreign interfacing related, not core language feature.
Show quoted text 5 lines
I never had a problem with that. I use them in headers, and not otherwise, so I never have a problem :-)
No. I know delphi allows to enumerate multidimensional arrays but fpc traverses only the…
Paul Ishenin wrote 6 lines
To be more precise:
{$apptype console}
type
TSingleDimArray = array[0..1] of integer;
TMultyDimArray = array[0..1] of TSingleDimArray;
var
MultyDimArray: TMultyDimArray;
a: TSingleDimArray;
i: integer;
begin
MultyDimArray[0,0]:=1;
MultyDimArray[0,1]:=2;
MultyDimArray[1,0]:=3;
MultyDimArray[1,1]:=4;
for a in MultyDimArray do
for i in a do
WriteLn(i);
for i in MultyDimArray do
Writeln(i);
end.The example compiles and works fine in Delphi. In fpc the second for-in loop gives an error. I will try to solve this later.
Best regards,
Paul Ishenin.
They were added to Delphi in version 6. I though they added them to support Qt and the…
Matt Emson :
Show quoted text 5 lines
Or to map logical values to physical ones (on some hardware).
Loops and arrays would be a plus, but I'd consider a compile-time error sufficient here. ;) I'd still expect "Succ" and "Pred" to work properly on those types.
Vinzent.
You can use the rtti generated for those enums.
Florian Klaempfl wroteYou can use the rtti generated for those enums.
Actually I can't :)
uses
typinfo;
type T = (a1, b1=5);
var
ch: T;
Info: PTypeInfo;
begin
Info := TypeInfo(T);
compiler: project1.lpr(10,11) Error: No type info available for this typeSo what is the best solution for now:
1) check if enum has jumps and give an error
2) skip any compiler code modifications since the next code still works and outputs 5 numbers:
type T = (a1, b1=5);
var
ch: T;begin
for ch in T do
WriteLn(Ord(ch)); // <- note Ord() hereend
3) fix write/writeln to write something for the non-existent enum members (in the example they are 2, 3, 4) instead of giving runtime error
4) write 2 magic functions: one to get the next enum value, one to get the previos enum value and implement a special handling in the for-in loop for such arrays
Best regards,
Paul Ishenin.
Paul Ishenin wroteSo what is the best solution for now: 1) check if enum has jumps and give an error
This is what Succ/Pred do currently. I think that if we decide _not_ to choose option (1), current Succ/Prev behaviour should be considered an error.
Show quoted text 9 lines
What are these numbers? (Sorry, I am at work, can not test from here). I would be surprised if they are "0 5".
3) fix write/writeln to write something for the non-existent enum members (in the example they are 2, 3, 4) instead of giving runtime error
No, I think runtime error is correct here.
4) write 2 magic functions: one to get the next enum value, one to get the previos enum value and implement a special handling in the for-in loop for such arrays
I always thought Succ/Pred are exactly such magic functions ;-)
Alexander Klenin wrote 12 lines
0, 1, 2, 3, 4, 5
Show quoted text 5 lines
yes, forgot about them.
Best regards,
Paul Ishenin.
Paul Ishenin wrote 17 lines
Oh. I think it is better to generate error than to output such nonsense.
Show quoted text 9 lines
So, I vote for converting "for i in Enum do" into
while ... do
...
i := Succ(i);
end;This will automatically fail for non-consecutive enums, which is fine for now. If later it will be decided to extend Succ/Pred functions to handle non-consecutive enums,
for..in loops will also start working for them.
Alexander Klenin wrote 6 lines
No need to complicate the code generation for all enums. This can be done only for the enums with jumps.
Best regards,
Paul Ishenin.
This is what Succ/Pred do currently. I think that if we decide _not_ to choose option…
Alexander Klenin wrote 5 lines
Btw, I tested succ, pred with delphi and with fpc in mode delphi.
type
T = (a1, b1 = 5);
var
ch: T;
begin
ch := a1;
WriteLn(Ord(Succ(ch)));
end.How do you think what is the output :) Answer: '1'
So succ/pred works another way we need.
Best regards,
Paul Ishenin.
Actually I can't :) uses typinfo; type T = (a1, b1=5); var ch: T; Info: PTypeInfo; begin…
Show quoted text 16 lines
This rtti isn't published for program use but e.g. writeln(<enum>) uses it.
Actually I can't :) uses typinfo; type T = (a1, b1=5); var ch: T; Info: PTypeInfo; begin…
Paul Ishenin wrote 19 lines
This is the best solution. There are many other things that don't work with enums that have jumps; This is just an additional one.
Enums with jumps are just a kludge to mimic C enums, which are in reality just a bunch of constants .
Michael.
Michael Van Canneyt wrote 5 lines
This is the easiest solution.
Enums with jumps are just a kludge to mimic C enums, which are in reality just a bunch of constants .
Maybe we can improve that enums a bit? I see that compiler gives no way to traverse such enums. If you have no info about such type you can't work with it properly: succ, pred, inc, dec don't work as expected, typeinfo is not present. I think that maybe at least for-in loop can work for them :)
You and Florian as well as my left and right cerebral hemispheres give me completely different directions. Therefore I stay with the same question. I will wait for more opinions.
Best regards,
Paul Ishenin.
This rtti isn't published for program use but e.g. writeln(<enum>) uses it.
Florian Klaempfl wroteThis rtti isn't published for program use but e.g. writeln(<enum>) uses it.
Yes, I was surprised when I tried to look why writeln gives a runtime error for unexistent element of enum.
Also I looked at compile - it just checks if enum has jumps and refuses typeinfo() call for it. Why does compiler hides this info?
Best regards,
Paul Ishenin.
Show quoted text 10 lines
Everything exposed must be maintained ;) I guess the reason is that it was an experimental implementation first and it was not enabled, later, nobody documented it and enabled it.
Yes, I was surprised when I tried to look why writeln gives a runtime error for…
Paul Ishenin wrote 9 lines
Because - for one thing - the RTTI functions of the typinfo unit will fail on it.
They expect 0 to be the first value, and count(elements)-1 to be the last, and everything else between should be valid values.
tmyenum = (a,b,c=-1,D);does not cope with this. If you want to support succ(), pred() and whatnot, you'll have to rewrite a large part of the RTL and RTTI stuff, which I think is such a can of works and so risky, that it isn't worth the effort. You'll also have to write all possible values, and so on. A can of worms you don't want to open.
Such enums should be used only to import some braindead C headers. Like I said, it's just a set of constants, wrapped in a type.
Maybe we should not name them enumerated.
Michael.
Michael Van Canneyt wroteBecause - for one thing - the RTTI functions of the typinfo unit will fail on it.
This is unfair. Why compiler functions (like write) have advantage over user functions.
What if user needs this info very much? Maybe allow typeinfo for them and document it limitation is better? Or add another special function: TypeInfo(TypeIdentifier, IKnowAboutLimitations: Boolean = False) ?
Best regards,
Paul Ishenin.
Paul Ishenin wrote 11 lines
Fair has nothing to do with it. Enums with jumps are an ugly hack, and go against all that enumerateds stand for. They only ensure some kind of type safety.
Using this ugly hack has consequences and should not be encouraged. Consequences:
- No for to
- No Succ/pred
- No low() high()
- No RTTI
and now, because you implemented it:
- No For in.
Like I said, we should not have named it enums. They are not enums. They're a hack to support some C construct in a type-safe way. No more, no less.
At the best you can name them 'auto-generated constants with possibility to specify an exception'.
Michael.
Michael Van Canneyt wroteFair has nothing to do with it. Enums with jumps are an ugly hack, and go against all that enumerateds stand for. They only ensure some kind of type safety.
Enums with jumps are hacks - agree.
Fair has nothing to do with it - disagree.
and now, because you implemented it: - No For in.
Under you presure I saw no possibility to implement for-in loop for that enums :) So I completely disabled for-in loop for them.
Best regards,
Paul Ishenin.
Yes, I was surprised when I tried to look why writeln gives a runtime error for…
Paul Ishenin wroteAlso I looked at compile - it just checks if enum has jumps and refuses typeinfo() call for it. Why does compiler hides this info?
http://bugs.freepascal.org/view.php?id=9551
Jonas
Enums with jumps are hacks - agree. Fair has nothing to do with it - disagree. Under you…
Paul Ishenin wrote 10 lines
I think it is a wise decision. It's an ugly construct, and is not worth the trouble (and I am convinced there would be more trouble than we can currently see :( )
Michael.
Fair has nothing to do with it. Enums with jumps are an ugly hack, and go against all…
Von: Michael Van Canneyt :
Like I said, we should not have named it enums. They are not enums. They're a hack to support some C construct in a type-safe way. No more, no less.
IBTD.
-- 8< --
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Conversion;procedure Enumeration_Magic is
-- Enumeration type.
type Op_Codes is (Jmp, Inc, Dec);
-- Representation with holes and same size as Standard.Integer.
for Op_Codes use (Jmp => 1, Inc => 2, Dec => 5);
for Op_Codes'Size use Integer'Size;
-- Unsafe cast function to get access to the actual values.
function To_Int is new Ada.Unchecked_Conversion (Source => Op_Codes,
Target => Integer);
Low : constant Op_Codes := Op_Codes'First;
High : constant Op_Codes := Op_Codes'Last;
begin
-- First/Last.
Ada.Text_IO.Put_Line ("Low => " & Low'Img &
", High => " & High'Img);
-- Loop through the range.
for X in Op_Codes'Range loop
Ada.Text_IO.Put_Line (X'Img & " => " & To_Int (X)'Img);
end loop;
end Enumeration_Magic;
-- 8< --Output:
-- 8< --
Low => JMP, High => DEC
JMP => 1
INC => 2
DEC => 5
-- 8< --
They might be an ugly hack, but I really doubt the part about being a C construct. And then, even in Ada, the definitions have to be in (ascending) order. Which means, the compiler only has to support "holes", not an arbitrary set of values.
Maybe this would be an option for FPC, too?
Vinzent.
"Vinzent Höfler" wrote 7 lines
[skip Ada sample]
They might be an ugly hack, but I really doubt the part about being a C construct. And then, even in Ada, the definitions have to be in (ascending) order. Which means, the compiler only has to support "holes", not an arbitrary set of values. Maybe this would be an option for FPC, too?
I agree that there is, in principle, nothing wrong with sparse enums. However, Ada is well-known for sacrificing performance for correctness and generality, and I am not sure if FPC should follow that. The best Succ(X) algorithm I can think of that works correctly on sparse enums is O(log enum_size).
Which means, the compiler only has to support "holes", not an arbitrary set of values.
Yes, current enum implementation in FPC is slightly broken in this aspect:
---
type
T = (a = 0, b = 0);
begin
Writeln(b);
end.
---
Prints 'a'.The best Succ(X) algorithm I can think of that works correctly on sparse enums is O(log enum_size).
It depends how much memory one wastes ;)
On Fri, Nov 6, 2009 at 05:51, "Vinzent Höfler" [skip Ada sample] I agree that there is,…
Alexander Klenin wroteThe best Succ(X) algorithm I can think of that works correctly on sparse enums is O(log enum_size).
As Florian does, I assume you discard the solution to use a lookup table from 'sparse values' to 'dense values'. Obviously that may quickly consume large amounts of memory, especially if the enum contains powers of two.
In theory, for .. in .. for sparse enums can be implemented in O(n) though. But then you should avoid using Succ.
Or, the memory location for a sparse enum variable could always contain a linear, 'dense' index into the table which holds the actual, 'sparse' values. Obviously this has a performance impact over not supporting Succ for sparse enums, but it doesn't change the runtime complexity of any code.
The question remains whether you're willing to put that much effort into it and whether you can live with the impact of doing table lookups each time the enum value needs to be casted to an integer (such as when calling C code).
Regards,
Bram
