Bug 11946 – need 'this' to access member when passing field to template parameter

Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-01-18T15:25:00Z
Last change time
2014-08-28T04:10:26Z
Keywords
rejects-valid
Assigned to
nobody
Creator
dlang-bugzilla

Comments

Comment #0 by dlang-bugzilla — 2014-01-18T15:25:45Z
//////////////////////////////////////// static // http://d.puremagic.com/issues/show_bug.cgi?id=7805 int f(A...)() { return 0; } struct S { int x; enum y = f!x(); } //////////////////////////////////////// This used to compile, but it was broken by https://github.com/D-Programming-Language/dmd/pull/2794 Also, issue 7805 was not fixed. It was marked as a duplicate of issue 11533, which the above pull request claims to fix. Whereas before, the "static" was required to avoid a compiler error, now it makes no difference as a compiler error occurs with and without "static".
Comment #1 by k.hara.pg — 2014-01-18T23:45:48Z
It's intended behavior introduced by issue 11533. Now, alias parameter is preferred than 'static' attribute _on template_. If you really need the syntax f!x(), you should define function template 'f' like follows: template f(alias X) { static string f() { //int x = X; return X.stringof; // OK } } struct S { int i; enum y = f!i(); pragma(msg, y); }
Comment #2 by dlang-bugzilla — 2014-01-19T07:20:28Z
It still seems like pointless breaking of code. If it really is intentional, then the diagnostic needs to be improved to tell the user how exactly to change their code. Also, there is still no sensible reason why "static" is required on a free function, regardless of whether it's in a template or not.
Comment #3 by k.hara.pg — 2014-01-20T03:41:05Z
(In reply to comment #2) > It still seems like pointless breaking of code. If it really is intentional, > then the diagnostic needs to be improved to tell the user how exactly to change > their code. > > Also, there is still no sensible reason why "static" is required on a free > function, regardless of whether it's in a template or not. OK, I reopen issue 7805 as an enhancement request. And fixing it will also fix this regression. https://github.com/D-Programming-Language/dmd/pull/3126
Comment #4 by dlang-bugzilla — 2014-01-20T05:50:53Z
> OK, I reopen issue 7805 as an enhancement request. Unless the enhancement is making the error message better, it is still a regression and not an enhancement, is it not? From my observations of related errors, I think there seems to be some conflation between aliases to declarations (without context pointers) and aliases to actual variables (which do have context pointers). For example, S.field and s.field. These bug reports and those where the compiler complains that "this for X should be type Y not type Z" indicates that in a lot of cases, the compiler attempts to pass the context pointer to blocks where it makes no sense to. IMO, all "this for X should be type Y not type Z" errors are confusing and useless, and need to be eliminated.
Comment #5 by bugzilla — 2014-01-21T00:25:04Z
(In reply to comment #0) > //////////////////////////////////////// > static // http://d.puremagic.com/issues/show_bug.cgi?id=7805 > int f(A...)() { return 0; } > > struct S { int x; enum y = f!x(); } > //////////////////////////////////////// > > This used to compile, but it was broken by > https://github.com/D-Programming-Language/dmd/pull/2794 > > Also, issue 7805 was not fixed. It was marked as a duplicate of issue 11533, > which the above pull request claims to fix. Whereas before, the "static" was > required to avoid a compiler error, now it makes no difference as a compiler > error occurs with and without "static". This should never have compiled. static should never have affected a template in global scope.
Comment #6 by k.hara.pg — 2014-01-22T21:52:01Z
I change my argue to that this regression should be marked as invalid. (In reply to comment #5) > (In reply to comment #0) > > //////////////////////////////////////// > > static // http://d.puremagic.com/issues/show_bug.cgi?id=7805 > > int f(A...)() { return 0; } > > > > struct S { int x; enum y = f!x(); } > > //////////////////////////////////////// > > > This should never have compiled. static should never have affected a template > in global scope. I also think so. But it had been worked because 'static' attribute is always inherited to the instantiated function. I *fixed* the bug in the PR: > > This used to compile, but it was broken by > > https://github.com/D-Programming-Language/dmd/pull/2794 === The main point of enhancment 11533 is that 'static template' should always behave as same as module-level templates, regardless its declared position. It's increasing consistency with currently known language concepts: - 'static' attribute on module level declaration is just redundant (meaningless). - 'static' declaration inside aggregates/functions should behave as same as module level declarations. * static member function is equivalent with free functions. * static local function is equivalent with free functions. By the fix, `static int f(A...)(){ ... }` and `int f(A...)(){ ... }` have now no difference at module level. Therefore, instantiated f!(S.x) always requires hidden 'this' pointer on the call. To be more precise, current D language alias template parameter is not designed just to take a symbol that is needed just only at compile-time. If given template argument has implicit runtime context, compiler will _always_ try to take runtime context at the same time. (To resolve the issue, "nested-ness interence" is sometimes proposed. But I'm still not sure it is possible feature.) To ignore the implicit automatic capturing, you need to write the idiom I explained in comment#1. As a conclusion: the reported breaking change is necessary due to add more consistency to the language spec.
Comment #7 by dlang-bugzilla — 2014-01-22T22:51:03Z
> It's increasing consistency with currently known language concepts: Yes, this is all fine and well, but it does not justify making code that worked before now break with an indecipherable error message! The message "need 'this' to access member f" is an outright lie and is only good at showing a glaring compiler implementation problem. (In reply to comment #1) > template f(alias X) > { > static string f() > { > //int x = X; > return X.stringof; // OK > } > } This just shows another bug - that you still need "static" (which makes NO sense for a free function, templated or not), but now you must hide it inside a template! > To be more precise, current D language alias template parameter is not designed > just to take a symbol that is needed just only at compile-time. > If given template argument has implicit runtime context, compiler will _always_ > try to take runtime context at the same time. So fix this first before breaking code? > To ignore the implicit automatic capturing, you need to write the idiom I > explained in comment#1. If one needs to go to a bugtracker and learn of a hacky workaround (put a static function in a template) from a compiler developer to make their code compile, this is a terrible situation for D. > As a conclusion: the reported breaking change is necessary due to add more > consistency to the language spec. I suggest that consistency bugs are fixed only after there is an immediate and obvious way to transition code that should not have compiled, to correct code which functions in the same way. In either case, this bug remains a REGRESSION.
Comment #8 by k.hara.pg — 2014-01-22T23:41:23Z
(In reply to comment #7) > > It's increasing consistency with currently known language concepts: > > Yes, this is all fine and well, but it does not justify making code that worked > before now break with an indecipherable error message! The message "need 'this' > to access member f" is an outright lie and is only good at showing a glaring > compiler implementation problem. There's no lie. The instantiated function _always_ needs valid runtime context, *regardless* the context is really used or not inside function body. Unfortunately current D has _no_ language feature to remove unused context pointer (I call it "nested-ness inference"). > (In reply to comment #1) > > template f(alias X) > > { > > static string f() > > { > > //int x = X; > > return X.stringof; // OK > > } > > } > > This just shows another bug - that you still need "static" (which makes NO > sense for a free function, templated or not), but now you must hide it inside a > template! It's not a bug. If the symbol X needs runtime context ('this' object or function frame), f() will be instantiated like as member function or nested function. To remove the implicit context pointer, 'static' will work. --- After fixing issue 11533, these two declarations have different meanings. template f(alias X) { static string f() { return null; } } // A static template f(alias X) { string f() { return null; } } // B In A, 'static' attribute is always added to the instantiated function 'f'. So Even if X requires runtime context, 'f' still cannot access it. In B, 'static' attribute is added _if_ X has no runtime context. If X needs runtime context, instantiated 'f' also take the context to access valid runtime storage of X. --- > > To be more precise, current D language alias template parameter is not designed > > just to take a symbol that is needed just only at compile-time. > > If given template argument has implicit runtime context, compiler will _always_ > > try to take runtime context at the same time. > > So fix this first before breaking code? It would be a not trivial language enhancement that still not yet designed. > > To ignore the implicit automatic capturing, you need to write the idiom I > > explained in comment#1. > > If one needs to go to a bugtracker and learn of a hacky workaround (put a > static function in a template) from a compiler developer to make their code > compile, this is a terrible situation for D. To explain the behavior, I'll update webside documentation and add a section in release note. > > As a conclusion: the reported breaking change is necessary due to add more > > consistency to the language spec. > > I suggest that consistency bugs are fixed only after there is an immediate and > obvious way to transition code that should not have compiled, to correct code > which functions in the same way. I already explained the way to do it in comment#1.
Comment #9 by dlang-bugzilla — 2014-01-22T23:53:40Z
(In reply to comment #8) > (In reply to comment #7) > > > It's increasing consistency with currently known language concepts: > > > > Yes, this is all fine and well, but it does not justify making code that worked > > before now break with an indecipherable error message! The message "need 'this' > > to access member f" is an outright lie and is only good at showing a glaring > > compiler implementation problem. > > There's no lie. The instantiated function _always_ needs valid runtime context, > *regardless* the context is really used or not inside function body. What?!?!? This makes absolutely no sense! There is nothing in the D spec about this! Templated functions must behave in the same way as free functions (whether the function itself is templated or it is inside an explicit template declaration), and "static" is meaningless on free functions! We are not talking about template mixins! The instantiation of non-mixin templates uses the template declaration's context! > Unfortunately current D has _no_ language feature to remove unused context > pointer (I call it "nested-ness inference"). As far as I can see, this is just a compiler implementation detail! Can you provide an example where this "feature" (passing context pointer from template instantiation site to a free function) is actually used? > It's not a bug. If the symbol X needs runtime context ('this' object or > function frame), It doesn't! .stringof does not need a gosh-darn context pointer! > f() will be instantiated like as member function or nested > function. WHY?!? It is a free function! > To remove the implicit context pointer, 'static' will work. ... > After fixing issue 11533, these two declarations have different meanings. > > template f(alias X) { static string f() { return null; } } // A > static template f(alias X) { string f() { return null; } } // B > > In A, 'static' attribute is always added to the instantiated function 'f'. So > Even if X requires runtime context, 'f' still cannot access it. > In B, 'static' attribute is added _if_ X has no runtime context. If X needs > runtime context, instantiated 'f' also take the context to access valid runtime > storage of X. This looks like absurd overcomplication. Do you really expect D users to have to understand the difference? > It would be a not trivial language enhancement that still not yet designed. Then I suggest that you postpone all breaking changes until all that is figured out. Breaking code to clean up compiler internals is inexcusable, IMO! > To explain the behavior, I'll update webside documentation and add a section in > release note. The more I think about it the less sense it makes to me. Maybe it makes sense from the point of view of a compiler developer (to use Chen Raymond's phraseology, you're looking at the world through "compiler-tinted glasses"). But as a D user, your explanation reads like one giant WTF to me. It is completely unintuitive and to me it seems that things are broken at a much more fundamental level if you defend the current logic in this way.
Comment #10 by k.hara.pg — 2014-01-23T01:29:55Z
(In reply to comment #9) > (In reply to comment #8) > > There's no lie. The instantiated function _always_ needs valid runtime context, > > *regardless* the context is really used or not inside function body. > > What?!?!? > > This makes absolutely no sense! > > There is nothing in the D spec about this! Templated functions must behave in > the same way as free functions (whether the function itself is templated or it > is inside an explicit template declaration), and "static" is meaningless on > free functions! > > We are not talking about template mixins! The instantiation of non-mixin > templates uses the template declaration's context! Yes, I'm talking about normal templates. Even if the template is defined in module level, if it has alias parameter, instantiated function may have implicit context. Indeed it is part of the implementation detail, but today it is mostly unavoidable issue. If you interest, you can read issue 5710 discussion. > > Unfortunately current D has _no_ language feature to remove unused context > > pointer (I call it "nested-ness inference"). > > As far as I can see, this is just a compiler implementation detail! > > Can you provide an example where this "feature" (passing context pointer from > template instantiation site to a free function) is actually used? // This is module level template function that have alias parameter. int foo(alias sym)() { return sym; } class C { int n; void test() { this.n = 10; alias fooN = foo!(C.n); // fooN has implicit context to access field variable n in class C. static assert(is(typeof(&fooN) == delegate)); assert(fooN() == 10); } } void main() { int n = 10; alias fooN = foo!n; // fooN has implicit context to access local variable n in main. static assert(is(typeof(&fooN) == delegate)); assert(fooN() == 10); new C().test(); } foo(alias sym) is module level template, but in both case instantiated functions will have implicit context pointer to access runtime data of 'sym'. > > It's not a bug. If the symbol X needs runtime context ('this' object or > > function frame), > > It doesn't! .stringof does not need a gosh-darn context pointer! Again, current compiler does not consider that the context is not really used inside function. > > f() will be instantiated like as member function or nested > > function. > > WHY?!? > > It is a free function! No. It's a template function that is defined in module level. > > To remove the implicit context pointer, 'static' will work. > > ... > > > After fixing issue 11533, these two declarations have different meanings. > > > > template f(alias X) { static string f() { return null; } } // A > > static template f(alias X) { string f() { return null; } } // B > > > > In A, 'static' attribute is always added to the instantiated function 'f'. So > > Even if X requires runtime context, 'f' still cannot access it. > > In B, 'static' attribute is added _if_ X has no runtime context. If X needs > > runtime context, instantiated 'f' also take the context to access valid runtime > > storage of X. > > This looks like absurd overcomplication. Do you really expect D users to have > to understand the difference? In many cases, learning it would not be necessary. But in your case the difference is necessary. Honestly, the alias parameter usage in your code does not fit the expected usage in language design. At least from D1 age, template alias parameter is designed to use the given symbol in runtime at instantiated code. > > It would be a not trivial language enhancement that still not yet designed. > > Then I suggest that you postpone all breaking changes until all that is figured > out. Breaking code to clean up compiler internals is inexcusable, IMO! > > > To explain the behavior, I'll update webside documentation and add a section in > > release note. > > The more I think about it the less sense it makes to me. Maybe it makes sense > from the point of view of a compiler developer (to use Chen Raymond's > phraseology, you're looking at the world through "compiler-tinted glasses"). > > But as a D user, your explanation reads like one giant WTF to me. It is > completely unintuitive and to me it seems that things are broken at a much more > fundamental level if you defend the current logic in this way. I agree that it is hard to understand behavior. That's why I had tried to fix the issue at first.
Comment #11 by k.hara.pg — 2014-01-23T07:50:17Z
(In reply to comment #8) > To explain the behavior, I'll update webside documentation and add a section in > release note. I opened a document improvement for the existing "Nested Templates" feature. https://github.com/D-Programming-Language/dlang.org/pull/479
Comment #12 by dlang-bugzilla — 2014-01-23T10:24:12Z
(In reply to comment #10) > // This is module level template function that have alias parameter. > int foo(alias sym)() { return sym; } > > class C > { > int n; > void test() > { > this.n = 10; > alias fooN = foo!(C.n); > // fooN has implicit context to access field variable n in class C. > static assert(is(typeof(&fooN) == delegate)); > assert(fooN() == 10); > } > } Thank you, I finally understand what's going on. But the situation looks really bad. What if you want to pass the "n" variable from both the struct and the function, to the same template? Or if the template already has a context pointer (it is a templated method)? The context pointers should be tied to the variables, not the same "this" pointer that is used in calling methods. Doing it that way just produces absurd error messages. In either case, at the very least the error message needs to be improved. The simplest solution would be to just change the wording for such cases to make it Google-able. But of course ideally the problem should be solved at its root. > Again, current compiler does not consider that the context is not really used > inside function. Well, this is another bug then. > > It is a free function! > > No. It's a template function that is defined in module level. I do not believe that the distinction needs to be forced upon the users. > In many cases, learning it would not be necessary. But in your case the > difference is necessary. > Honestly, the alias parameter usage in your code does not fit the expected > usage in language design. > At least from D1 age, template alias parameter is designed to use the given > symbol in runtime at instantiated code. I just tested another bit of code: struct O { int f(A...)() { return 0; } struct S { int x; void g() { auto y = f!x(); } } } With this one, we get: test3.d(11,13): Error: template instance f!(x) cannot use local 'x' as parameter to non-global template f(A...)() test3.d(11,16): Error: this for f needs to be type O not type S IMHO the correct solution would be to pass the context pointers of alias parameters separately, so as to not interfere with any existing context pointers of the template.
Comment #13 by dlang-bugzilla — 2014-01-23T18:48:25Z
After some thinking: "static" must not affect whether alias parameters receive context pointers. Currently, there can only be one context pointer per function, but this limitation will hopefully be lifted in the future. When that happens, there will be no way to decide whether each alias parameter carries along a context pointer or not. Therefore, "static" must only untie the function from its immediate surrounding declaration context (struct, class or function frame). As I understand, currently, if a declaration CAN have a context pointer, the compiler seems to demand it at all times when passing it as an alias parameter. Instead, it should pass a context pointer if and only if if it is actually available at the scope of the template instantiation. For example, it will be available when calling a templated function from a non-static method, but it will not be available when declaring an enum, or calling from a static method. Later, if the template function's code attempts to do anything with the alias parameter which ACTUALLY requires its context, for example reading/writing it or taking its address, then it should raise an error. So, the context pointer check needs to stop being eager and be made as late as possible. Additionally, if you want to explicitly choose whether you want to pass an alias parameter WITH or WITHOUT a context pointer, I think the language already has the necessary syntax, and for class/struct fields it is quite intuitive: C.x (no context pointer) vs. c.x (c is the context pointer), or this.x vs. typeof(this).x etc. It is more complicated with local variables, but I think __traits(getMember, __traits(parent, n), "n") would work. However, I believe this should not be necessary at all, since the compiler should not demand a context pointer without even bothering to check if the template actually needs it.
Comment #14 by k.hara.pg — 2014-01-23T19:51:25Z
(In reply to comment #13) > After some thinking: > > "static" must not affect whether alias parameters receive context pointers. [snip] > "static" must only untie the function from its > immediate surrounding declaration context (struct, class or function frame). This is just same with the behavior that was implemented by issue 11533. In git-head, static attribute on template does only unite instantiated functions and aggregate types from its immediate surrounding declaration context. void main() { int x; // Adding 'static' on local template foo unlinks instantied function // from the enclosing main() function context. static void foo(alias fun)() { // x = 10; // NG fun(); } static void bar() {} foo!bar(); pragma(msg, is(typeof(&foo!bar) == delegate)); // prints false // And nested-ness of the instantiated function only depends on // the passed argument on the alias parameter void nested() {} pragma(msg, is(typeof(&foo!nested) == delegate)); // prints true } So in the OP code: static int f(A...)() { return 0; } struct S { int x; enum y = f!x(); } f!x is now tied to the runtime object S, and calling it on context-less scope is rejected.
Comment #15 by k.hara.pg — 2014-01-23T19:52:56Z
(In reply to comment #14) > static attribute on template does only unite instantiated functions ... static attribute on template does only >>untied<< instantiated functions ...
Comment #16 by dlang-bugzilla — 2014-01-23T19:54:06Z
OK... but then why does your suggestion in comment#1 work? If it is as you say, then "static" should have no effect on free functions, regardless if they are in a template or not, right?
Comment #17 by k.hara.pg — 2014-01-23T20:16:30Z
(In reply to comment #16) > OK... but then why does your suggestion in comment#1 work? If it is as you say, > then "static" should have no effect on free functions, regardless if they are > in a template or not, right? Again, 'f' is not a free function, it is a function template that declared in module level. Therefore, instantiated function f!x will become nested if inner static attribute does not exist. // static int f(A...)() { return 0; } is equivalent with static template f(A...) { int f() { return 0; } } struct S { int x; enum y = f!x // f!x is equivalent with member function of S. } On the other hand: template f(A...) { static int f() { return 0; } } struct S { int x; enum y = f!x // f!x is equivalent with 'static' member function of S. }
Comment #18 by dlang-bugzilla — 2014-01-23T21:34:14Z
OK, but when I said: > "static" must not affect whether alias parameters receive context pointers. [snip] > "static" must only untie the function from its > immediate surrounding declaration context (struct, class or function frame). I specifically meant this case. It should not matter whether the function is in a template or not, if it is not declared in a struct/class/function, then "static" should have no effect (or even be forbidden, as the compiler now forbids many senseless attributes). With your change, "static" is still used as a mechanism to tell the compiler whether the function needs the context at the INSTANTIATION site, which should not be necessary, as I said in comment#13 the compiler should instantiate/evaluate the function regardless of whether a context pointer is needed, and if the function's code actually needs the context pointer then the compiler will complain at that point. But to get back on topic, you said that you're not sure if this problem will ever be solved. But there have been a few unanswered suggestions here and in the other discussion you mentioned, do you think they are all unfeasible?
Comment #19 by k.hara.pg — 2014-01-23T22:47:53Z
(In reply to comment #18) > OK, but when I said: > > > "static" must not affect whether alias parameters receive context pointers. > [snip] > > "static" must only untie the function from its > > immediate surrounding declaration context (struct, class or function frame). > > I specifically meant this case. It should not matter whether the function is in > a template or not, if it is not declared in a struct/class/function, then > "static" should have no effect (or even be forbidden, as the compiler now > forbids many senseless attributes). Maybe you're misunderstanding the effect of 'static' attribute. As far as I know, it means that: - If 'static' attribute is specified to a nested declaration, it will make the nested declaration 'unnested'. In above description, of course 'nested declaration' contains the instantiated functions that implicitly made nested, like f!x. That's why static attribute on module level declaration is meaningless - module level declaration is always non-nested, so 'unnesting' has no effect, and adding static attribute to the instantiated nested function is meaningful. > With your change, "static" is still used as > a mechanism to tell the compiler whether the function needs the context at the > INSTANTIATION site As I explained above, It is expected behavior of 'static' attribute. > which should not be necessary, as I said in comment#13 [snip] Even if it will become possible in the future, but currently it isn't. I have no opinion about non existing feature.
Comment #20 by dlang-bugzilla — 2014-01-23T22:55:12Z
(In reply to comment #19) > Maybe you're misunderstanding the effect of 'static' attribute. > As far as I know, it means that: I think I understand how it works now. But I don't think this is how it SHOULD work, because it is not intuitive. No other programming language does this, and no one expects it to act like this. So I think it is a limitation imposed by implementation detail that needs to be fixed. > Even if it will become possible in the future, but currently it isn't. > I have no opinion about non existing feature. I hope it will be fixed, I think this is an important issue. Maybe Facebook will place a nice bounty in the next round :)
Comment #21 by k.hara.pg — 2014-01-23T23:33:10Z
(In reply to comment #20) > (In reply to comment #19) > > Maybe you're misunderstanding the effect of 'static' attribute. > > As far as I know, it means that: > > I think I understand how it works now. But I don't think this is how it SHOULD > work, because it is not intuitive. No other programming language does this, and > no one expects it to act like this. So I think it is a limitation imposed by > implementation detail that needs to be fixed. I think it is enough reasonable definition because: 1. Currently dmd works that way. 2. It is consistent definition and allow such the code in comment#1. Anyway, can you agree that the OP code needs fixup with 2.065 and this issue should me marked as INVALID?
Comment #22 by dlang-bugzilla — 2014-01-23T23:40:21Z
No, I do not agree that this bug is INVALID. It is still a regression, and I maintain that the current behavior and your suggested workaround are illogical. > 1. Currently dmd works that way. This is not a good argument - if it works in a wrong way, it needs to be fixed. > 2. It is consistent definition and allow such the code in comment#1. It shouldn't be required to either use "static" or put the function in a template block. Anyway, I understand if this is a very complicated problem that is not worth stopping the release for, so I would agree with a WONTFIX (or REMIND/LATER?) resolution. But I think it would be good to hear someone else's opinion in this discussion first.
Comment #23 by bugzilla — 2014-01-26T12:31:01Z
> This just shows another bug - that you still need "static" (which makes NO sense for a free function, templated or not), but now you must hide it inside a template! I agree.
Comment #24 by bugzilla — 2014-01-26T12:46:49Z
Some thoughts: 1. 'static' in D has come to mean "a context pointer is not supplied". For example, in C: void foo() { static int x; } x can be accessed without the "context pointer", in this case the stack pointer. Static doesn't mean it is unnested or global. 2. Alias template arguments are never "passed" to a template in the way that arguments are passed to functions. They simply make the symbol in scope in the template body. 3. Static as an attribute makes no sense in a context where there is no context pointer. I.e. declarations at global level, declarations inside global templates, etc. 4. A use of a symbol, where that use does not require a context pointer, should not issue an error if a context pointer is not supplied. 5. I don't think we're going to resolve this easily without a lot of careful thought.
Comment #25 by bugzilla — 2014-01-26T12:51:42Z
I suspect this should work: int f(alias A)() { return 0; } struct S { int x; enum y = f!x(); } whereas this should not: int f(alias A)() { return A; } struct S { int x; enum y = f!x(); } i.e. this check: foo3.d(1): Error: function foo3.S.f!(x).f need 'this' to access member f should not be done for arguments to template alias parameters.
Comment #26 by k.hara.pg — 2014-01-29T18:23:31Z
(In reply to comment #24) > Some thoughts: > > 1. 'static' in D has come to mean "a context pointer is not supplied". For > example, in C: > > void foo() { > static int x; > } > > x can be accessed without the "context pointer", in this case the stack > pointer. Static doesn't mean it is unnested or global. I used the word 'unnested' to represent the equivalent meaning with "a context pointer is not supplied". So basically I agree with this. > 2. Alias template arguments are never "passed" to a template in the way that > arguments are passed to functions. They simply make the symbol in scope in the > template body. About the merely visibility of the passed alias parameter, it is correct. But, instantiated function f!(x).f should have a context to access context-full symbol 'x'. _Currently_ this is not avoidable. > 3. Static as an attribute makes no sense in a context where there is no context > pointer. I.e. declarations at global level, declarations inside global > templates, etc. Yes. > 4. A use of a symbol, where that use does not require a context pointer, should > not issue an error if a context pointer is not supplied. Essentially yes. But current compiler does not support the principle, because of the lack of language feature. > 5. I don't think we're going to resolve this easily without a lot of careful > thought. Definitely yes.
Comment #27 by k.hara.pg — 2014-01-29T18:28:07Z
(In reply to comment #25) > I suspect this should work: > > int f(alias A)() { return 0; } > struct S { int x; enum y = f!x(); } > > whereas this should not: > > int f(alias A)() { return A; } > struct S { int x; enum y = f!x(); } > > i.e. this check: > > foo3.d(1): Error: function foo3.S.f!(x).f need 'this' to access member f > > should not be done for arguments to template alias parameters. Currently this is necessary. As a technical talk, in current semantic analysis system, the "context-ness" of a function - whether a function really needs a context or not - should be determined without its body analysis. As far as I see, current dmd code relies on the assumption. To relax the limitation, we should infer the context-ness as same as attribute inference, but it should be considered as a new language enhancement but it is not definitely so small.
Comment #28 by k.hara.pg — 2014-01-29T18:44:24Z
On the other hand, the purpose of issue 11533 is simple and consistent. If a module level declaration is moved into function/aggreage scope with additional 'static' attribute, it should have exactly same behavior. Examples: module a; int var; void foo(); class C {} void bar(T)(T) {} struct S(alias pred) { void f() {} } void baz(alias v)(T) { v = 1; } class Test { static int var; static void foo(); static class C {} static void bar(T)(T) {} static struct S(alias pred) { void f() {} } // New! static void baz(alias v)(T) { v = 1; } // New! } void test() { static int var; static void foo(); static class C {} static void bar(T)(T) {} static struct S(alias pred) { void f() {} } // New! static void baz(alias v)(T) { v = 1; } // New! } The case of 'S' and 'baz' are newly supported by fixing 11533. - S(alias pred) will become context-free if 'pred' is context-free. - baz(alias v) will become context-free if 'v' is context-free. Indeed, to support baz case current 'static' attribute semantics is made a little complex, but the introduced behavior is simple, consistent, and easy understandable. I think fixiing 11533 is a huge win for the D code locality.
Comment #29 by public — 2014-03-03T07:42:12Z
I agree with Vladimir here. This is basically replacing one hack with another so that both code will break and no desired solution will be introduced. Such change can't be justified and this supposed win is imaginary. About anything that legitimates statement "templated free function is not a free function" is a huge "NO!" in my opinion.
Comment #30 by dlang-bugzilla — 2014-03-03T07:56:27Z
The irony is that as much as I dislike this approach, it does allow doing more things with template alias parameters, and I've started to rely on it in my own code. I even took it a step further in https://github.com/D-Programming-Language/dmd/pull/3345 to do the same things with methods. I noticed Kenji wrote this on the D Wiki: http://wiki.dlang.org/Brush_Up_Language_Features#Nested_Symbols The idea is to introduce "static alias" template parameters, which make it explicit if the alias parameter needs to transmit the symbol's context to the template (and thus nest the template within that context), or not. I think it is a sensible approach in lieu of automagically determining if that context is needed by the template (which Kenji claims is "mostly impossible").
Comment #31 by public — 2014-03-03T08:08:42Z
(In reply to comment #30) > The irony is that as much as I dislike this approach, it does allow doing more > things with template alias parameters, and I've started to rely on it in my own > code. I even took it a step further in It does not really matter. What is the point of adding more features is they break basic guarantees? It does not fit nicely with idea that templates are not affected by instantiation scope and forces you to pollute your code with loads of boilerplate just to be sure. I am effectively forced to go though my code upon next release and replace all void foo(alias X)(...) {} with template foo(alias X) { static void foo(...) {] } _everywhere_. How I am even supposed to not hate it? > The idea is to introduce "static alias" template parameters, which make it > explicit if the alias parameter needs to transmit the symbol's context to the > template (and thus nest the template within that context), or not. I think it > is a sensible approach in lieu of automagically determining if that context is > needed by the template (which Kenji claims is "mostly impossible"). It should be other way around, "dynamic alias" for those who do want a context pointer. "Don't pay for what you don't use" and principle of least surprise.
Comment #32 by dlang-bugzilla — 2014-03-03T08:12:41Z
(In reply to comment #31) > It does not really matter. What is the point of adding more features is they > break basic guarantees? It does not fit nicely with idea that templates are not > affected by instantiation scope and forces you to pollute your code with loads > of boilerplate just to be sure. I certainly won't complain if this change is reverted and replaced with a suitable alternative, even if it means I'll have to rewrite all the code that depends on it. > It should be other way around, "dynamic alias" for those who do want a context > pointer. "Don't pay for what you don't use" and principle of least surprise. Fair enough, though we don't have a "dynamic" keyword we can abuse here...
Comment #33 by k.hara.pg — 2014-03-03T08:16:49Z
(In reply to comment #30) > I noticed Kenji wrote this on the D Wiki: > http://wiki.dlang.org/Brush_Up_Language_Features#Nested_Symbols > > The idea is to introduce "static alias" template parameters, which make it > explicit if the alias parameter needs to transmit the symbol's context to the > template (and thus nest the template within that context), or not. I think it > is a sensible approach in lieu of automagically determining if that context is > needed by the template (which Kenji claims is "mostly impossible"). In my thought, excepting lambdas, the context-inference feature is not implementable, because it will make the mangling names of the function local symbols unstable. (On lambdas, they have unique names in their declared scope, so we can stop encoding their 'this' pointer into the mangled name.) So if we really need to capture any symbols without their contexts, the idea "static alias parameter" would be the most better solution.
Comment #34 by dlang-bugzilla — 2014-03-03T08:18:37Z
(In reply to comment #33) > because it will make the mangling names of the function local symbols unstable. Could you please explain this further? Isn't name mangling only done once all semantics are resolved, so it will happen once the compiler knows if the template needs the context or not?
Comment #35 by k.hara.pg — 2014-03-03T08:26:28Z
(In reply to comment #34) > (In reply to comment #33) > > because it will make the mangling names of the function local symbols unstable. > > Could you please explain this further? Isn't name mangling only done once all > semantics are resolved, so it will happen once the compiler knows if the > template needs the context or not? Read the section "Do not mangle context-ness of parent lambdas" in http://wiki.dlang.org/Brush_Up_Language_Features
Comment #36 by dlang-bugzilla — 2014-03-03T08:50:14Z
(In reply to comment #35) > Read the section "Do not mangle context-ness of parent lambdas" in > http://wiki.dlang.org/Brush_Up_Language_Features Thanks. Sorry I missed that. So do you think that if we solve the lambda mangling problem, it would be possible to infer static-ness of alias parameters, thus avoiding the need of "static alias"?
Comment #37 by k.hara.pg — 2014-03-03T22:45:48Z
(In reply to comment #36) > (In reply to comment #35) > So do you think that if we solve the lambda mangling problem, it would be > possible to infer static-ness of alias parameters, thus avoiding the need of > "static alias"? My opinion is that would be impossible to infer static-ness in generic case. We can avoid the issue about lambdas by adding a hack for the mangling scheme, but the hack won't work for non-lambda functions.
Comment #38 by dlang-bugzilla — 2014-03-04T00:27:47Z
Well, IMHO mangleof should just result in a forward reference error in such cases. As far as I can see, static inference is much more important than mangleof of nested functions inside those functions.
Comment #39 by doob — 2014-06-28T09:54:02Z
I'm not sure if this is related but I have a similar case as Vladimir's first post: module foo; static int f(A...)() { pragma(msg, typeof(A)); return 0; } module bar; import foo; struct S { private int x; enum y = f!x(); } Here "x" is private and I'm accessing the tuple in "f". The error I get is " Error: struct bar.S member x is not accessible". This worked fine in DMD 2.064.2.
Comment #40 by bugzilla — 2014-07-07T05:46:20Z
(In reply to Jacob Carlborg from comment #39) > Here "x" is private and I'm accessing the tuple in "f". The error I get is " > Error: struct bar.S member x is not accessible". This worked fine in DMD > 2.064.2. Yes, it's not accessible because it is marked private, and "f" does not have access to private members. It also has nothing to do with Issue 11946, and should be filed separately if you feel it needs to be addressed further.
Comment #41 by bugzilla — 2014-07-07T06:12:00Z
(In reply to Kenji Hara from comment #27) > As a technical talk, in current semantic analysis system, the "context-ness" > of a function - whether a function really needs a context or not - should be > determined without its body analysis. As far as I see, current dmd code > relies on the assumption. But D already has the notion of doing a "local instantiation" of a global template, if an alias parameter is a local symbol - see hasNestedArgs().
Comment #42 by doob — 2014-07-07T06:24:20Z
(In reply to Walter Bright from comment #40) > It also has nothing to do with Issue 11946, and should be filed separately > if you feel it needs to be addressed further. The reason I'm asking is because if I remove "private" I get the same error as reported in this issue. And the workaround in Kenji's first post [1] fixes the problem. [1] https://issues.dlang.org/show_bug.cgi?id=11946#c1
Comment #43 by k.hara.pg — 2014-07-09T05:43:33Z
(In reply to Walter Bright from comment #41) > But D already has the notion of doing a "local instantiation" of a global > template, if an alias parameter is a local symbol - see hasNestedArgs(). Right, but if an instantiation is determined to "local instantiation", compiler cannot "unlocalize" it automatically. --- To me current behavior is solid designed and more useful than old one. In D, "static" keyword is used to unlocalize local symbols. 1. In class/struct member scope, variable declarations and function declarations are implicitly localized in the instance object. By emitting static, they will be unlocalized. 2. In function statement scope, variable declarations and function declarations are implicitly localized in the function execution context. By emitting static, they will be unlocalized. 3. In module scope, every declared symbols have no implicit context, so static keyword has no meaning for them. But, with templates, the localization is checked twice - on template declaration itself and the instantiated code. 1-a. In class/struct member scope, function template is localized by default. So class C { int a; void foo()() { a = 1; } } void main() { new C().foo!()(); } the instantiated function C.foo!()() will work as member function. 1-b. If function template has static attribute, the template declaration it is explicitly unlocalized. So class C { int a; static void bar()() {} } void main() { C.bar!()(); } the instantiated function C.bar!()() will work as static member function. 1-c. If function template has static attribute and alias parameter, the template declaration it is explicitly unlocalized, *but* the instantiated function can be localized depending on the argument of alias parameter. So class C { int a; static void baz(alias x)() { x = 10; } } void main() { int b; C.baz!(b)(); } the instantiated function C.baz!(b)() can be localized in other local scope. 1-d. and then: class C { int a; static template goo(alias x) { >>static<< void goo() { static assert(!__traits(compiles, x = 10)); } } void main() { int b; C.baz!(b)(); } the static attribute on the instantiated function will unlocalize it even if the template is instantiated with local symbol. Completely same rule is applied for No.2 and No.3 cases. In earier version, the semantics 'c' was not possible, because the syntax `static void baz(alias x)() { ... }` had worked as like 'd'. The new 'c' behavior is useful in particular case. For example: auto map(alias pred, R)(R r) { static struct Result(alias pred) { R r; auto front() { return pred(r.front); } } return Result!pred(r); } If pred does not have a context, the Result!pred is unlocalized by default. Otherwise, Result!pred will be automatically localized.
Comment #44 by timon.gehr — 2014-07-15T08:31:31Z
(In reply to Kenji Hara from comment #21) > (In reply to comment #20) > > (In reply to comment #19) > > > Maybe you're misunderstanding the effect of 'static' attribute. > > > As far as I know, it means that: > > > > I think I understand how it works now. But I don't think this is how it SHOULD > > work, because it is not intuitive. No other programming language does this, and > > no one expects it to act like this. So I think it is a limitation imposed by > > implementation detail that needs to be fixed. > > I think it is enough reasonable definition because: > 1. Currently dmd works that way. That's basically diametrally opposed to how such things should be argued. > 2. It is consistent definition and allow such the code in comment#1. > ... No, it is not a "consistent" definition, see below. Also, the code in comment#1 is ugly. > Anyway, can you agree that the OP code needs fixup with 2.065 and this issue > should me marked as INVALID? I don't think this issue is invalid. (In reply to Kenji Hara from comment #43) > (In reply to Walter Bright from comment #41) > > But D already has the notion of doing a "local instantiation" of a global > > template, if an alias parameter is a local symbol - see hasNestedArgs(). > > Right, but if an instantiation is determined to "local instantiation", > compiler cannot "unlocalize" it automatically. > > --- > To me current behavior is solid designed and more useful than old one. > ... I'm sorry, but the current behaviour is an ugly patchwork. Even if there is value in explicit 'unlocalization' (which I am not convinced of), introducing/moving around subtle and completely ad-hoc distinctions barring any sound motivation like that is not going to fly. For one thing, what if I want to make some members of my template scope static and not others? class C{ int x; template T(alias a){ int foo(){ return x; } static int bar(int y){ return a(y); } } } Just because I don't want bar to be a member function of class C, with the new behaviour I now _also_ disable local instantiation. What is this? I also think that 'mangleof' shouldn't influence the design of this feature in the slightest, forward references need an orthogonal and well-founded design anyway.
Comment #45 by k.hara.pg — 2014-07-15T09:12:57Z
(In reply to timon.gehr from comment #44) > For one thing, what if I want to make some members of my template scope static > and not others? > > class C{ > int x; > template T(alias a){ > int foo(){ return x; } > static int bar(int y){ return a(y); } > } > } > > Just because I don't want bar to be a member function of class C, with the > new behaviour I now _also_ disable local instantiation. What is this? foo will become member function, and bar will become static member function. void main() { C c = new C(); c.x = 5; static int g(int n) { return n*10; } assert(c.T!g.foo() == 5); // foo is member function assert(C.T!g.bar(2) == 20); // bar is static member function }
Comment #46 by public — 2014-07-27T20:46:12Z
I recommend to revert https://github.com/D-Programming-Language/dmd/pull/2794 as it blocks the release and re-introduce it back again once solution is found - with matching language spec change PR.
Comment #47 by k.hara.pg — 2014-07-31T03:55:57Z
*** Issue 13062 has been marked as a duplicate of this issue. ***
Comment #48 by bugzilla — 2014-08-22T07:53:04Z
Comment #49 by dlang-bugzilla — 2014-08-22T12:07:26Z
May I ask why you're removing quote characters from issue titles? IMHO they are easier to read when the error message is quoted and thus distinguishable from the rest of the title.
Comment #50 by bugzilla — 2014-08-25T19:50:02Z
(In reply to Vladimir Panteleev from comment #49) > May I ask why you're removing quote characters from issue titles? IMHO they > are easier to read when the error message is quoted and thus distinguishable > from the rest of the title. I remove them because when I do a git checkout -a -m "message" I do a cut&paste from the bugzilla issue for the message. The " mess it all up. Yes, I could re-edit the git command line, but when the line wraps because it's too long, which it usually does, and then the command line editor for putty fouls everything up. It just makes things marginally easier for me to not have to deal with the ".
Comment #51 by doob — 2014-08-25T19:57:12Z
(In reply to Walter Bright from comment #50) > I remove them because when I do a > > git checkout -a -m "message" > > I do a cut&paste from the bugzilla issue for the message. The " mess it all > up. Yes, I could re-edit the git command line, but when the line wraps > because it's too long, which it usually does, and then the command line > editor for putty fouls everything up. > > It just makes things marginally easier for me to not have to deal with the ". If you check the message first you can use ' instead of ". BTW, I never use the -m flag and always write the commit message in my text editor. Then I get spell checking, indication if a line is too long and so on for free.
Comment #52 by dlang-bugzilla — 2014-08-25T20:01:47Z
Well, do you really think that saving a few seconds is worth the potential confusion? Let's look at the title now: > need 'this' to access member when passing field to template parameter (Consider that the reader is not familiar with this error message.) Who is the entity needing "this", the compiler or the programmer? Where does the error message end? Is it just "need 'this'"? "need 'this' to access member"? Or is the entire line coming from the compiler? What should I give to "grep" when searching the DMD source code? Besides, how many times are you going to type that command line? What takes longer, editing the issue title, or editing the commit message on the command line (or a text editor, as Jacob suggested)?
Comment #53 by yebblies — 2014-08-27T18:31:34Z
(In reply to Walter Bright from comment #50) > (In reply to Vladimir Panteleev from comment #49) > > May I ask why you're removing quote characters from issue titles? IMHO they > > are easier to read when the error message is quoted and thus distinguishable > > from the rest of the title. > > I remove them because when I do a > > git checkout -a -m "message" > > I do a cut&paste from the bugzilla issue for the message. The " mess it all > up. Yes, I could re-edit the git command line, but when the line wraps > because it's too long, which it usually does, and then the command line > editor for putty fouls everything up. > > It just makes things marginally easier for me to not have to deal with the ". This is classic!
Comment #54 by github-bugzilla — 2014-08-27T21:35:33Z
Commits pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/8c73839f6f1b2b3073d2189c8fd73309cf4ada28 fix Issue 11946 - need 'this' to access member when passing field to template parameter https://github.com/D-Programming-Language/dmd/commit/f32b97cfca5da1c00f47ac3941b19ef4d5c73c44 Merge pull request #3884 from WalterBright/fix11946 [reg] fix Issue 11946 - need 'this' to access member when passing field to template parameter
Comment #55 by github-bugzilla — 2014-08-28T04:10:26Z
Commit pushed to 2.066 at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/8b7a3cbb783e44fcd55ca019d8a266dafa97ddcb Merge pull request #3884 from WalterBright/fix11946 [reg] fix Issue 11946 - need 'this' to access member when passing field to template parameter