Skip to content

Commit

Permalink
Merge pull request #4721 from jmdavis/issue_16364
Browse files Browse the repository at this point in the history
Fix issue# 16364. Make getUDAs and hasUDA consistent.
  • Loading branch information
andralex authored Aug 10, 2016
2 parents faf0fc1 + 29470eb commit af39eff
Showing 1 changed file with 151 additions and 38 deletions.
189 changes: 151 additions & 38 deletions std/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -6640,36 +6640,23 @@ B select(bool cond : false, A, B)(lazy A a, B b) { return b; }
static assert(is(typeof(b) == real));
}

/**
* Determine if a symbol has a given
* $(DDSUBLINK spec/attribute,uda, user-defined attribute).
*/
/++
Determine if a symbol has a given
$(DDSUBLINK spec/attribute, uda, user-defined attribute).
See_Also:
$(LREF getUDAs)
+/
template hasUDA(alias symbol, alias attribute)
{
import std.meta : staticIndexOf, staticMap;

static if (is(attribute == struct) || is(attribute == class))
{
template GetTypeOrExp(alias S)
{
static if (is(typeof(S)))
alias GetTypeOrExp = typeof(S);
else
alias GetTypeOrExp = S;
}
enum bool hasUDA = staticIndexOf!(attribute, staticMap!(GetTypeOrExp,
__traits(getAttributes, symbol))) != -1;
}
else
enum bool hasUDA = staticIndexOf!(attribute, __traits(getAttributes, symbol)) != -1;
enum hasUDA = getUDAs!(symbol, attribute).length != 0;
}

///
@safe unittest
{
enum E;
struct S;
struct Named { string name; }
struct S {}

@("alpha") int a;
static assert(hasUDA!(a, "alpha"));
Expand All @@ -6694,37 +6681,101 @@ template hasUDA(alias symbol, alias attribute)
@S int e;
static assert(!hasUDA!(e, "alpha"));
static assert(hasUDA!(e, S));
static assert(!hasUDA!(e, S()));
static assert(!hasUDA!(e, E));

@(S, E, "alpha") int f;
static assert(hasUDA!(f, "alpha"));
@S() int f;
static assert(!hasUDA!(f, "alpha"));
static assert(hasUDA!(f, S));
static assert(hasUDA!(f, E));
static assert(hasUDA!(f, S()));
static assert(!hasUDA!(f, E));

@(S, E, "alpha") int g;
static assert(hasUDA!(g, "alpha"));
static assert(hasUDA!(g, S));
static assert(hasUDA!(g, E));

@(100) int g;
static assert(hasUDA!(g, 100));
@(100) int h;
static assert(hasUDA!(h, 100));

@Named("abc") int h;
static assert(hasUDA!(h, Named));
struct Named { string name; }

@Named("abc") int i;
static assert(hasUDA!(i, Named));
static assert(hasUDA!(i, Named("abc")));
static assert(!hasUDA!(i, Named("def")));

struct AttrT(T)
{
string name;
T value;
}

@AttrT!int("answer", 42) int j;
static assert(hasUDA!(j, AttrT));
static assert(hasUDA!(j, AttrT!int));
static assert(!hasUDA!(j, AttrT!string));

@AttrT!string("hello", "world") int k;
static assert(hasUDA!(k, AttrT));
static assert(!hasUDA!(k, AttrT!int));
static assert(hasUDA!(k, AttrT!string));

struct FuncAttr(alias f) { alias func = f; }
static int fourtyTwo() { return 42; }
static size_t getLen(string s) { return s.length; }

@FuncAttr!getLen int l;
static assert(hasUDA!(l, FuncAttr));
static assert(!hasUDA!(l, FuncAttr!fourtyTwo));
static assert(hasUDA!(l, FuncAttr!getLen));
static assert(!hasUDA!(l, FuncAttr!fourtyTwo()));
static assert(!hasUDA!(l, FuncAttr!getLen()));

@FuncAttr!getLen() int m;
static assert(hasUDA!(m, FuncAttr));
static assert(!hasUDA!(m, FuncAttr!fourtyTwo));
static assert(hasUDA!(m, FuncAttr!getLen));
static assert(!hasUDA!(m, FuncAttr!fourtyTwo()));
static assert(hasUDA!(m, FuncAttr!getLen()));
}

/**
* Gets the $(DDSUBLINK spec/attribute,uda, user-defined attributes) of the
* given type from the given symbol.
*/
/++
Gets the matching $(DDSUBLINK spec/attribute, uda, user-defined attributes)
from the given symbol.
If the UDA is a type, then any UDAs of the same type on the symbol will
match. If the UDA is a template for a type, then any UDA which is an
instantiation of that template will match. And if the UDA is a value,
then any UDAs on the symbol which are equal to that value will match.
See_Also:
$(LREF hasUDA)
+/
template getUDAs(alias symbol, alias attribute)
{
import std.meta : Filter;

template isDesiredUDA(alias S) {
static if (__traits(compiles, is(typeof(S) == attribute)))
template isDesiredUDA(alias toCheck)
{
static if (is(typeof(attribute)) && !__traits(isTemplate, attribute))
{
enum isDesiredUDA = is(typeof(S) == attribute);
static if (__traits(compiles, toCheck == attribute))
enum isDesiredUDA = toCheck == attribute;
else
enum isDesiredUDA = false;
}
else
else static if (is(typeof(toCheck)))
{
enum isDesiredUDA = isInstanceOf!(attribute, typeof(S));
static if (__traits(isTemplate, attribute))
enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck));
else
enum isDesiredUDA = is(typeof(toCheck) == attribute);
}
else static if (__traits(isTemplate, attribute))
enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
else
enum isDesiredUDA = is(toCheck == attribute);
}
alias getUDAs = Filter!(isDesiredUDA, __traits(getAttributes, symbol));
}
Expand All @@ -6739,34 +6790,96 @@ template getUDAs(alias symbol, alias attribute)
}

@Attr("Answer", 42) int a;
static assert(getUDAs!(a, Attr).length == 1);
static assert(getUDAs!(a, Attr)[0].name == "Answer");
static assert(getUDAs!(a, Attr)[0].value == 42);

@(Attr("Answer", 42), "string", 9999) int b;
static assert(getUDAs!(b, Attr).length == 1);
static assert(getUDAs!(b, Attr)[0].name == "Answer");
static assert(getUDAs!(b, Attr)[0].value == 42);

@Attr("Answer", 42) @Attr("Pi", 3) int c;
static assert(getUDAs!(c, Attr).length == 2);
static assert(getUDAs!(c, Attr)[0].name == "Answer");
static assert(getUDAs!(c, Attr)[0].value == 42);
static assert(getUDAs!(c, Attr)[1].name == "Pi");
static assert(getUDAs!(c, Attr)[1].value == 3);

static assert(getUDAs!(c, Attr("Answer", 42)).length == 1);
static assert(getUDAs!(c, Attr("Answer", 42))[0].name == "Answer");
static assert(getUDAs!(c, Attr("Answer", 42))[0].value == 42);

static assert(getUDAs!(c, Attr("Answer", 99)).length == 0);

struct AttrT(T)
{
string name;
T value;
}

@AttrT!uint("Answer", 42) @AttrT!int("Pi", 3) @AttrT int d;
static assert(getUDAs!(d, AttrT).length == 2);
static assert(getUDAs!(d, AttrT)[0].name == "Answer");
static assert(getUDAs!(d, AttrT)[0].value == 42);
static assert(getUDAs!(d, AttrT)[1].name == "Pi");
static assert(getUDAs!(d, AttrT)[1].value == 3);

static assert(getUDAs!(d, AttrT!uint).length == 1);
static assert(getUDAs!(d, AttrT!uint)[0].name == "Answer");
static assert(getUDAs!(d, AttrT!uint)[0].value == 42);

static assert(getUDAs!(d, AttrT!int).length == 1);
static assert(getUDAs!(d, AttrT!int)[0].name == "Pi");
static assert(getUDAs!(d, AttrT!int)[0].value == 3);

struct SimpleAttr {}

@SimpleAttr int e;
static assert(getUDAs!(e, SimpleAttr).length == 1);
static assert(is(getUDAs!(e, SimpleAttr)[0] == SimpleAttr));

@SimpleAttr() int f;
static assert(getUDAs!(f, SimpleAttr).length == 1);
static assert(is(typeof(getUDAs!(f, SimpleAttr)[0]) == SimpleAttr));

struct FuncAttr(alias f) { alias func = f; }
static int add42(int v) { return v + 42; }
static string concat(string l, string r) { return l ~ r; }

@FuncAttr!add42 int g;
static assert(getUDAs!(g, FuncAttr).length == 1);
static assert(getUDAs!(g, FuncAttr)[0].func(5) == 47);

static assert(getUDAs!(g, FuncAttr!add42).length == 1);
static assert(getUDAs!(g, FuncAttr!add42)[0].func(5) == 47);

static assert(getUDAs!(g, FuncAttr!add42()).length == 0);

static assert(getUDAs!(g, FuncAttr!concat).length == 0);
static assert(getUDAs!(g, FuncAttr!concat()).length == 0);

@FuncAttr!add42() int h;
static assert(getUDAs!(h, FuncAttr).length == 1);
static assert(getUDAs!(h, FuncAttr)[0].func(5) == 47);

static assert(getUDAs!(h, FuncAttr!add42).length == 1);
static assert(getUDAs!(h, FuncAttr!add42)[0].func(5) == 47);

static assert(getUDAs!(h, FuncAttr!add42()).length == 1);
static assert(getUDAs!(h, FuncAttr!add42())[0].func(5) == 47);

static assert(getUDAs!(h, FuncAttr!concat).length == 0);
static assert(getUDAs!(h, FuncAttr!concat()).length == 0);

@("alpha") @(42) int i;
static assert(getUDAs!(i, "alpha").length == 1);
static assert(getUDAs!(i, "alpha")[0] == "alpha");

static assert(getUDAs!(i, 42).length == 1);
static assert(getUDAs!(i, 42)[0] == 42);

static assert(getUDAs!(i, 'c').length == 0);
}

/**
Expand Down

0 comments on commit af39eff

Please sign in to comment.