On Monday, 21 April 2014 at 16:45:15 UTC, Jacob Carlborg wrote:
On Sunday, 20 April 2014 at 14:38:47 UTC, Frustrated wrote:
How bout this!
Why not allow one to define their own attributes from a
generalized subset and then define a few standard ones like
@nogc.
Sounds like you want AST macros. Have a look at this
http://wiki.dlang.org/DIP50#Declaration_macros
--
/Jacob Carlborg
Not quite. AST macros simply transform code. Attributes attach
meta data to code. While I'm sure there is some overlap they are
not the same.
Unless AST macros have the ability to arbitrary add additional
contextual information to meta code then they can't emulate
attributes.
E.g., Suppose you have D with AST macros but not attributes, how
can you add them?
In the dip, you have
macro attr (Context context, Declaration decl)
{
auto attrName = decl.name;
auto type = decl.type;
return <[
private $decl.type _$decl.name;
$decl.type $decl.name ()
{
return _$decl.name;
}
$decl.type $decl.name ($decl.type value)
{
return _$decl.name = value;
}
]>;
}
class Foo
{
@attr int bar;
}
but attr is not an attribute. It is an macro. @attr converts the
"int bar" field into a private setter and getter. This has
nothing to do with attributes.
(just cause you use the attr word and the @ symbol doesn't make
it an attribute)
I don't see how you could ever add attributes to D using AST
macros above unless the definition of an AST macro is modified.
[Again, assuming D didn't have attributes in the first place]
This does not mean that AST macros could not be used to help
define the generalized attributes though.
What I am talking about is instead of hard coding attributes in
the compiler, one abstracts and generalizes the code so that any
attribute could be added in the future with minimal work.
It would simply require one to add the built in attributes list,
add the attribute grammar(which is used to reduce compound
attributes), add any actions that happen when the attribute is
used in code.
e.g.,
builtin_attributes = {
{pureness, pure, !pure/impure,
attr = any(attr, impure) => impure
attr = all(attr, pure) => pure
}
{gc, gc, !gc/nogc,
attr = any(attr, gc) => gc
attr = all(attr, nogc) => nogc
}
etc... }
notices that pureness and gc have the same grammatical rules.
Code would be added to handle the pureness and gc attributes when
they are come across for optimization purposes.
The above syntax is just made up and pretty bad but hopefully not
too difficult to get the bigger picture.
Every new built in attribute would just have to be added to the
list above(easy) and code that uses it for whatever purpose would
be added in the code where it belongs.
User define attributes essentially would make the attributes list
above dynamic allowing the user to add to it. The compiler would
only be told how to simplify the attributes using the grammar and
would do so but would not have any code inserted because there is
no way for the user to hook into the compiler properly(I suppose
it could be done if the compiler was written in an oop like way).
In any case, because the compiler knows how to simplify UDA's by
using the provided grammar it makes UDA's much more powerful. CT
reflection and AST macros would make them way more powerful. The
ability to add hooks into the compiler would nearly give the user
the same power as the compiler writer. Of course, this would
probably lead to all kinds of compatibility problems in user code.
Basically, as of now, all we can do is define UDA's, we can't
define how they relate to each other(the grammar) nor what
happens when the exist(the behavior). There should not be any
real difference between UDA's and built in attributes(except
hooking, only because of complexity issues) and having a
generalized attribute system in the compiler would go a long way
to bridging the gap.
The main thing to take away from this is that *if* D had such an
attribute system, the gc/nogc issue would be virtually
non-existent. It would only take a few minutes to add a UDA
@gc/@nogc to D in user code. At least a few people would have
done it, and it's merits could be seen. Then it would be only a
matter of "copy and pasting" the definition of the attribute to
the compiler code and it would then become a built in making it
available for everyone.
At some point optimizations in the compiler could potentially be
added... just for fun. Not really the point of the attribute but
having it does provide such possibilities.
Also, with such a system, attributes can propagate from the
bottom up and inference makes it a lot easier.
E.g., atomic statements could be "marked" with attributes. Blocks
of statements can be marked but also inherit from the statements
and blocks they use.
With such a system, one could mark individual assembly
instructions as, say, @dangerous. If @dangerous was a "bubbling"
attribute then you could check and see which functions in your
code was @dangerous, or if your program was @dangerous.
One could do it similar with @allocate. Any primitive thing that
allocates in D should get the attribute. Then, if @allocate is a
"bubbling" attribute, you could find out exactly which parts of
code in your allocates. You could even find out were this happens.
e.g.,
void foo()
{
auto m = core.stdc.stdlib.malloc(size);
bar(); // assume bar does not have @allocate attribute(does
not allocate memory in any way)
}
foo would be marked @allocate since malloc is marked with
@allocate and @allocate is an inheriting attribute. A utility
could be written that shows the hierarchy of the program allowing
you to explore things and you could find foo and see that the
reason why foo is marked @allocate is because it calls malloc
which is marked @allocate. (malloc would be explicitly marked
while foo would be deduced by the compiler for us)
Hopefully it is easy to see that by starting from the bottom and
with a well defined attribute system the compiler can become more
powerful by automatically simplifying attributes for us no matter
how complex the program.