import std.traits;
auto fun(T)(out T ret) {
}
version(none) {
auto fun(T)(T ret) {
}
}
void main() {
struct A {
auto f() {
}
}
A a;
fun!A(a);
}
With the latest dmd (from HEAD), I get the following compile errors:
test.d(3): Error: cannot access frame pointer of test.main.A
test.d(18): Error: template instance test.fun!(A) error instantiating
This only happens when the struct has a function and it's passed as an out parameter. This worked fine on DMD 2.060, but breaks with the HEAD on master.
Any following will cause this to compile:
* remove f()
* make A static
* change out in fun to anything else (ref, in, ...)
This may be related to http://d.puremagic.com/issues/show_bug.cgi?id=8850
Tests run on Linux x64, dmd compiled with 'gmake -f posix.mak MODEL=64;'
Comment #1 by k.hara.pg — 2012-11-07T05:12:10Z
This is a corner case of fixing bug 8339, but I think the current behavior is intended.
Let's try to replace the out parameter usage to a simple pointer in your code.
auto fun(T)(T* ret) {
*ret = T(); // Initialize the referred variable to T.init.
// It's automatically inserted by the compiler.
}
void main() {
struct A {
auto f() {}
}
A a;
fun!A(&a);
}
With current dmd the 'out' variable is initialized at the callee side, not caller side. It requires A's enclosing frame pointer for the correct initialization, but fun!A cannot access to main's frame, so compiler reports errors as you have shown.
Comment #2 by bugzilla — 2012-11-13T23:37:24Z
D templates have an interesting feature where they can be "locally instantiated" if their arguments refer to local arguments. This is controlled by the "isnested" member in template.c, set by TemplateInstance::hasNestedArgs().
It should have the effect of moving the definition of fun() into main(), like this:
--------------------------
void main() {
auto fun(T)(T* ret) {
*ret = T();
}
struct A {
auto f() {}
}
A a;
fun!A(&a);
}
-----------------------
which compiles successfully. So I'd say it is a bug, and the fault is likely in TemplateInstance::hasNestedArgs().
Comment #3 by k.hara.pg — 2012-11-14T00:13:59Z
(In reply to comment #2)
> So I'd say it is a bug, and the fault is likely in
> TemplateInstance::hasNestedArgs().
I think that the rule should not be applied to the template type argument, even if the type is a nested struct.
Let's consider more complicated case.
This code currently compiles successfully.
auto foo() {
struct X { void f(){} } // nested
return X();
}
auto bar() {
struct Y { void g(){} } // nested
return Y();
}
// import std.typecons;
struct Tuple(T...) {
T field;
}
Tuple!T tuple(T...)(T args) {
return typeof(return)(args);
}
void main() {
auto t = tuple(foo(), bar());
}
In above code, the template struct Tuple is instantiated with X and Y.
If the nested struct type argument make the given template instance nested, Tuple!(X, Y) should have two context pointers, but the code generation will fail.
Then the change you say will break much existing code. I think it is not mostly acceptable.
Comment #4 by bugzilla — 2012-11-14T00:31:21Z
The example you gave an example of a bug where a reference to a local is returned, something entirely different.
Comment #5 by k.hara.pg — 2012-11-14T01:07:54Z
(In reply to comment #4)
> The example you gave an example of a bug where a reference to a local is
> returned, something entirely different.
No, they are is related.
My argue is: there is no generic rule which make Tuple!(X, Y) un-nested but fun!A make nested. TemplateInstance::hasNestedArgs() works on the template instance, so it cannot know the actual instantiated result is a type (from Tuple!(X, Y)) or a function (from fun!A). It's time paradox.
To implement your argument, we need much special code to support the case.
It increases the compiler complexity for the much less benefit.
Comment #6 by k.hara.pg — 2012-11-14T01:08:01Z
Furthermore, the original code had generated incorrect code silently. This slight complicated code might cause access violation in 2.060 or earlier.
auto fun(T)(out T ret) {
// ret is incorrectly initialized,
// and has null context pointer
ret.f(); // Access Violation
}
void main() {
int val;
struct A {
auto f(){ val = 1; }
}
A a;
fun!A(a);
}
So, I'd like to argue that this issues should be an 'accepts-invalid' bug rather than a regression.
Comment #7 by bugzilla — 2012-11-14T01:13:28Z
I agree that the original code silently generated wrong code. So this isn't really a regression.
But I think we can fix it. There is no way to make your example work, because it is returning a reference to a local. But the original one can work, and there are many examples of local structs being used as parameters to global templates, and they work because of the isnested logic.
Comment #8 by k.hara.pg — 2012-11-14T02:05:18Z
(In reply to comment #7)
> But I think we can fix it. There is no way to make your example work, because
> it is returning a reference to a local. But the original one can work, and
> there are many examples of local structs being used as parameters to global
> templates, and they work because of the isnested logic.
Since months ago, I have fixed some nested struct and template bugs not to break existing codes. From the experience, it seems much difficult to *fix* this issue. If you can implement that without ugly hack, I'd like to see it before merging to the main trunk.
Could you create a pull request for that?
Comment #9 by github-bugzilla — 2012-12-25T18:28:58Z
This isn't a regression, it was an error not detected before.
The commit is a fix for it, but the fix breaks the test suite (nested.d) and the phobos unittests for std.array. I suspect the test suite is wrong, but for the moment the fix is commented out.
Comment #11 by Bastiaan — 2019-01-17T19:36:05Z
Today I was hit by this. I was able to work around it, but still like to be on the CC list.
Note: the committed fix is still not enabled. Six years have passed, maybe give it another go?
:-)
Comment #12 by robert.schadek — 2024-12-13T18:01:48Z