Bug 12571 – __traits(parent) should work for typed manifest constant in initializer
Status
RESOLVED
Resolution
FIXED
Severity
major
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-04-13T08:51:00Z
Last change time
2014-04-15T13:14:22Z
Keywords
ice, pull
Assigned to
nobody
Creator
andrej.mitrovich
Comments
Comment #0 by andrej.mitrovich — 2014-04-13T08:51:58Z
This may or may not be a bug. But there was a nice trick from the D Templates Book[1] which allowed a simple way to extract the symbol name of the current scope:
-----
mixin template getScopeName()
{
enum scopeName = __traits(identifier, __traits(parent, scopeName));
}
void main()
{
mixin getScopeName;
pragma(msg, scopeName);
}
-----
2.062:
$ dmd test.d
> main
2.063+:
> test.d(3): Error: circular reference to 'test.main.getScopeName!().scopeName'
> test.d(3): Error: argument __error has no parent
> test.d(3): Error: argument false has no identifier
> test.d(8): Error: mixin test.main.getScopeName!() error instantiating
If this is working as expected I suggest we introduce a new trait "scopeName". I'll file a separate report if this is closed.
I've seen enough people try to hack their way around with using .stringof, __FUNCTION__, and other tricks just to get the name of the current scope.
There is also a library workaround but it introduces symbol bloat:
-----
mixin template getScopeName()
{
private enum __unused = 0;
enum getScopeName = __traits(identifier, __traits(parent, __unused));
}
void main()
{
mixin getScopeName;
pragma(msg, getScopeName); // main
}
-----
[1] : https://github.com/PhilippeSigaud/D-templates-tutorial
Comment #1 by dlang-bugzilla — 2014-04-15T10:39:02Z
(In reply to Andrej Mitrovic from comment #0)
> -----
> mixin template getScopeName()
> {
> enum scopeName = __traits(identifier, __traits(parent, scopeName));
> }
>
> void main()
> {
> mixin getScopeName;
> pragma(msg, scopeName);
> }
> -----
I don't think the code should work. When declaring scopeName variable, referring it in its initializer should cause forward reference error.
> There is also a library workaround but it introduces symbol bloat:
[snip]
More simple another way exists.
mixin template getScopeName()
{
// enum scopeName = __traits(identifier, __traits(parent, scopeName));
enum scopeName = __traits(identifier, __traits(parent, getScopeName));
}
void main()
{
mixin getScopeName;
pragma(msg, scopeName);
}
Mixed-in template also have parent, so __traits(parent, getScopeName) will return the mixed in scope.
Comment #3 by andrej.mitrovich — 2014-04-15T10:49:58Z
(In reply to Kenji Hara from comment #2)
> (In reply to Andrej Mitrovic from comment #0)
> > -----
> > mixin template getScopeName()
> > {
> > enum scopeName = __traits(identifier, __traits(parent, scopeName));
> > }
> >
> > void main()
> > {
> > mixin getScopeName;
> > pragma(msg, scopeName);
> > }
> > -----
>
> I don't think the code should work. When declaring scopeName variable,
> referring it in its initializer should cause forward reference error.
>
> > There is also a library workaround but it introduces symbol bloat:
> [snip]
>
> More simple another way exists.
>
> mixin template getScopeName()
> {
> // enum scopeName = __traits(identifier, __traits(parent, scopeName));
> enum scopeName = __traits(identifier, __traits(parent, getScopeName));
> }
>
> void main()
> {
> mixin getScopeName;
> pragma(msg, scopeName);
> }
>
> Mixed-in template also have parent, so __traits(parent, getScopeName) will
> return the mixed in scope.
That doesn't work. It will return 'test' which is the module name rather than 'main'.
Comment #4 by andrej.mitrovich — 2014-04-15T10:51:58Z
(In reply to Kenji Hara from comment #2)
> I don't think the code should work. When declaring scopeName variable,
> referring it in its initializer should cause forward reference error.
Note that C seems to have a greater ability than D w.r.t. this issue. For example a piece of code I ran into recently:
-----
int** arr = malloc(sizeof(**arr) * 10);
-----
This works in C but can't in D. Whether that's a good thing or not, I don't know.
Comment #5 by k.hara.pg — 2014-04-15T11:33:52Z
(In reply to Andrej Mitrovic from comment #3)
> That doesn't work. It will return 'test' which is the module name rather
> than 'main'.
Oh, sorry. I didn't see the local execution result carefully.
----
I confirmed root problem. The opening code must not work. But, if the type of scopeName is explicitly specified, it should work.
mixin template getScopeName()
{
// NG, circular reference to scopeName
// enum scopeName = __traits(identifier, __traits(parent, scopeName));
// should be OK
enum string scopeName = __traits(identifier, __traits(parent, scopeName));
}
In original case, the type of scopeName is inferred from its initializer. Therefore on the evaluation of __traits(parent, scopeName), it will recursively invoke semantic analysis of the initialize, then it will cause forward reference.
By adding variable type, the declaration and definition of scopeName variable will be properly separated. Therefore on __traits(parent, scopeName), it should not cause recursive semantic analysis of the initializer. Finally it should work as expected.
----
But, currently the code will cause ICE. It is definitely a compiler bug. I'll open a pull request to fix it.
Comment #6 by andrej.mitrovich — 2014-04-15T11:36:31Z
(In reply to Kenji Hara from comment #5)
> By adding variable type, the declaration and definition of scopeName
> variable will be properly separated. Therefore on __traits(parent,
> scopeName), it should not cause recursive semantic analysis of the
> initializer. Finally it should work as expected.
Interesting. Thanks for examining this. I'll CC Philippe Sigaud so he's up-to-date on what needs fixing in the Templates book.