Hi
I have found a possible problem, illustrated in the three programs below (and in the comments):
program one; uses windows; var h : THandle = LoadLibrary ('winstl32.dll'); v : ^function : integer = GetProcAddress (h, 'Chief32DllVersion'); begin Writeln ('Handle=', h); Writeln ('V is assigned=', Assigned (v)); { TRUE } Writeln ('Version=', v^); { correct result } Writeln ('DLL Unloaded=',FreeLibrary (h)); { TRUE } end.
program two; uses windows; var h : THandle = LoadLibrary ('winstl32.dll'); v : function : integer = GetProcAddress (h, 'Chief32DllVersion'); begin Writeln ('Handle=', h); Writeln ('V is assigned=', Assigned (v)); { TRUE } Writeln ('Version=', v); { wrong result } Writeln ('DLL Unloaded=',FreeLibrary (h)); { TRUE } end.
program three; uses windows; var h : THandle = LoadLibrary ('winstl32.dll'); v : function : integer = GetProcAddress (h, 'Chief32DllVersion'); begin Writeln ('Handle=', h); Writeln ('V is assigned=', Assigned (v)); { TRUE } @v := GetProcAddress (h, 'Chief32DllVersion'); { saves the day } Writeln ('Version=', v); { correct result } Writeln ('DLL Unloaded=', FreeLibrary (h)); { TRUE } end.
What is the possible problem?
1. With all programs, declarations of "h" and "v" are accepted by GPC. But they are not accepted by Delphi, FreePascal, VirtualPascal, or BP ("cannot evaluate this expression" or "constant expression expected"). Is this an EP thing or is it a GPC extension?
BTW: gcc (Mingw) doesn't accept this type of initialisation.
2. As you can see from the comments, the assignment of "v" to GetProcAddress ... seems to succeed (in that the variable is indeed assigned), but silently produces a wrong result, meaning that it didn't really get assigned as it should.
Any comments? Thanks.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.greatchief.plus.com/
Prof A Olowofoyeku wrote:
program two; uses windows; var h : THandle = LoadLibrary ('winstl32.dll'); v : function : integer = GetProcAddress (h, 'Chief32DllVersion'); begin Writeln ('Handle=', h); Writeln ('V is assigned=', Assigned (v)); { TRUE } Writeln ('Version=', v); { wrong result } Writeln ('DLL Unloaded=',FreeLibrary (h)); { TRUE } end.
What is the possible problem?
- With all programs, declarations of "h" and "v" are accepted by GPC.
But they are not accepted by Delphi, FreePascal, VirtualPascal, or BP ("cannot evaluate this expression" or "constant expression expected"). Is this an EP thing or is it a GPC extension?
BTW: gcc (Mingw) doesn't accept this type of initialisation.
GPC extension (motivated by EP).
- As you can see from the comments, the assignment of "v" to
GetProcAddress ... seems to succeed (in that the variable is indeed assigned), but silently produces a wrong result, meaning that it didn't really get assigned as it should.
There is a bug: instead of assigning result of the call to GetProcAddress GPC is assigning (address of) GetProcAddress itself.
One can easily fix this example (so that GPC reports type error), but as usual parameterless functions are a tricky case. Below partial fix:
--- p/typecheck.c.bb 2005-02-28 13:40:33.000000000 +0100 +++ p/typecheck.c 2005-03-08 04:45:14.612619688 +0100 @@ -1624,7 +1626,8 @@ /* Procedural variables. (Pointers to routines should cause no problems.) */ if (TREE_CODE (type) == REFERENCE_TYPE && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE - && TREE_CODE (TREE_VALUE (init)) == CALL_EXPR) + && TREE_CODE (TREE_VALUE (init)) == CALL_EXPR + && TREE_OPERAND (TREE_VALUE (init), 1) == NULL_TREE) { tree op = TREE_OPERAND (TREE_VALUE (init), 0); if (TREE_CODE (op) == ADDR_EXPR)
On 8 Mar 2005 at 5:09, Waldek Hebisch wrote:
Prof A Olowofoyeku wrote:
program two; uses windows; var h : THandle = LoadLibrary ('winstl32.dll'); v : function : integer = GetProcAddress (h, 'Chief32DllVersion'); begin Writeln ('Handle=', h); Writeln ('V is assigned=', Assigned (v)); { TRUE } Writeln ('Version=', v); { wrong result } Writeln ('DLL Unloaded=',FreeLibrary (h)); { TRUE } end.
What is the possible problem?
- With all programs, declarations of "h" and "v" are accepted by GPC.
But they are not accepted by Delphi, FreePascal, VirtualPascal, or BP ("cannot evaluate this expression" or "constant expression expected"). Is this an EP thing or is it a GPC extension?
BTW: gcc (Mingw) doesn't accept this type of initialisation.
GPC extension (motivated by EP).
- As you can see from the comments, the assignment of "v" to
GetProcAddress ... seems to succeed (in that the variable is indeed assigned), but silently produces a wrong result, meaning that it didn't really get assigned as it should.
There is a bug: instead of assigning result of the call to GetProcAddress GPC is assigning (address of) GetProcAddress itself.
One can easily fix this example (so that GPC reports type error), but as usual parameterless functions are a tricky case. Below partial fix:
--- p/typecheck.c.bb 2005-02-28 13:40:33.000000000 +0100 +++ p/typecheck.c 2005-03-08 04:45:14.612619688 +0100
[...]
Works for me, thanks.
Another problem with function types. Delphi and BP do not accept this program;
program bug1; {$X+} type tfunc = function : pointer; var bar : tfunc; function foo : integer; begin foo := 1024 * 2; end; begin bar := tfunc (foo); { this is where Delphi and BP barf } writeln (integer (bar)); end.
GPC compiles the code, and then the program proceeds to crash when it is run. FreePascal compiles the code, and runs it correctly without crashing. Delphi accepts this instead "bar := tfunc (@foo);", and GPC of course accepts it. Both run it correctly. BP does not accept it - but that is not imporant.
The question is: should GPC compile "bar := tfunc (foo);" only for the program to crash when it gets there? If GPC refuses to compile it, that would be consistent with both BP and Delphi. If GPC compiles it and the program runs correctly, that would be fine also. But if the program will crash, then perhaps GPC should refuse to compile the line. Any comments? Thanks.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.greatchief.plus.com/
Prof A Olowofoyeku wrote:
On 8 Mar 2005 at 5:09, Waldek Hebisch wrote:
Another problem with function types. Delphi and BP do not accept this program;
program bug1; {$X+} type tfunc = function : pointer; var bar : tfunc; function foo : integer; begin foo := 1024 * 2; end; begin bar := tfunc (foo); { this is where Delphi and BP barf } writeln (integer (bar)); end.
GPC compiles the code, and then the program proceeds to crash when it is run. FreePascal compiles the code, and runs it correctly without crashing. Delphi accepts this instead "bar := tfunc (@foo);", and GPC of course accepts it. Both run it correctly. BP does not accept it - but that is not imporant.
The question is: should GPC compile "bar := tfunc (foo);" only for the program to crash when it gets there? If GPC refuses to compile it, that would be consistent with both BP and Delphi. If GPC compiles it and the program runs correctly, that would be fine also. But if the program will crash, then perhaps GPC should refuse to compile the line. Any comments? Thanks.
The differences are: 1) GPC allows casting integers to function types 2) Inside a cast to function type GPC calls `foo'. It seems that FPC just uses adress of `foo'.
Concering 1: it is dangerous, but sometimes convenient. Usually we prefer safe constructs and implement unsafe versions only for compatibility. But casts are dangerous anyway... Of course, we could requrire double casts (which even now are sometimes necessary) like: tfunc (pointer(foo))
Concering 2: There is genuine ambiguity here. Namely, for parameterless function we can either call the function and cast the value or cast the reference to the function. Arguably, the second choice is more natural. But assuming that we need to mix functions and integers (say, in an interpreter for untyped language), then we may wish to write:
program bug1; {$X+} type tfunc = function : pointer; function baz : integer; begin baz := 1024 * 2; end; var bar : tfunc; function foo : integer; begin foo := integer(@baz); end; begin bar := tfunc (foo); { this is where Delphi and BP barf } writeln (integer (bar)); end.
For this program GPC gives "correct" result. In default mode FPC rejects it, in Delphi mode FPC accepts it but gives "wrong" result. BTW, when I write:
bar := tfunc (foo());
then my copy of FPC (1.9.4 on Debian) still gives wrong result.
As you can see there is real mess here. I agree that on your example FPC way is more usefull than GPC way. But before we change GPC we should know what _exact_ rules BP, Delphi and FPC use (BTW any dialect with casts and function types is worth checking (Mac ???)). Also, we need to distinguish intended behaviour and bugs (I consider FPC treatment of `tfunc (foo());' as an obvious bug).
On 9 Mar 2005 at 2:01, Waldek Hebisch wrote:
[...]
The differences are:
- GPC allows casting integers to function types
- Inside a cast to function type GPC calls `foo'. It seems that FPC just uses adress of `foo'.
Concering 1: it is dangerous, but sometimes convenient. Usually we prefer safe constructs and implement unsafe versions only for compatibility. But casts are dangerous anyway... Of course, we could requrire double casts (which even now are sometimes necessary) like: tfunc (pointer(foo))
Concering 2: There is genuine ambiguity here. Namely, for parameterless function we can either call the function and cast the value or cast the reference to the function. Arguably, the second choice is more natural.
That is what I was intending in the "bar := tfunc (foo);" assignment (i.e., to assign to "bar" the address of "foo") - not to call "foo". You are right that the call is ambiguous. Perhaps Delphi and BP just refuse to guess what the programmer is trying to do, and issue a compiler error - however, perhaps they also assume that I want to call the function "foo", but refuse to do so because they don't like the type-cast.
GPC guesses (wrongly, as far my intentions are concerned) that I want to call "foo", and does so. But "bar" is not assigned a valid address, and so the call to it crashes the program. FPC guesses (rightly, as far as my intentions are concerned) that what I want is the address of "foo", and issues it. Bottom line - the compiler should really not have to guess what I am trying to do, especially since I could have removed any ambiguity by using "bar := tfunc (addr (foo));" or "bar := tfunc (@foo);", as I eventually did.
But assuming that we need to mix functions and integers (say, in an interpreter for untyped language), then we may wish to write:
program bug1; {$X+} type tfunc = function : pointer; function baz : integer; begin baz := 1024 * 2; end; var bar : tfunc; function foo : integer; begin foo := integer(@baz); end; begin bar := tfunc (foo); { this is where Delphi and BP barf } writeln (integer (bar)); end.
For this program GPC gives "correct" result.
This result is correct if the assumption that the intention is to call "foo", rather than to assign the address of "foo".
In default mode FPC rejects it, in Delphi mode FPC accepts it but gives "wrong" result.
As above. FPC assumes that what I want is the address of "foo", and gives it to me. That is as far my original intentions are concerned. The code, as written, does actually mean that "foo" should be called. This is the assumption (correct, regardless of my intentions) of GPC, BP and Delphi (even though BP and Delphi do not like the call).
BTW, when I write:
bar := tfunc (foo());
then my copy of FPC (1.9.4 on Debian) still gives wrong result.
What it gives is the value of "foo", which, inside "foo", you assign the address of "baz" (i.e., foo returns the address of baz as an integer). That seems correct to me.
As you can see there is real mess here.
Indeed.
I agree that on your example FPC way is more usefull than GPC way. But before we change GPC we should know what _exact_ rules BP, Delphi and FPC use
Well, it seems to me that BP and Delphi and GPC are all assuming that, by assigning a value to "bar", what we really want to do is to call "foo", rather than to assign it's address to "bar". However, since both BP and Delphi reject the call, perhaps GPC should also reject it in BP and Delphi mode (Virtual Pascal also rejects it by the way). If GPC will accept it at all (which it should when in GPC mode), then it should assign the address of "foo", rather than to call the function (just like FPC).
(BTW any dialect with casts and function types is worth checking (Mac ???)). Also, we need to distinguish intended behaviour and bugs (I consider FPC treatment of `tfunc (foo());' as an obvious bug).
Not necessarily. Since "bar" is a pointer to a function, when we assign a value to it, it would seem natural to expect that what we are assigning to it is an address, since that is all that can be assigned to it. However, Delphi accepts 'tfunc (foo());' and treats it as a call to "foo", rather than the assignment of "foo's" address. So the trailing brackets make all the difference to Delphi's acceptance or rejection of the code.
In conclusion, I think this is what GPC should do:
1. "bar := tfunc (foo)" = assign to bar the address of foo, but don't call foo
2. "bar := tfunc (foo ())" = call foo (and also assign to bar the address of foo?) so that "writeln (integer (bar))" should result in a second call to foo - otherwise, what value does bar hold?
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.greatchief.plus.com/
Prof A Olowofoyeku wrote:
On 9 Mar 2005 at 2:01, Waldek Hebisch wrote:
BTW, when I write:
bar := tfunc (foo());
then my copy of FPC (1.9.4 on Debian) still gives wrong result.
What it gives is the value of "foo", which, inside "foo", you assign the address of "baz" (i.e., foo returns the address of baz as an integer). That seems correct to me.
I do not know what your FPC is doing, but AFAICS my copy of FPC treats:
bar := tfunc (foo);
and
bar := tfunc (foo());
the same. Now, the first form is ambiguous, and what FPC is doing with this form is usefull and reasonable. IMHO the second form has only one possible meaning: empty parentheses mean that `foo' should be called and the _return value_ of `foo' should be converted and assigned to `bar'.
In conclusion, I think this is what GPC should do:
- "bar := tfunc (foo)" = assign to bar the address of foo, but don't
call foo
- "bar := tfunc (foo ())" = call foo (and also assign to bar the
address of foo?) so that "writeln (integer (bar))" should result in a second call to foo - otherwise, what value does bar hold?
I see two clear cases:
bar := tfunc (@foo) { assign to bar the address of foo }
and
bar := tfunc (foo ()) { call foo and assign the return value to bar }
Plain `foo' should either mean `@foo' or `foo ()' or return variable of function `foo' (but here I am interested in values, so the third case is excluded) depending on context. I am slightly oversimpifing here -- IIRC we sometimes reject `@foo' where `foo' is acceptable, but to point is that we have to decide if we want address of foo or we want the return value. We have a bunch of syntactic contexts: left hand side of an assignment, initializers, casts, function parameters (three sorts of function parameters: value parameters, variable parameters and functional parameters).
Note that beside plain functions ("function constants") we have also function variables, pointers and results of casts so we can form rather large collection of function valued expressions.
IMHO we should use the same rule for assignment, initializers and value parameters. I am tempted to say that we should use the same rule also for variable parameters and functional parameters, but variable parameters have special restrictions and ATM functional parameters do less strict type checking then assignment.
Casts are really nasty case, since in BP casts frequently give different result then assignment (when both are legal). Also, it seems that BP casts are supposed to handle values (except for casts as lvalues). So for example is:
i := integer(foo); bar := tfunc(pointer(i));
or
bar := tfunc(pointer(integer(foo)));
the same as:
bar := tfunc(foo);
(In FPC one should use `longint' instead of `integer'.)
What happens when the return type of `foo' is `pointer'? What if return type of `foo' is pointer to `tfunc'?
On 10 Mar 2005 at 3:55, Waldek Hebisch wrote:
Prof A Olowofoyeku wrote:
On 9 Mar 2005 at 2:01, Waldek Hebisch wrote:
BTW, when I write:
bar := tfunc (foo());
then my copy of FPC (1.9.4 on Debian) still gives wrong result.
What it gives is the value of "foo", which, inside "foo", you assign the address of "baz" (i.e., foo returns the address of baz as an integer). That seems correct to me.
I do not know what your FPC is doing, but AFAICS my copy of FPC treats:
bar := tfunc (foo);
and
bar := tfunc (foo());
the same.
Yes (as does GPC (currently)).
Now, the first form is ambiguous, and what FPC is doing with this form is usefull and reasonable. IMHO the second form has only one possible meaning: empty parentheses mean that `foo' should be called and the _return value_ of `foo' should be converted and assigned to `bar'.
I am not sure. I think the second form is just as ambiguous as the first. The parentheses may simply signify that "foo" is a function/procedure as opposed to a variable (where parentheses should cause an error), in which case, it is no different from not using the parentheses. In neither case would I want a conversion of the return value of "foo". In both cases, I would want "bar" to hold the address of "foo", because that is the purpose of "bar" (i.e., to hold the address of a function), so that the expressions "bar;", or "bar();" or "writeln (Integer (bar));" will each cause function "foo" to be called.
In conclusion, I think this is what GPC should do:
- "bar := tfunc (foo)" = assign to bar the address of foo, but don't
call foo
- "bar := tfunc (foo ())" = call foo (and also assign to bar the
address of foo?) so that "writeln (integer (bar))" should result in a second call to foo - otherwise, what value does bar hold?
I see two clear cases:
bar := tfunc (@foo) { assign to bar the address of foo }
Yes.
and
bar := tfunc (foo ()) { call foo and assign the return value to bar }
My instincts would say "call foo and assign its address to bar" (because I would have thought that "bar;" should also result in a call to "foo"). In Delphi, calling "bar;" or "bar();" does nothing. Surely that must be wrong? However, my instincts and feelings are irrelevant. There is an ambiguity IMHO, and it should be resolved. Delphi's resolution of it corresponds to yours, and that is fine by me (if only for the sake of compatibility with Delphi), even though I still find it strange that calling "bar" after assigning it a value does nothing.
Plain `foo' should either mean `@foo' or `foo ()' or return variable of function `foo' (but here I am interested in values, so the third case is excluded) depending on context. I am slightly oversimpifing here -- IIRC we sometimes reject `@foo' where `foo' is acceptable, but to point is that we have to decide if we want address of foo or we want the return value.
I think we can resolve things on the basis that; plain "foo" = "@foo" { ( FPC ) } and "foo ()" = "call foo" { ( GPC, Delphi ) }
BTW: "@bar := tfunc (@foo);" is treated in exactly the same way by GPC, Delphi, and FPC (BP, of course, doesn't compile it). In this case, we are stating clearly that what we want is an address.
We have a bunch of syntactic contexts: left hand side of an assignment, initializers, casts, function parameters (three sorts of function parameters: value parameters, variable parameters and functional parameters).
Note that beside plain functions ("function constants") we have also function variables, pointers and results of casts so we can form rather large collection of function valued expressions.
IMHO we should use the same rule for assignment, initializers and value parameters. I am tempted to say that we should use the same rule also for variable parameters and functional parameters, but variable parameters have special restrictions and ATM functional parameters do less strict type checking then assignment.
Casts are really nasty case, since in BP casts frequently give different result then assignment (when both are legal). Also, it seems that BP casts are supposed to handle values (except for casts as lvalues). So for example is:
i := integer(foo);
This calls the function, and assigns its return value.
bar := tfunc(pointer(i));
This type-casts a numeric return value. It is not the same as "bar := tfunc(foo);"
or
bar := tfunc(pointer(integer(foo)));
Off the top of my head, I think FPC would treat this as the same as "bar := tfunc(foo);" - i.e., returning an address. GPC would also treat it as the same - but currently calling "foo" and returning its return value.
(In FPC one should use `longint' instead of `integer'.)
What happens when the return type of `foo' is `pointer'? What if return type of `foo' is pointer to `tfunc'?
I am getting a headache! :-)
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.greatchief.plus.com/