I've identified two issues related to this problem.
https://dlang.org/spec/attribute.html#uda says:
"User-Defined Attributes (UDA) are compile-time expressions that can be attached to a declaration."
This should be invalid:
struct jsonize { string name; }
@jsonize int i;
`jsonize` is a type, but it is used as an expression. We see how this misbehaves when we inspect it:
foreach (uda; __traits(getAttributes, i)) writeln(uda);
// Error: cannot pass type jsonize as a function argument
This is an issue whenever we try to inspect the content of a UDA that does not have required parameters.
While looking for a workaround, I thought to create a function that returned the UDA value, thinking this function would be executed at compile-time and the result returned as a UDA value. This was not the case:
size_t print(string s = "hello world")
{
writeln(s);
return s.length;
}
@print int i;
void main()
{
writeln("testing");
static foreach (uda; __traits(getAttributes, j))
{
writeln(uda);
}
}
It compiles and prints:
testing
hello world
10
That shouldn't happen. The spec says the expression should be a compile-time expression.
We see more clearly what's happening with pragma(msg):
struct _UDA2 {}
_UDA2 UDA2(string s) { writeln("hello world"); return _UDA2(); }
@UDA2("hi") int ir;
pragma(msg, __traits(getAttributes, ir).stringof);
This prints out: tuple(UDA2("hi")) -- it's a tuple containing a function call. I suspect that this will produce strange breakages in rare cases when using attributes at compile time (in addition to the current situation where invalid code is accepted).
Comment #1 by dhasenan — 2018-07-29T20:16:06Z
Other things you can use as UDAs:
* modules: @(std.stdio)
* packages: @(std)
* references to members: struct A { int i; } @(A.i)
* uint s; @(s++). This UDA changes every time you access it.
* @(new Object). Likewise.
* string str; @(str ~= "x"). Likewise.
The only thing that doesn't work is declarations, because that's forbidden by the grammar. It seems like there's no special semantic analysis applied other than recursing into child expressions.
Lowering should help:
@Something int x;
into
immutable __tmp = Something;
@__tmp int x;
Comment #2 by dhasenan — 2018-07-30T02:46:08Z
Correction: non-CTFEable expressions besides function calls without arguments cannot be used as UDAs. Some of my testing was incorrect. Had B4S1L3 on IRC been more professional or friendly, I might have seen that sooner.
Comment #3 by dhasenan — 2018-07-30T02:49:22Z
Oh hey, more fun:
---
module test2;
import std.stdio;
struct S
{
this(int i) { writeln(i); }
}
size_t print(string s = "hello world")
{
writeln(s);
return s.length;
}
size_t x(size_t i) { return i + 1; }
struct A { static uint x; }
string s = "";
int i;
@(std.stdio)
@(new Object)
@(x(4))
@(S(1))
@(A.x)
@(A.x++)
@print
int j;
pragma(msg, __traits(getAttributes, j).stringof);
---
With the pragma(msg), this compiles. Without, it doesn't. Weee!
Comment #4 by nick — 2023-05-03T12:20:13Z
> "User-Defined Attributes (UDA) are compile-time expressions that can be attached to a declaration."
Fixed this wording in https://github.com/dlang/dlang.org/pull/3598.
> This should be invalid:
> struct jsonize { string name; }
> @jsonize int i;
> `jsonize` is a type, but it is used as an expression. We see how this misbehaves when we inspect it:
> foreach (uda; __traits(getAttributes, i)) writeln(uda);
> // Error: cannot pass type jsonize as a function argument
It is actually correct, a UDA can be defined by a type name. And naturally you can't pass a type as a function argument. You can use it in declarations, scroll down to the getAttributes example:
https://dlang.org/spec/attribute.html#uda
The other behaviour is very weird for @print. Also non-compile-time expressions should definitely be disallowed.
Comment #5 by b2.temp — 2023-10-16T04:38:55Z
The specs have changed since. Neia, can you re-evluate the validity of your report ?
Comment #6 by robert.schadek — 2024-12-13T19:00:02Z