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.
(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
@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