OK, so I woke up this morning to find a huge discussion on attributes, and I'd like to once again propose the idea of how to define and use an attribute.

I do not like the idea of:

@attr(identifier)

Why? Because what the hell did we create that "@" syntax for? I though it was to avoid misinterpreting such things as normal symbols, and avoid creating new keywords. Why should the compiler be the only one able to use such symbols?

Another thing I don't like is some idea that only a certain type of construct can be the identifier. An attribute should have one requirement -- that it can be created/determined at compile time.

So here is my proposal:

1. Introduce a new compiler-defined attribute @attribute (or @attr or something better, the name isn't important).
2. This attribute can *only* be used on a module-level function.
3. @attribute functions *must* be CTFE-able.
4. An @attribute function can be used as a user-defined attribute on any declaration using the syntax @identifier where identifier is the name of the attribute function (subject to normal function lookup rules). If the attribute can be called without parameters, the parentheses are optional. 5. When used on a declaration, that CTFE function is called during compile-time, and the result of that function is stored as metadata on that symbol. It does not affect the type of the symbol or transfer to any other symbols that are assigned to the value of that declaration (in other words, it *cannot* be used as a type constructor). 6. The metadata is stored in a key-value pair, with the key being the symbol of the @attribute function, and the value being the result of the CTFE function. 7. One can lookup whether an attribute exists on a symbol using __traits(hasAttribute, symbol). 8. One can retrieve the value of the CTFE result using __traits(getAttribute, symbol). If the CTFE function returns void, this is a compiler error.

And that's it. We can extend this eventually to storing something in TypeInfo, but I'm not sure we need that. However, I want to stress that having runtime type metadata is not a requirement for this proposal.

Example usage:

@attribute bool serializable(bool yesorno = true) { return yesorno; }

unittest {
   // serializable is a normal function also
   assert(serializable() == true);
   assert(serializable(true) == true);
   assert(serializable(false) == false);
}

@serializable struct MyType
{
   int x;
   int y;
   @serializable(false) int z;
}

string serialize(T)(const ref T t) if (__traits(hasAttribute, serializable) && __traits(getAttribute, serializable))
{
// serialize each field. Skip any fields that are marked as serializable == false
}

-Steve

Reply via email to