diff --git a/changelog/alias_from_trait.dd b/changelog/alias_from_trait.dd new file mode 100644 index 000000000000..62233b68f951 --- /dev/null +++ b/changelog/alias_from_trait.dd @@ -0,0 +1,29 @@ +Aliases can be created directly from a `__trait`. + +Previously an `AliasSeq` was necessary in order to declare an alias based on a `__trait`. +Now the grammar allows to write shorter declarations: + +--- +struct Foo +{ + 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: + +--- +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. diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index 8e75e6803b50..fabe93ca87bf 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -4138,6 +4138,24 @@ struct ASTBase } } + extern (C++) class TypeTraits : Type + { + TraitsExp exp; + Loc loc; + + extern (D) this(Loc loc, TraitsExp exp) + { + super(Tident); + this.loc = loc; + this.exp = exp; + } + + override void accept(Visitor v) + { + v.visit(this); + } + } + extern (C++) final class TypeIdentifier : TypeQualified { Identifier ident; diff --git a/src/dmd/dmangle.d b/src/dmd/dmangle.d index 9fa8593b141e..e5e4005c5a36 100644 --- a/src/dmd/dmangle.d +++ b/src/dmd/dmangle.d @@ -102,6 +102,7 @@ private immutable char[TMAX] mangleChar = Tslice : '@', Treturn : '@', Tvector : '@', + Ttraits : '@', ]; unittest diff --git a/src/dmd/dsymbolsem.d b/src/dmd/dsymbolsem.d index b57f11e966bb..960c0df75ea3 100644 --- a/src/dmd/dsymbolsem.d +++ b/src/dmd/dsymbolsem.d @@ -5976,6 +5976,21 @@ Laftersemantic: void aliasSemantic(AliasDeclaration ds, Scope* sc) { //printf("AliasDeclaration::semantic() %s\n", ds.toChars()); + if (ds.type && ds.type.ty == Ttraits) + { + TypeTraits tt = cast(TypeTraits) ds.type; + 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 + } + } if (ds.aliassym) { auto fd = ds.aliassym.isFuncLiteralDeclaration(); diff --git a/src/dmd/hdrgen.d b/src/dmd/hdrgen.d index 227b26760af7..08f74525f5fe 100644 --- a/src/dmd/hdrgen.d +++ b/src/dmd/hdrgen.d @@ -797,6 +797,12 @@ public: buf.writestring(t.dstring); } + override void visit(TypeTraits t) + { + //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod); + visit(t.exp); + } + override void visit(TypeVector t) { //printf("TypeVector::toCBuffer2(t.mod = %d)\n", t.mod); diff --git a/src/dmd/mtype.d b/src/dmd/mtype.d index bc183c6fa9db..0d4b45f42539 100644 --- a/src/dmd/mtype.d +++ b/src/dmd/mtype.d @@ -285,6 +285,7 @@ enum ENUMTY : int Tvector, Tint128, Tuns128, + TTraits, TMAX, } @@ -332,6 +333,7 @@ alias Tnull = ENUMTY.Tnull; alias Tvector = ENUMTY.Tvector; alias Tint128 = ENUMTY.Tint128; alias Tuns128 = ENUMTY.Tuns128; +alias Ttraits = ENUMTY.TTraits; alias TMAX = ENUMTY.TMAX; alias TY = ubyte; @@ -472,6 +474,7 @@ extern (C++) abstract class Type : RootObject sizeTy[Terror] = __traits(classInstanceSize, TypeError); sizeTy[Tnull] = __traits(classInstanceSize, TypeNull); sizeTy[Tvector] = __traits(classInstanceSize, TypeVector); + sizeTy[Ttraits] = __traits(classInstanceSize, TypeTraits); return sizeTy; }(); @@ -5017,6 +5020,43 @@ extern (C++) final class TypeDelegate : TypeNext } } +/** + * This is a shell containing a TraitsExp that can be + * either resolved to a type or to a symbol. + * + * The point is to allow AliasDeclarationY to use `__traits(getMember)` + * directly (VS using a library helper in the past). + */ +extern (C++) final class TypeTraits : Type +{ + Loc loc; + + /// The expression to resolve as type or symbol. + TraitsExp exp; + /// The symbol when exp doesn't represent a type. + Dsymbol sym; + + final extern (D) this(Loc loc, TraitsExp exp) + { + super(Ttraits); + this.loc = loc; + this.exp = exp; + } + + override Type syntaxCopy() + { + TraitsExp te = cast(TraitsExp) exp.syntaxCopy(); + TypeTraits tt = new TypeTraits(loc, te); + tt.mod = mod; + return tt; + } + + override void accept(Visitor v) + { + v.visit(this); + } +} + /*********************************************************** */ extern (C++) abstract class TypeQualified : Type diff --git a/src/dmd/mtype.h b/src/dmd/mtype.h index 0607044b78e2..cf8fdb660f09 100644 --- a/src/dmd/mtype.h +++ b/src/dmd/mtype.h @@ -91,6 +91,7 @@ enum ENUMTY Tvector, Tint128, Tuns128, + TTraits, TMAX }; typedef unsigned char TY; // ENUMTY @@ -580,6 +581,17 @@ class TypeDelegate : public TypeNext void accept(Visitor *v) { v->visit(this); } }; +class TypeTraits : public Type +{ + Loc loc; + /// The expression to resolve as type or symbol. + TraitsExp *exp; + /// The symbol when exp doesn't represent a type. + Dsymbol *sym; + Type *syntaxCopy(); + void accept(Visitor *v) { v->visit(this); } +}; + class TypeQualified : public Type { public: diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 57cc2e8c18c4..1962f58a600d 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -568,6 +568,7 @@ final class Parser(AST) : Lexer case TOK.union_: case TOK.class_: case TOK.interface_: + case TOK.traits: Ldeclaration: a = parseDeclarations(false, pAttrs, pAttrs.comment); if (a && a.dim) @@ -3719,6 +3720,27 @@ final class Parser(AST) : Lexer t = parseVector(); 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; + } + else + { + t = new AST.TypeTraits(loc, te); + } + break; + } + case TOK.const_: // const(type) nextToken(); @@ -5394,6 +5416,7 @@ final class Parser(AST) : Lexer case TOK.dot: case TOK.typeof_: case TOK.vector: + case TOK.traits: /* https://issues.dlang.org/show_bug.cgi?id=15163 * If tokens can be handled as * old C-style declaration or D expression, prefer the latter. @@ -5442,7 +5465,6 @@ final class Parser(AST) : Lexer case TOK.typeid_: case TOK.is_: case TOK.leftBracket: - case TOK.traits: case TOK.file: case TOK.fileFullPath: case TOK.line: @@ -6905,6 +6927,25 @@ final class Parser(AST) : Lexer goto Lfalse; goto L3; + case TOK.traits: + // __traits(getMember + t = peek(t); + if (t.value != TOK.leftParentheses) + goto Lfalse; + auto lp = t; + t = peek(t); + if (t.value != TOK.identifier || t.ident != Id.getMember) + goto Lfalse; + if (!skipParens(lp, &lp)) + goto Lfalse; + // we are in a lookup for decl VS statement + // so we expect a declarator following __trait if it's a type. + // other usages wont be ambiguous (alias, template instance, type qual, etc.) + if (lp.value != TOK.identifier) + goto Lfalse; + + break; + case TOK.const_: case TOK.immutable_: case TOK.shared_: @@ -8303,6 +8344,7 @@ final class Parser(AST) : Lexer case TOK.function_: case TOK.delegate_: case TOK.typeof_: + case TOK.traits: case TOK.vector: case TOK.file: case TOK.fileFullPath: diff --git a/src/dmd/parsetimevisitor.d b/src/dmd/parsetimevisitor.d index b958466c9e0e..6a8346b77cc6 100644 --- a/src/dmd/parsetimevisitor.d +++ b/src/dmd/parsetimevisitor.d @@ -142,6 +142,7 @@ public: void visit(AST.TypeStruct t) { visit(cast(AST.Type)t); } void visit(AST.TypeNext t) { visit(cast(AST.Type)t); } void visit(AST.TypeQualified t) { visit(cast(AST.Type)t); } + void visit(AST.TypeTraits t) { visit(cast(AST.Type)t); } // TypeNext void visit(AST.TypeReference t) { visit(cast(AST.TypeNext)t); } diff --git a/src/dmd/strictvisitor.d b/src/dmd/strictvisitor.d index 7fd448fde179..1572869079b2 100644 --- a/src/dmd/strictvisitor.d +++ b/src/dmd/strictvisitor.d @@ -121,6 +121,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.TypeAArray) { assert(0); } override void visit(AST.TypeSArray) { assert(0); } override void visit(AST.TypeQualified) { assert(0); } + override void visit(AST.TypeTraits) { assert(0); } override void visit(AST.TypeIdentifier) { assert(0); } override void visit(AST.TypeReturn) { assert(0); } override void visit(AST.TypeTypeof) { assert(0); } diff --git a/src/dmd/transitivevisitor.d b/src/dmd/transitivevisitor.d index 7cfabc6801eb..486999dfa63d 100644 --- a/src/dmd/transitivevisitor.d +++ b/src/dmd/transitivevisitor.d @@ -459,6 +459,11 @@ package mixin template ParseVisitMethods(AST) t.upr.accept(this); } + override void visit(AST.TypeTraits t) + { + t.exp.accept(this); + } + // Miscellaneous //======================================================== diff --git a/src/dmd/typesem.d b/src/dmd/typesem.d index 9507410b4ed5..8faa5fd29bb0 100644 --- a/src/dmd/typesem.d +++ b/src/dmd/typesem.d @@ -1645,6 +1645,20 @@ private extern (C++) final class TypeSemanticVisitor : Visitor result = t; } + override void visit(TypeTraits mtype) + { + import dmd.traits : semanticTraits; + + result = null; + if (Expression e = semanticTraits(mtype.exp, sc)) + { + if (Dsymbol ds = getDsymbol(e)) + mtype.sym = ds; + else if (Type t = getType(e)) + result = t.addMod(mtype.mod); + } + } + override void visit(TypeReturn mtype) { //printf("TypeReturn::semantic() %s\n", toChars()); diff --git a/src/dmd/visitor.h b/src/dmd/visitor.h index 992488602a0e..cdcea2e8e763 100644 --- a/src/dmd/visitor.h +++ b/src/dmd/visitor.h @@ -81,6 +81,7 @@ class TypeClass; class TypeTuple; class TypeSlice; class TypeNull; +class TypeTraits; class Dsymbol; @@ -427,6 +428,7 @@ class ParseTimeVisitor virtual void visit(TypeStruct *t) { visit((Type *)t); } virtual void visit(TypeNext *t) { visit((Type *)t); } virtual void visit(TypeQualified *t) { visit((Type *)t); } + virtual void visit(TypeTraits *t) { visit((Type *)t); } // TypeNext virtual void visit(TypeReference *t) { visit((TypeNext *)t); } diff --git a/test/fail_compilation/e7804_1.d b/test/fail_compilation/e7804_1.d new file mode 100644 index 000000000000..3975f753e7d2 --- /dev/null +++ b/test/fail_compilation/e7804_1.d @@ -0,0 +1,9 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/e7804_1.d(9): Error: invalid `__traits`, only `getMember` can give types and symbols +--- +*/ +module e7804_1; + +__traits(farfelu, Aggr, "member") a; diff --git a/test/runnable/e7804.d b/test/runnable/e7804.d new file mode 100644 index 000000000000..457277e54026 --- /dev/null +++ b/test/runnable/e7804.d @@ -0,0 +1,47 @@ +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))); + + +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; +} + + + +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;}} +alias m1 = __traits(getMember, WithSym, "foo"); +alias m2 = WithSym.foo; +static assert(__traits(isSame, m1, m2)); +alias f1 = __traits(getMember, WithSym, "bar"); +alias f2 = WithSym.bar; +static assert(__traits(isSame, f1, f2)); + + +void main(){}