On template instance argument, a template arguments *sometimes* is treated just as a symbol.
That looks weird and inconsistent behavior.
----
template Id(alias A) { alias Id = A; } // L1
template ErrId(alias A) { static assert(0); }
template TypeTuple(TL...) { alias TypeTuple = TL; }
class C {
void fun(){}
void tfun(T)(){}
TypeTuple!(int, long) field; // L7
void test()
{
auto c = this;
alias t1a = Id!(c.fun); // OK
alias t1b = Id!(this.fun); // Weird error, bad
// test1.d(1): Error: variable test1.A type void is inferred
// from initializer this.fun(), and variables cannot be of type void
// -> internally given DotVarExp
alias t2a = Id!(c.tfun); // OK
alias t2b = ErrId!(this.tfun); // No error occurs, why?
// -> internally given DotTemplateExp
alias t3a = Id!(foo); // OK
alias t3b = Id!(mixin("foo")); // Prints weird error, bad
// test1.d(1): Error: variable test1.A type void is inferred
// from initializer foo(), and variables cannot be of type void
// -> internally given TemplateExp
alias t4b = TypeTuple!(field); // L28, NG
// test1.d(7): Error: expression this._field_field_0 is not a valid template value argument
// test1.d(7): Error: expression this._field_field_1 is not a valid template value argument
// test1.d(28): Error: template instance test1.TypeTuple!(this._field_field_0, this._field_field_1) error instantiating
alias t4a = TypeTuple!(GetField!0); // L32, NG
// test1.d(42): Error: alias test1.GetField!(0).GetField cannot alias an expression _field_field_0
// test1.d(42): Error: alias test1.GetField!(0).GetField cannot alias an expression _field_field_0
// test1.d(32): Error: template instance test1.GetField!(0) error instantiating
// test1.d(42): Error: alias test1.GetField!(0).GetField cannot alias an expression _field_field_0
// test1.d(32): Error: C.field[0u] is not a type
}
}
void foo()(){}
template GetField(size_t i) { alias GetField = C.field[i]; } // L42
void main(){ (new C()).test(); }
----
From the internal of dmd implementation, if failure to give just as a symbol, the argument has been semantically analysed as an expression which refers a symbol - DotVarExp, TemplateExp, and DotTemplteExp.
D does not support an aliasing of expression, so I think these *expression template arguments* should be deduced to just a symbol. In such deduction, the *context* expression for the symbol (e1, in DotVarExp or DotTemplateExp) should be just removed.
It will increase consistency and will relax some limitations.
Comment #1 by k.hara.pg — 2012-11-30T19:24:41Z
This is a root cause of some 2.061head regressions - bug 9091, bug 8972, and bug 8971.
If you use __traits() to get member symbol inside class/struct member function, the found member symbol will have the implicit 'this'.
Passing the __traits result to the template argument will caught the *bug* which I explained here.
class C {
int var;
void test() {
__traits(getMember, C, "var"); // same as this.var -> DotVarExp
alias t = Id!(__traits(getMember, C, "var")); // same as Id!(this.var), hit!
}
}
void main() {
__traits(getMember, C, "var") // same as C.var == Just a symbol
alias t = Id!(__traits(getMember, C, "var")); // same as Id!(C.var), no problem
}
Therefore, I think this is an important semantic analysis bug which should be fixed.
Comment #2 by k.hara.pg — 2012-11-30T19:36:04Z
As a related note, this program runs successfully with no error in current compiler.
It looks weird, but is expected from the view of language consistency.
void main(){ (new C(1)).test(); }
template Id(alias A) { alias Id = A; }
class C
{
int n;
this(int x){ n = x; }
int fun(){ return n; }
void test()
{
auto c = new C(2);
assert(c.fun() == 2);
alias f = Id!(c.fun);
assert(f() == 1); // calls this.fun, not c.fun
}
}
The following bugs that were already fixed are fundamentally dependent on this.
Issue 8971 - __traits(getOverloads) fails in a member function
Issue 8972 - __traits fails in a struct member function
Comment #5 by github-bugzilla — 2012-12-09T12:52:07Z
(In reply to comment #2)
> As a related note, this program runs successfully with no error in current
> compiler.
> It looks weird, but is expected from the view of language consistency.
>
> void main(){ (new C(1)).test(); }
> template Id(alias A) { alias Id = A; }
> class C
> {
> int n;
> this(int x){ n = x; }
> int fun(){ return n; }
>
> void test()
> {
> auto c = new C(2);
> assert(c.fun() == 2);
> alias f = Id!(c.fun);
> assert(f() == 1); // calls this.fun, not c.fun
> }
> }
Language _sanity_ is what is important. It should call c.fun or be a compile time error. Deliberately assigning unsupported code strange semantics in order to be consistent with existing buggy compiler behaviour just makes it harder to lift restrictions later.