diff --git a/changelog/dmd.opUDAOn.dd b/changelog/dmd.opUDAOn.dd new file mode 100644 index 000000000000..c717ce44713a --- /dev/null +++ b/changelog/dmd.opUDAOn.dd @@ -0,0 +1,33 @@ +A new way to trigger reflection via UDA + +Two new operator overloads are added, these are ``opUDAOn`` and ``opChildOfUDAOn``. +These take the form of the following signature: ``void opUDAOn(alias symbol)()`` + +They will be called using CTFE to execute them, with the symbol being the declaration that it is on. +For ``opChildOfUDAOn`` instead of the declaration it is on, it works on the children of this declaration. +It works for enums, classes, interfaces, structs, global variables and free-functions. + +The following code will print that it is on ``Parent`` and ``freefunction``, and that it is on child ``Child``. + +```d +struct UDA { + void opUDAOn(alias symbol)() { + pragma(msg, "On: " ~ __traits(identifier, symbol)); + } + + void opChildOfUDAOn(alias symbol)() { + pragma(msg, "Child: " ~ __traits(identifier, symbol)); + } +} + +@UDA +class Parent { +} + +class Child : Parent { +} + +@UDA +void freefunction() { +} +``` diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index 36ce6ce40a7c..604ff134ebf9 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -3435,6 +3435,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor dsym.semanticRun = PASS.semanticdone; + symbolForOpUDAOn(dsym, sc); + if (dsym.type.toBasetype().ty == Terror) dsym.errors = true; @@ -5006,6 +5008,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } + symbolForOpUDAOn(sd, sc); + if (global.errors != errors) { // The type is no good. @@ -5892,6 +5896,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor .error(cldec.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", cldec.kind, cldec.toPrettyChars, cd.loc.toChars()); } + symbolForOpUDAOn(cldec, sc); + symbolForOpChildOfUDAOn(cldec, sc); + if (global.errors != errors || (cldec.baseClass && cldec.baseClass.errors)) { // The type is no good, but we should keep the @@ -6216,6 +6223,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc2.pop(); + symbolForOpUDAOn(idec, sc); + symbolForOpChildOfUDAOn(idec, sc); + if (global.errors != errors) { // The type is no good. @@ -10474,3 +10484,121 @@ extern (D) bool oneMembers(Dsymbols* members, out Dsymbol ps, Identifier ident) //printf("\ttrue\n"); return true; } + +// Should be private, but is public due to +void symbolForOpUDAOn(Dsymbol contextSymbol, Scope* sc, Dsymbol attributeSource=null) +{ + import dmd.opover : search_function; + + if (contextSymbol.inNonRoot) + return; + + if (attributeSource is null) + attributeSource = contextSymbol; + + Identifier usingOp = attributeSource is contextSymbol ? Id.opUDAOn : Id.opChildOfUDAOn; + + void handle(Expression expr) + { + Objects* tiobjects = new Objects; + tiobjects.push(contextSymbol); + + auto dtie = new DotTemplateInstanceExp(contextSymbol.loc, expr, usingOp, tiobjects); + expr = dtie.expressionSemantic(sc); + + if (auto dve = expr.isDotVarExp) + { + if (dve.var.isFuncDeclaration) + { + expr = new CallExp(contextSymbol.loc, expr); + expr = expr.expressionSemantic(sc); + expr = ctfeInterpret(expr); + } + } + } + + void handleType(TypeExp te) + { + if (auto ts = te.type.isTypeStruct) + { + if (ts.sym is null) + return; + + Dsymbol dsym2 = search_function(ts.sym, usingOp); + if (dsym2 is null) + return; + + handle(new DotIdExp(te.loc, te, Id._init)); + } + } + + void handleStructLiteral(StructLiteralExp sle) + { + Dsymbol dsym2 = search_function(sle.sd, usingOp); + if (dsym2 is null) + return; + + handle(sle); + } + + void dispatch(Expression expr) { + if (auto sle = expr.isStructLiteralExp) + handleStructLiteral(sle); + else if (auto te = expr.isTypeExp) + handleType(te); + } + + if (auto vd = attributeSource.isVarDeclaration) + { + if (vd.needThis) + return; // This will trigger dual-context. + } + + if (auto attrs = attributeSource.userAttribDecl()) + { + foreach(attr2; *attrs.atts) + { + if (auto te = attr2.isTupleExp) + { + // te.e0 should be null here + + if (te.exps is null) + continue; + + foreach(attr3; *te.exps) + dispatch(attr3); + } + else + dispatch(attr2); + } + } +} + +void symbolForOpChildOfUDAOn(ClassDeclaration cd, Scope* sc, ClassDeclaration contextSymbol = null) +{ + if (cd.baseclasses is null) + return; + + if (contextSymbol is null) + contextSymbol = cd; + + if (contextSymbol.inNonRoot) + return; + + // Okay this may not make much sense. + // On a class declaration the base classes include both interfaces and classes, + // but they won't be normalized to include all parents. + // Instead we first check the base classes list, + // and then for each of the interfaces that they inherit we go ahead and see both it, and its parents. + + foreach(base; *cd.baseclasses) + { + symbolForOpUDAOn(contextSymbol, sc, base.sym); + + foreach(ref bi; base.baseInterfaces) + { + symbolForOpUDAOn(contextSymbol, sc, bi.sym); + symbolForOpChildOfUDAOn(bi.sym, sc, contextSymbol); + } + } +} diff --git a/compiler/src/dmd/enumsem.d b/compiler/src/dmd/enumsem.d index 1710172f54e0..1a0196d6e8c1 100644 --- a/compiler/src/dmd/enumsem.d +++ b/compiler/src/dmd/enumsem.d @@ -252,6 +252,8 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) // Set semantic2done here to indicate all members have been processed // This prevents using the enum in a final switch while being defined ed.semanticRun = PASS.semantic2done; + + symbolForOpUDAOn(ed, sc); } Expression getDefaultValue(EnumDeclaration ed, Loc loc) diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 8caab59eb5a7..93d499aa38f3 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -8616,6 +8616,8 @@ struct Id final static Identifier* opOpAssign; static Identifier* opIndexOpAssign; static Identifier* opSliceOpAssign; + static Identifier* opUDAOn; + static Identifier* opChildOfUDAOn; static Identifier* classNew; static Identifier* classDelete; static Identifier* apply; diff --git a/compiler/src/dmd/funcsem.d b/compiler/src/dmd/funcsem.d index 0935bdc9ee17..2bd19b4913f6 100644 --- a/compiler/src/dmd/funcsem.d +++ b/compiler/src/dmd/funcsem.d @@ -888,6 +888,8 @@ Ldone: funcdecl.semanticRun = PASS.semanticdone; + symbolForOpUDAOn(funcdecl, sc); + /* Save scope for possible later use (if we need the * function internals) */ diff --git a/compiler/src/dmd/id.d b/compiler/src/dmd/id.d index 49f47e4a9a0a..60c40a27eee0 100644 --- a/compiler/src/dmd/id.d +++ b/compiler/src/dmd/id.d @@ -244,6 +244,8 @@ immutable Msgtable[] msgtable = { "opOpAssign" }, { "opIndexOpAssign" }, { "opSliceOpAssign" }, + { "opUDAOn" }, + { "opChildOfUDAOn" }, { "classNew", "new" }, { "classDelete", "delete" }, diff --git a/compiler/test/compilable/opUDAOn.d b/compiler/test/compilable/opUDAOn.d new file mode 100644 index 000000000000..019f2d6b6f52 --- /dev/null +++ b/compiler/test/compilable/opUDAOn.d @@ -0,0 +1,152 @@ +/* +TEST_OUTPUT: +--- +On: globala +On: globalb +On: globalc +On: freefunctiona +On: freefunctionb +On: sfield1a +On: method1a +On: Ca +On: sfield1b +On: method1b +On: Cb +On: sfield2a +On: method2a +On: Sa +On: sfield2b +On: method2b +On: Sb +On: method3a +On: Ia +On: method3b +On: Ib +On: C_Parent +Child: C_Child +On: E +On: I_Parent +Child: I_Child +Child: C_I_Child +Child: C_I2_Child +--- +*/ + +string[] caught1, caught2; + +struct UDA { + void opUDAOn(alias symbol)() { + pragma(msg, "On: ", __traits(identifier, symbol)); + } + + void opChildOfUDAOn(alias symbol)() { + pragma(msg, "Child: ", __traits(identifier, symbol)); + } +} + +@UDA +int globala; + +@UDA() +int globalb; + +@UDA +@UDA() +int globalc; + +@UDA +void freefunctiona() { +} + +@UDA() +void freefunctionb() { +} + +@UDA +class Ca { + @UDA + int field1a; + + @UDA + static int sfield1a; + + @UDA + void method1a() { + } +} + +@UDA() +class Cb { + @UDA() + int field1b; + + @UDA() + static int sfield1b; + + @UDA() + void method1b() { + } +} + +@UDA +struct Sa { + @UDA + int field2a; + + @UDA + static int sfield2a; + + @UDA + void method2a() { + } +} + +@UDA() +struct Sb { + @UDA() + int field2b; + + @UDA() + static int sfield2b; + + @UDA() + void method2b() { + } +} + +@UDA +interface Ia { + @UDA + void method3a(); +} + +@UDA() +interface Ib { + @UDA() + void method3b(); +} + +@UDA +class C_Parent { +} + +class C_Child : C_Parent { +} + +@UDA +enum E { + A +} + +@UDA +interface I_Parent { +} + +interface I_Child : I_Parent { +} + +class C_I_Child : I_Parent { +} + +class C_I2_Child : I_Child { +}