diff --git a/changelog/alias_from_trait.dd b/changelog/alias_from_trait.dd index 62233b68f951..ea7733a4918a 100644 --- a/changelog/alias_from_trait.dd +++ b/changelog/alias_from_trait.dd @@ -1,12 +1,15 @@ 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]; @@ -14,16 +17,11 @@ 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. diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index fabe93ca87bf..1ca7526bf49f 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -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; @@ -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 diff --git a/src/dmd/dsymbolsem.d b/src/dmd/dsymbolsem.d index bf0c689ce44a..77a7bfb1a72b 100644 --- a/src/dmd/dsymbolsem.d +++ b/src/dmd/dsymbolsem.d @@ -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) { diff --git a/src/dmd/mtype.d b/src/dmd/mtype.d index bdfa326e5c33..e428a2ff235f 100644 --- a/src/dmd/mtype.d +++ b/src/dmd/mtype.d @@ -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; @@ -5055,6 +5057,11 @@ extern (C++) final class TypeTraits : Type { v.visit(this); } + + override d_uns64 size(const ref Loc loc) + { + return SIZE_INVALID; + } } /*********************************************************** diff --git a/src/dmd/mtype.h b/src/dmd/mtype.h index cf8fdb660f09..166f2e90f935 100644 --- a/src/dmd/mtype.h +++ b/src/dmd/mtype.h @@ -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); } }; diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 1962f58a600d..8b11d2733eb8 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -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) diff --git a/src/dmd/typesem.d b/src/dmd/typesem.d index 8faa5fd29bb0..97005a6f2d62 100644 --- a/src/dmd/typesem.d +++ b/src/dmd/typesem.d @@ -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) diff --git a/test/fail_compilation/e7804_1.d b/test/fail_compilation/e7804_1.d index 3975f753e7d2..38c25fbf7ae6 100644 --- a/test/fail_compilation/e7804_1.d +++ b/test/fail_compilation/e7804_1.d @@ -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"); diff --git a/test/fail_compilation/e7804_2.d b/test/fail_compilation/e7804_2.d new file mode 100644 index 000000000000..2a5353dea95f --- /dev/null +++ b/test/fail_compilation/e7804_2.d @@ -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; +} diff --git a/test/runnable/e7804.d b/test/runnable/e7804.d index 457277e54026..0af30f656e0e 100644 --- a/test/runnable/e7804.d +++ b/test/runnable/e7804.d @@ -1,3 +1,5 @@ +/* REQUIRED_ARGS: -unittest +*/ module e7804; struct Bar {static struct B{}} @@ -5,6 +7,8 @@ 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"); @@ -12,27 +16,24 @@ 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;}} @@ -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(); +}