Currently functions pointers can be implicitly converted despite having different calling conventions. If at all possible, function pointers should require a cast to change calling convention.
In a related problem, it is possible to implicitly convert function pointers with different argument lists.
The following code compiles but segfaults (Access Violation) on dmd 2.040 / winxp.
import std.stdio;
void foo(real f)
{
writeln("bar ", f);
}
void main()
{
void function(ubyte[4]) ptr1 = &foo;
extern(C) void function(long, long) ptr2 = ptr1;
ptr1([0xFF, 0xFF, 0xFF, 0xFF]);
ptr2(3, 8);
}
Function pointers cannot, however, be cast to a pointer with a different return type.
To solve this problem: Disable all implicit conversions between function pointers, requiring function parameters, return type, and calling convention to match for assignment copying. This prevents accidental conversions and allows template code to be aware of the correct calling convention.
Comment #1 by dfj1esp02 — 2010-10-18T10:58:09Z
*** Issue 5069 has been marked as a duplicate of this issue. ***
Comment #2 by bearophile_hugs — 2010-10-18T11:06:24Z
Sobirari Muhomori has closed bug 5069 because it's a duplicate of this one. But please don't ignore one thing I've written in bug 5069, about error messages, where for a wrong usage of std.c.stdlib.qsort I have asked for a single better error message (instead of the current two worse error messages):
Line 15: Error: cannot implicitly convert expression (& compare) of type int
function(const void*, const void*) to extern(C) int function(const void*, const
void*)
Comment #3 by dfj1esp02 — 2010-10-18T11:09:27Z
Uh, crash has a critical severity.
Comment #4 by dfj1esp02 — 2010-10-18T11:14:48Z
> The following code compiles but segfaults (Access Violation) on dmd 2.040 /
> winxp.
Is this a cross-platform bug?
Comment #5 by yebblies — 2010-10-18T19:29:40Z
(In reply to comment #4)
> Is this a cross-platform bug?
Yes. The problem is that pointers to functions with different calling conventions and parameters can be implicitly converted to each other. This can easily cause stack corruption and/or corrupted parameters. This is not unique to any platform.
------------------------------
import std.stdio;
import std.typetuple;
extern(D)
{
void d1() { writeln("d1"); }
void d2(int a) { writeln("d2(", a, ")"); }
void d3(int a, int b) { writeln("d3(", a, ", ", b, ")"); }
void d4(long a, long b) { writeln("d4(", a, ", ", b, ")"); }
}
extern(C)
{
void c1() { writeln("c1"); }
void c2(int a) { writeln("c2(", a, ")"); }
void c3(int a, int b) { writeln("c3(", a, ", ", b, ")"); }
void c4(long a, long b) { writeln("c4(", a, ", ", b, ")"); }
}
extern(Windows)
{
void w1() { writeln("w1"); }
void w2(int a) { writeln("w2(", a, ")"); }
void w3(int a, int b) { writeln("w3(", a, ", ", b, ")"); }
void w4(long a, long b) { writeln("w4(", a, ", ", b, ")"); }
}
extern(C++)
{
void cpp1() { writeln("cpp1"); }
void cpp2(int a) { writeln("cpp2(", a, ")"); }
void cpp3(int a, int b) { writeln("cpp3(", a, ", ", b, ")"); }
void cpp4(long a, long b) { writeln("cpp4(", a, ", ", b, ")"); }
}
extern(Pascal)
{
void p1() { writeln("p1"); }
void p2(int a) { writeln("p2(", a, ")"); }
void p3(int a, int b) { writeln("p3(", a, ", ", b, ")"); }
void p4(long a, long b) { writeln("p4(", a, ", ", b, ")"); }
}
void main()
{
size_t stack;
asm { mov stack, ESP; }
writeln(stack);
alias TypeTuple!(d1, d2, d3, d4, c1, c2, c3, c4, w1, w2, w3, w4, cpp1, cpp2, cpp3, cpp4, p1, p2, p3, p4) functions;
auto fptr = &d3;
foreach(f; functions)
{
fptr = &f;
fptr(1, 2);
}
asm { mov stack, ESP; }
writeln(stack);
}
Comment #6 by dfj1esp02 — 2010-10-19T11:03:16Z
*** Issue 4893 has been marked as a duplicate of this issue. ***
Comment #7 by dfj1esp02 — 2010-10-19T11:04:48Z
also
---
import core.stdc.stdio;
void f(int[] a)
{
a[0]=42;
}
int main()
{
immutable int[] b = [1,2,3];
void function(const int[] a) g=&f;
printf("%d\n",b[0]);
g(b);
printf("%d\n",b[0]);
return 0;
}
---
1
42
Comment #8 by yebblies — 2011-05-09T04:43:55Z
*** Issue 5827 has been marked as a duplicate of this issue. ***
Comment #9 by yebblies — 2011-05-13T06:00:57Z
*** Issue 5994 has been marked as a duplicate of this issue. ***
Comment #10 by yebblies — 2011-05-13T06:08:31Z
(In reply to comment #9)
> *** Issue 5994 has been marked as a duplicate of this issue. ***
According to Don in beta mailing list:
> Regression was introduced in 2.038.
Comment #11 by timon.gehr — 2011-05-14T08:12:54Z
(In reply to comment #8)
> *** Issue 5827 has been marked as a duplicate of this issue. ***
Well, not exactly. But I guess it can be fixed together. My bug report was about function pointers with _different return types_ converting to each other:
Timon Gehr wrote:
> The following invalid D code is accepted by DMD:
>
> import std.stdio;
>
> int a=0;
> ref int g(){
> writeln("called g");
> return ++a;
> }
>
> void main(){
> int function() f=&g; //this should issue an error!
> writeln(cast(int)&a);
> writeln(f());
> }
>
> Output:
> -144939256
> called g
> -144939256
>
> (&g is implicitly cast to "int function() ref", after that, the identical
> calling conventions for "ref int" (int*) and int result in an implicit
> reinterpret-cast from "ref int" to int.)
Comment #12 by yebblies — 2011-06-04T22:52:55Z
*** Issue 5821 has been marked as a duplicate of this issue. ***
Comment #13 by yebblies — 2011-06-06T19:33:41Z
*** Issue 4891 has been marked as a duplicate of this issue. ***
Comment #14 by yebblies — 2011-06-06T19:39:53Z
*** Issue 5434 has been marked as a duplicate of this issue. ***
Comment #15 by yebblies — 2011-06-06T20:01:10Z
The reason this happens is because TypeFunction does not override constConv. When called on TypeFunction, TypeNext::constConv is actually called, which only compares the return type. (next in TypeFunction is the return type)
I've created a possible fix in dmd pull 88 which defines TypeFunction::constConv to disallow most implicit conversions between function pointers.
The current patch allows reasonable purity, safety and nothrow conversions. If you're interested, please take a look and see if all cases are covered.