Bug 15464 – Template parameter-dependent attributes

Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2015-12-21T13:56:00Z
Last change time
2016-01-03T14:02:45Z
Keywords
preapproved
Assigned to
nobody
Creator
andrei

Comments

Comment #0 by andrei — 2015-12-21T13:56:40Z
Related forum discussion: http://forum.dlang.org/post/[email protected] Attributes should be allowed to depend on template parameters. Without this, their usefulness is seriously and artificially limited. Example in the post: void insertFrontMany(C, R)(C container, R range) @BigO(complexity!(C.insertFront) * linearTime) { ... } currently does not compile. Walter and I discussed this language change and we're preapproving it. Per Timon Gehr's reply, implementation could achieved the feature by means of a lowering.
Comment #1 by kozzi11 — 2015-12-21T18:27:58Z
Comment #2 by kozzi11 — 2015-12-21T18:29:19Z
but s.ident https://github.com/D-Programming-Language/dmd/blob/master/src/parse.d#L4139 must be put to some temporaly variable before changunf s :)
Comment #3 by kozzi11 — 2015-12-22T08:06:17Z
Comment #4 by k.hara.pg — 2015-12-23T00:23:45Z
(In reply to Andrei Alexandrescu from comment #0) > Per Timon Gehr's reply, implementation could achieved the feature by means of a lowering. A function template declaration: void foo(T)() @attr(T.field) {} Is now lowered to: @attr(T.field) template foo(T) { void foo() {} } So, as Timon said, if we change the lowered form as follows, it will support the enhancement naturally. template foo(T) { void foo() @attr(T.field) {} } However, I have some questions about the corner cases in the change. Q1. The enhancement will move prefix and postfix UDAs into the template member. But it would not change the label and block style UDAs behavior. Today, a template declaration can have UDAs. So the change would change the behavior of currently working code. ---- enum attr; void foo(T)(T t) @attr {} // Now equivalent with: // @attr { templete foo(T) { void foo(T t) {} } } // After the change, it will become same with: // templete foo(T) { @attr { void foo(T t) {} } } alias fooInt = foo!int; // Now: pragma(msg, __traits(getAttributes, foo)); // tuple((attr)) pragma(msg, __traits(getAttributes, fooInt)); // tuple((attr)) // After the change: //pragma(msg, __traits(getAttributes, foo)); // tuple() --> breaking! //pragma(msg, __traits(getAttributes, fooInt)); // tuple((attr)) ---- ---- enum attr; @attr: void foo1(T)(T t) {} // label @attr { void foo2(T)(T t) {} } // block @attr void foo3(T)(T t) {} // prefix void foo4(T)(T t) @attr {} // postfix // Now pragma(msg, __traits(getAttributes, foo1)); // tuple(attr) pragma(msg, __traits(getAttributes, foo2)); // tuple(attr) pragma(msg, __traits(getAttributes, foo3)); // tuple(attr) pragma(msg, __traits(getAttributes, foo4)); // tuple(attr) // After //pragma(msg, __traits(getAttributes, foo1)); // tuple(attr) //pragma(msg, __traits(getAttributes, foo2)); // tuple(attr) //pragma(msg, __traits(getAttributes, foo3)); // tuple() --> breaking! //pragma(msg, __traits(getAttributes, foo4)); // tuple() --> breaking! ---- Q2. For the consistency, prefix UDAs should be handled specially for any other templates. Right? ---- // All of them will be changed to workable? @uda(T.field) class C(T) {} @uda(T.field) struct S(T) {} @uda(T.field) interface I(T) {} @uda(T.field) union U(T) {} @uda(T.field) template Foo(T) {} // But those will be left as unworkable? // block style @uda(T.field) { class C(T) {} } @uda(T.field) { struct S(T) {} } @uda(T.field) { interface I(T) {} } @uda(T.field) { union U(T) {} } @uda(T.field) { template Foo(T) {} } // label style public { @uda(T.field): class C(T) {} } public { @uda(T.field): struct S(T) {} } public { @uda(T.field): interface I(T) {} } public { @uda(T.field): union U(T) {} } public { @uda(T.field): template Foo(T) {} } ----
Comment #5 by kozzi11 — 2015-12-23T08:00:04Z
(In reply to Kenji Hara from comment #4) > (In reply to Andrei Alexandrescu from comment #0) > > Per Timon Gehr's reply, implementation could achieved the feature by means of a lowering. > > A function template declaration: > > void foo(T)() @attr(T.field) {} > > Is now lowered to: > > @attr(T.field) template foo(T) { void foo() {} } > > So, as Timon said, if we change the lowered form as follows, it will support > the enhancement naturally. > > template foo(T) { void foo() @attr(T.field) {} } > > > However, I have some questions about the corner cases in the change. > ... > ---- My current PR affects only postfix udas, so @uda(T.field) void f(T)(){} still does not compile and will have same behaviour as before. So only postfix UDAs change and break, this can be maybe avoid if this new lowering will be applied only to UDAs with templates parametrs. The biggest problem I see here is consistency
Comment #6 by k.hara.pg — 2015-12-24T03:02:21Z
(In reply to Daniel Kozak from comment #5) > So only postfix UDAs change and break, this can be maybe avoid if this new > lowering will be applied only to UDAs with templates parametrs. > > The biggest problem I see here is consistency Yes, my concern is consistency. Currently label, block, prefix, and postfix style attributes (both built-ins and UDAs) have no difference. If attributes conflict, the innermost attribute is precedence. And, prefix and postfix position are treated as "same level". For example: @safe void foo() @trusted {} // Error, prefix @safe attribute conflicts with postfix @trusted If this enhancement needs to break existing consistency, it should have clear answers which derived from some new language design decisions. Otherwise the enhancement will be a merely convenient feature and will increase the chaos in D.
Comment #7 by andrei — 2015-12-25T02:02:18Z
Thanks Daniel for the work and Kenji for the analysis! There are similarities between this situation and: static if (condition1) void func(T)() if (condition2); condition1 cannot use T because it hasn't "appeared" syntactically yet. Of course, technically it's possible to make that work but it's not natural. Somewhat similarly, for template functions it stands to reason that prefix attributes apply to the template symbol, whereas postfix attributes apply to the instantiation. Then: * Attributes of the template propagate to the instantiation * If there's a conflict between the template and instantiation attributes, that's an error. Sounds reasonable?
Comment #8 by andrei — 2015-12-28T21:20:20Z
Ping?
Comment #9 by bugzilla — 2015-12-30T10:31:24Z
I think Daniel's PR is good because it does not change the consistency - it just changes what is in scope. Scope rules have always affected attributes based on the location of the attribute.
Comment #10 by github-bugzilla — 2015-12-30T10:54:28Z
Commits pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/012b685ea12aa045c9bd04a1c9d4abf74e0dac0d fix issue 15464 https://github.com/D-Programming-Language/dmd/commit/7a49fadf16bcc4fceb3b573b3845663f25cd1271 Merge pull request #5314 from Kozzi11/issue15464 fix: Issue 15464 - Template parameter-dependent attributes
Comment #11 by andrei — 2015-12-30T21:19:38Z
@Daniel any chance you could also update the documentation accordingly?
Comment #12 by kozzi11 — 2015-12-31T07:40:32Z
(In reply to Andrei Alexandrescu from comment #11) > @Daniel any chance you could also update the documentation accordingly? Yep, I will do it as soon as I finish some other thing :).
Comment #13 by github-bugzilla — 2016-01-03T14:02:45Z