Bug 9748 – Wrong scope of templated nested functions in static foreach
Status
RESOLVED
Resolution
DUPLICATE
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2013-03-18T08:01:00Z
Last change time
2015-07-27T13:16:34Z
Keywords
wrong-code
Assigned to
nobody
Creator
dlang-bugzilla
Comments
Comment #0 by dlang-bugzilla — 2013-03-18T08:01:51Z
template Tuple(T...) { alias T Tuple; }
void main()
{
foreach (i; Tuple!(1, 2, 3))
{
uint j;
void set()(int n) { j = n; }
set(i);
assert(j==i);
}
}
This fails because the "set" function will always refer to the "j" variable declared in the first foreach iteration. If you move the "j" declaration outside the loop, the asserts pass.
Comment #1 by k.hara.pg — 2013-10-03T00:48:48Z
More explicit test case:
template Tuple(T...) { alias T Tuple; }
extern(C) int printf(const char*, ...);
void main()
{
//foreach (i; Tuple!(1, 2))
{
enum i = 1;
uint j;
void set()(int n) { j = n; printf("set1\n"); }
set(i);
printf("i = %d, j = %d\n", i, j);
assert(j == i);
}
//foreach (i; Tuple!(1, 2))
{
enum i = 2;
uint j;
void set()(int n) { j = n; printf("set2\n"); }
set(i);
printf("i = %d, j = %d\n", i, j);
assert(j == i);
}
}
The second 'set' function template is not called.
Comment #2 by pro.mathias.lang — 2014-09-27T11:22:20Z
Real world use case where I hit this bug:
import std.typetuple, std.traits;
struct UDAStruct {
string identifier;
}
class MyClass {
@(UDAStruct("p3"), UDAStruct("P2"), UDAStruct("p1"))
void func(int p1, string p2, float p3) {}
}
template CmpIdentifier(UDAStruct uda, string pname) {
pragma(msg, "Instantiated for: "~pname);
enum CmpIdentifier = (uda.identifier == pname);
}
unittest {
alias Func = MyClass.func;
enum ParamNames = ParameterIdentifierTuple!Func;
enum ParamAttr = __traits(getAttributes, Func);
foreach (attr; ParamAttr) {
alias Cmp(string s) = CmpIdentifier!(attr, s);
pragma(msg, "Current attr is: "~attr.identifier);
static assert(anySatisfy!(Cmp, ParamNames));
}
}
void main() {}
Output:
Current attr is: p3
Instantiated for: p1
Instantiated for: p2
Instantiated for: p3
Current attr is: P2
Current attr is: p1
(From: http://forum.dlang.org/thread/[email protected] )
Comment #3 by bugzilla — 2014-10-16T18:45:53Z
Even smaller test case report from Mathias Lang:
void main()
{
{
void set()(int i) { assert(i); }
set(1);
}
{
void set()(int i) {}
set(0);
}
}
You probably know what's going to happen: the assert would be triggered. The FE would behave correctly, but backend will mangle the two functions the same way, leading to the first instance of "set" replacing the second one, and a nice linker message (Linux x86_64):
/usr/bin/ld: Warning: size of symbol `_D9a_bug97484mainFZ8__T3setZ3setMFNaNbNiNfiZv' changed from 29 in a_bug9748.o to 10 in a_bug9748.o
Comment #4 by pro.mathias.lang — 2014-10-16T19:22:56Z
Thank you Walter.
As mentioned in my email, solving this will require some change in the mangling scheme to support declarations in different nested scope.
In addition, if we allow this as valid code, we should relax the limitation exhibited by #4894 [1]. Would you agree to it ?
This needs support from the backend, as the mangling will change to take nested scope into account.
[1] https://issues.dlang.org/show_bug.cgi?id=4894
Possibly related bugs: https://issues.dlang.org/show_bug.cgi?id=10619
Comment #5 by k.hara.pg — 2015-07-26T13:19:59Z
*** Issue 9756 has been marked as a duplicate of this issue. ***
Comment #6 by k.hara.pg — 2015-07-27T13:16:34Z
*** This issue has been marked as a duplicate of issue 14831 ***