Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions changelog/alias_from_trait.dd
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
Aliases can be created directly from a `__trait`.

Previously an `AliasSeq` was necessary in order to declare an alias based on a `__trait`.
Aliases can be created directly from the traits that return symbol(s) or tuples.
This includes `getMember`, `allMembers`, `derivedMembers`, `parent`, `getOverloads`,
`getVirtualFunctions`, `getVirtualMethods`, `getUnitTests` and finally `getAttributes`.
Previously an `AliasSeq` was necessary in order to alias their return.
Now the grammar allows to write shorter declarations:

---
struct Foo
{
int a;
static int a;
}

alias oldWay = AliasSeq!(__traits(getMember, Foo, "a"))[0];
alias newWay = __traits(getMember, Foo, "a");
---

To permit this it was more interesting to include `__trait` in the basic types
instead of just changing the alias syntax, so as bonus,
a new way of declaring the variables exists:
rather than just changing the alias syntax. So additionally, wherever a type appears
a `__trait` can be used, for example in a variable declaration:

---
struct Foo { static struct Bar {} }
const(__traits(getMember, Foo, "Bar")) fooBar;
static assert(is(typeof(fooBar) == const(Foo.Bar)));
---

Despite of being a type from the syntax point of view and when used specifically for an `alias`
the `getMember` trait can also represent symbols or even imports.
This does not change the alias semantic since for example the same distinction
was already done in case of an identifier chain.
11 changes: 10 additions & 1 deletion src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -4142,8 +4142,9 @@ struct ASTBase
{
TraitsExp exp;
Loc loc;
bool inAliasDeclaration;

extern (D) this(Loc loc, TraitsExp exp)
extern (D) this(const ref Loc loc, TraitsExp exp)
{
super(Tident);
this.loc = loc;
Expand All @@ -4154,6 +4155,14 @@ struct ASTBase
{
v.visit(this);
}

override Type syntaxCopy()
{
TraitsExp te = cast(TraitsExp) exp.syntaxCopy();
TypeTraits tt = new TypeTraits(loc, te);
tt.mod = mod;
return tt;
}
}

extern (C++) final class TypeIdentifier : TypeQualified
Expand Down
15 changes: 6 additions & 9 deletions src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -5954,18 +5954,15 @@ void aliasSemantic(AliasDeclaration ds, Scope* sc)
//printf("AliasDeclaration::semantic() %s\n", ds.toChars());
if (ds.type && ds.type.ty == Ttraits)
{
// TypeTraits is itself a kind of nested alias so its semantic is called
// manually, beforehand.
TypeTraits tt = cast(TypeTraits) ds.type;
tt.inAliasDeclaration = true;
if (auto t = typeSemantic(tt, tt.loc, sc))
{
ds.type = t;
}
else
{
if (tt.sym)
ds.aliassym = tt.sym;
// __trait(getMember) always returns alias-able stuff
// so at this point either we're good or traits sem has emitted an error
}
else if (tt.sym)
ds.aliassym = tt.sym;
tt.inAliasDeclaration = false;
}
if (ds.aliassym)
{
Expand Down
9 changes: 8 additions & 1 deletion src/dmd/mtype.d
Original file line number Diff line number Diff line change
Expand Up @@ -5035,8 +5035,10 @@ extern (C++) final class TypeTraits : Type
TraitsExp exp;
/// The symbol when exp doesn't represent a type.
Dsymbol sym;
/// Indicates wether we are in an alias or not.
bool inAliasDeclaration;

final extern (D) this(Loc loc, TraitsExp exp)
final extern (D) this(const ref Loc loc, TraitsExp exp)
{
super(Ttraits);
this.loc = loc;
Expand All @@ -5055,6 +5057,11 @@ extern (C++) final class TypeTraits : Type
{
v.visit(this);
}

override d_uns64 size(const ref Loc loc)
{
return SIZE_INVALID;
}
}

/***********************************************************
Expand Down
2 changes: 2 additions & 0 deletions src/dmd/mtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,8 @@ class TypeTraits : public Type
TraitsExp *exp;
/// The symbol when exp doesn't represent a type.
Dsymbol *sym;
/// Indicates wether we are in an alias or not.
bool inAliasDeclaration;
Type *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
Expand Down
20 changes: 3 additions & 17 deletions src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -3721,25 +3721,11 @@ final class Parser(AST) : Lexer
break;

case TOK.traits:
{
AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp();
if (!te)
{
// error already emitted while parsing primary
t = new AST.TypeError;
}
else if (te.ident != Id.getMember)
{
// even if this is not a grammar error, it's not worth continuing.
error("invalid `__traits`, only `getMember` can give types and symbols");
t = new AST.TypeError;
}
if (AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp())
t = new AST.TypeTraits(token.loc, te);
else
{
t = new AST.TypeTraits(loc, te);
}
t = new AST.TypeError;
break;
}

case TOK.const_:
// const(type)
Expand Down
34 changes: 33 additions & 1 deletion src/dmd/typesem.d
Original file line number Diff line number Diff line change
Expand Up @@ -1650,13 +1650,45 @@ private extern (C++) final class TypeSemanticVisitor : Visitor
import dmd.traits : semanticTraits;

result = null;
if (mtype.ty == Terror)
{
result = mtype;
return;
}
if (mtype.exp.ident != Id.allMembers &&
mtype.exp.ident != Id.derivedMembers &&
mtype.exp.ident != Id.getMember &&
mtype.exp.ident != Id.parent &&
mtype.exp.ident != Id.getOverloads &&
mtype.exp.ident != Id.getVirtualFunctions &&
mtype.exp.ident != Id.getVirtualMethods &&
mtype.exp.ident != Id.getAttributes &&
mtype.exp.ident != Id.getUnitTests)
{
static immutable (const(char)*)[2] ctxt = ["as type", "in alias"];
.error(mtype.loc, "trait `%s` is either invalid or not supported %s",
mtype.exp.ident.toChars, ctxt[mtype.inAliasDeclaration]);
result = mtype;
mtype.ty = Terror;
return;
}
if (Expression e = semanticTraits(mtype.exp, sc))
{
if (Dsymbol ds = getDsymbol(e))
if (TupleExp te = e.toTupleExp)
mtype.sym = new TupleDeclaration(mtype.loc,
Identifier.generateId("__aliastup"), cast(Objects*) te.exps);
else if (Dsymbol ds = getDsymbol(e))
mtype.sym = ds;
else if (Type t = getType(e))
result = t.addMod(mtype.mod);
}
if (!mtype.inAliasDeclaration && !result)
{
if (!global.errors)
.error(mtype.loc, "`%s` does not give a valid type", mtype.toChars);
result = mtype;
mtype.ty = Terror;
}
}

override void visit(TypeReturn mtype)
Expand Down
4 changes: 3 additions & 1 deletion test/fail_compilation/e7804_1.d
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/*
TEST_OUTPUT:
---
fail_compilation/e7804_1.d(9): Error: invalid `__traits`, only `getMember` can give types and symbols
fail_compilation/e7804_1.d(10): Error: trait `farfelu` is either invalid or not supported as type
fail_compilation/e7804_1.d(11): Error: trait `farfelu` is either invalid or not supported in alias
---
*/
module e7804_1;

__traits(farfelu, Aggr, "member") a;
alias foo = __traits(farfelu, Aggr, "member");
19 changes: 19 additions & 0 deletions test/fail_compilation/e7804_2.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
TEST_OUTPUT:
---
fail_compilation/e7804_2.d(17): Error: `__traits(getMember, Foo, "func")` does not give a valid type
fail_compilation/e7804_2.d(18): Error: cannot cast `int` to `__traits(getMember, Foo, "func")`
---
*/
module e7804_2;

class Foo
{
void func(){}
}

void test()
{
__traits(getMember, Foo, "func") var;
auto a = cast(__traits(getMember, Foo, "func")) 0;
}
78 changes: 71 additions & 7 deletions test/runnable/e7804.d
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
/* REQUIRED_ARGS: -unittest
*/
module e7804;

struct Bar {static struct B{}}
alias BarB = __traits(getMember, Bar, "B");
static assert(is(BarB == Bar.B));
static assert(is(const(__traits(getMember, Bar, "B")) == const(Bar.B)));

alias BarBParent = __traits(parent, BarB);
static assert(is(BarBParent == Bar));

struct Foo {alias MyInt = int;}
alias FooInt = __traits(getMember, Foo, "MyInt");
static immutable FooInt fi = 42;
static assert(fi == 42);
void declVsStatementSupport()
{
__traits(getMember, Foo, "MyInt") i1;
const(__traits(getMember, Foo, "MyInt")) i2;
__traits(getMember, Foo, "MyInt") i1 = 1;
const(__traits(getMember, Foo, "MyInt")) i2 = 1;
assert(i1 == i2);
}



enum __traits(getMember, Foo, "MyInt") a0 = 12;
static assert(is(typeof(a0) == int));
static assert(a0 == 12);


const __traits(getMember, Foo, "MyInt") a1 = 46;
static this(){assert(a1 == 46);}


__traits(getMember, Foo, "MyInt") a2 = 78;
static this(){assert(a2 == 78);}


const(__traits(getMember, Foo, "MyInt")) a3 = 63;
static this(){assert(a3 == 63);}


struct WithSym {static int foo; static int bar(){return 42;}}
Expand All @@ -44,4 +45,67 @@ alias f2 = WithSym.bar;
static assert(__traits(isSame, f1, f2));


void main(){}
auto ovld(const(char)[] s){return s;}
auto ovld(int i){return i;}
alias ovlds = __traits(getOverloads, e7804, "ovld");


struct TmpPrm(T)
if (is(T == int)){T t;}
TmpPrm!(__traits(getMember, Foo, "MyInt")) tpt = TmpPrm!(__traits(getMember, Foo, "MyInt"))(42);


@Foo @(1) class Class
{
final void virtual(){}
int virtual(int p){return p;}
void test(this T)()
{
alias vf = __traits(getVirtualFunctions, Class, "virtual");
assert(vf.length == 2);
alias vm = __traits(getVirtualMethods, Class, "virtual");
assert(vm.length == 1);
assert(vm[0](42) == 42);
alias attribs = __traits(getAttributes, Class);
assert(attribs.length == 2);
assert(attribs[0] is Foo);
assert(attribs[1] == 1);

alias objectAll = __traits(allMembers, Object);
alias classDerived = __traits(derivedMembers, Class);
alias classAll = __traits(allMembers, Class);
enum Seq(T...) = T;
static assert (classAll == Seq!(classDerived, objectAll));
}
}


struct UnitTests
{
static int count;
unittest { count++; }
unittest {++++count;}
static void test()
{
alias tests = __traits(getUnitTests, UnitTests);
static assert(tests.length == 2);
foreach(t; tests) t();
assert(count == 6); // not 3 because executed automatically (DRT) then manually
}
}

void main()
{
declVsStatementSupport();
assert(a1 == 46);
assert(a2 == 78);
assert(a3 == 63);
assert(f1() == f2());
Foo.MyInt fmi = cast(__traits(getMember, Foo, "MyInt")) 0;
auto c = __traits(getMember, Foo, "MyInt").max;
assert(c == int.max);
assert(ovlds[0]("farfelu") == "farfelu");
assert(ovlds[1](42) == 42);
(new Class).test();
UnitTests.test();
}