diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index c8001d010901..19732e71f406 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -4085,6 +4085,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 82648547a9e0..aec6d8a47899 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/hdrgen.d b/src/dmd/hdrgen.d index 208545ee061f..02a983786e32 100644 --- a/src/dmd/hdrgen.d +++ b/src/dmd/hdrgen.d @@ -784,6 +784,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 7534b39b4b0d..ed8942fd443c 100644 --- a/src/dmd/mtype.d +++ b/src/dmd/mtype.d @@ -334,6 +334,7 @@ enum ENUMTY : int Tvector, Tint128, Tuns128, + TTraits, TMAX, } @@ -381,6 +382,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; @@ -512,6 +514,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; }(); @@ -5240,6 +5243,41 @@ 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; + + final extern (D) this(Loc loc, TraitsExp exp) + { + super(Ttraits); + this.loc = loc; + this.exp = exp; + } + + final 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/parse.d b/src/dmd/parse.d index 4de9d69767f9..b1ca2cb58f54 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -513,6 +513,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) @@ -3495,6 +3496,28 @@ 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 be aliased and not `%s`", + te.ident.toChars); + t = new AST.TypeError; + } + else + { + t = new AST.TypeTraits(loc, te); + } + break; + } + case TOK.const_: // const(type) nextToken(); @@ -6502,6 +6525,16 @@ final class Parser(AST) : Lexer goto Lfalse; goto L3; + case TOK.traits: + // __traits(getMember + t = peek(t); + if (t.value != TOK.leftParentheses) + goto Lfalse; + t = peek(t); + if (t.value != TOK.identifier || t.ident != Id.getMember) + goto Lfalse; + break; + case TOK.const_: case TOK.immutable_: case TOK.shared_: diff --git a/src/dmd/parsetimevisitor.d b/src/dmd/parsetimevisitor.d index 6b1d3673453b..0f95118dd311 100644 --- a/src/dmd/parsetimevisitor.d +++ b/src/dmd/parsetimevisitor.d @@ -138,6 +138,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 442bd40a8416..9cbae6a450b9 100644 --- a/src/dmd/strictvisitor.d +++ b/src/dmd/strictvisitor.d @@ -120,6 +120,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 c335d1303040..a225a692ebf0 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 972847d98f34..ae614753297e 100644 --- a/src/dmd/typesem.d +++ b/src/dmd/typesem.d @@ -1307,6 +1307,21 @@ private extern (C++) final class TypeSemanticVisitor : Visitor result = t; } + override void visit(TypeTraits mtype) + { + import dmd.traits : semanticTraits; + import dmd.dtemplate : getType; + + result = null; + if (Expression e = semanticTraits(mtype.exp, sc)) + if (Type t = getType(e)) + result = t.addMod(mtype.mod); + //if (result) printf("TypeTraits found to be %s\n".ptr, result.toChars); + if (result is null) + mtype.error(loc, "`%s` cannot be resolved to a valid type", + mtype.toChars()); + } + override void visit(TypeReturn mtype) { //printf("TypeReturn::semantic() %s\n", toChars()); diff --git a/test/fail_compilation/e7804_1.d b/test/fail_compilation/e7804_1.d new file mode 100644 index 000000000000..f3f5659d9c1e --- /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 be aliased and not `farfelu` +--- +*/ +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..432c91aedff9 --- /dev/null +++ b/test/runnable/e7804.d @@ -0,0 +1,32 @@ +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); + + +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);} + + +void main(){}