Skip to content

Commit 57a2891

Browse files
committed
Implement opUDAOn and opChildOfUDAOn
1 parent 5748504 commit 57a2891

File tree

7 files changed

+321
-0
lines changed

7 files changed

+321
-0
lines changed

changelog/dmd.opUDAOn.dd

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
A new way to trigger reflection via UDA
2+
3+
Two new operator overloads are added, these are ``opUDAOn`` and ``opChildOfUDAOn``.
4+
These take the form of the following signature: ``void opUDAOn(alias symbol)()``
5+
6+
They will be called using CTFE to execute them, with the symbol being the declaration that it is on.
7+
For ``opChildOfUDAOn`` instead of the declaration it is on, it works on the children of this declaration.
8+
It works for enums, classes, interfaces, structs, global variables and free-functions.
9+
10+
The following code will print that it is on ``Parent`` and ``freefunction``, and that it is on child ``Child``.
11+
12+
```d
13+
struct UDA {
14+
void opUDAOn(alias symbol)() {
15+
pragma(msg, "On: " ~ __traits(identifier, symbol));
16+
}
17+
18+
void opChildOfUDAOn(alias symbol)() {
19+
pragma(msg, "Child: " ~ __traits(identifier, symbol));
20+
}
21+
}
22+
23+
@UDA
24+
class Parent {
25+
}
26+
27+
class Child : Parent {
28+
}
29+
30+
@UDA
31+
void freefunction() {
32+
}
33+
```

compiler/src/dmd/dsymbolsem.d

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3435,6 +3435,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
34353435

34363436
dsym.semanticRun = PASS.semanticdone;
34373437

3438+
symbolForOpUDAOn(dsym, sc);
3439+
34383440
if (dsym.type.toBasetype().ty == Terror)
34393441
dsym.errors = true;
34403442

@@ -5006,6 +5008,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
50065008
}
50075009
}
50085010

5011+
symbolForOpUDAOn(sd, sc);
5012+
50095013
if (global.errors != errors)
50105014
{
50115015
// The type is no good.
@@ -5892,6 +5896,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
58925896
.error(cldec.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", cldec.kind, cldec.toPrettyChars, cd.loc.toChars());
58935897
}
58945898

5899+
symbolForOpUDAOn(cldec, sc);
5900+
symbolForOpChildOfUDAOn(cldec, sc);
5901+
58955902
if (global.errors != errors || (cldec.baseClass && cldec.baseClass.errors))
58965903
{
58975904
// The type is no good, but we should keep the
@@ -6216,6 +6223,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
62166223

62176224
sc2.pop();
62186225

6226+
symbolForOpUDAOn(idec, sc);
6227+
symbolForOpChildOfUDAOn(idec, sc);
6228+
62196229
if (global.errors != errors)
62206230
{
62216231
// The type is no good.
@@ -10474,3 +10484,121 @@ extern (D) bool oneMembers(Dsymbols* members, out Dsymbol ps, Identifier ident)
1047410484
//printf("\ttrue\n");
1047510485
return true;
1047610486
}
10487+
10488+
// Should be private, but is public due to
10489+
void symbolForOpUDAOn(Dsymbol contextSymbol, Scope* sc, Dsymbol attributeSource=null)
10490+
{
10491+
import dmd.opover : search_function;
10492+
10493+
if (contextSymbol.inNonRoot)
10494+
return;
10495+
10496+
if (attributeSource is null)
10497+
attributeSource = contextSymbol;
10498+
10499+
Identifier usingOp = attributeSource is contextSymbol ? Id.opUDAOn : Id.opChildOfUDAOn;
10500+
10501+
void handle(Expression expr)
10502+
{
10503+
Objects* tiobjects = new Objects;
10504+
tiobjects.push(contextSymbol);
10505+
10506+
auto dtie = new DotTemplateInstanceExp(contextSymbol.loc, expr, usingOp, tiobjects);
10507+
expr = dtie.expressionSemantic(sc);
10508+
10509+
if (auto dve = expr.isDotVarExp)
10510+
{
10511+
if (dve.var.isFuncDeclaration)
10512+
{
10513+
expr = new CallExp(contextSymbol.loc, expr);
10514+
expr = expr.expressionSemantic(sc);
10515+
expr = ctfeInterpret(expr);
10516+
}
10517+
}
10518+
}
10519+
10520+
void handleType(TypeExp te)
10521+
{
10522+
if (auto ts = te.type.isTypeStruct)
10523+
{
10524+
if (ts.sym is null)
10525+
return;
10526+
10527+
Dsymbol dsym2 = search_function(ts.sym, usingOp);
10528+
if (dsym2 is null)
10529+
return;
10530+
10531+
handle(new DotIdExp(te.loc, te, Id._init));
10532+
}
10533+
}
10534+
10535+
void handleStructLiteral(StructLiteralExp sle)
10536+
{
10537+
Dsymbol dsym2 = search_function(sle.sd, usingOp);
10538+
if (dsym2 is null)
10539+
return;
10540+
10541+
handle(sle);
10542+
}
10543+
10544+
void dispatch(Expression expr) {
10545+
if (auto sle = expr.isStructLiteralExp)
10546+
handleStructLiteral(sle);
10547+
else if (auto te = expr.isTypeExp)
10548+
handleType(te);
10549+
}
10550+
10551+
if (auto vd = attributeSource.isVarDeclaration)
10552+
{
10553+
if (vd.needThis)
10554+
return; // This will trigger dual-context.
10555+
}
10556+
10557+
if (auto attrs = attributeSource.userAttribDecl())
10558+
{
10559+
foreach(attr2; *attrs.atts)
10560+
{
10561+
if (auto te = attr2.isTupleExp)
10562+
{
10563+
// te.e0 should be null here
10564+
10565+
if (te.exps is null)
10566+
continue;
10567+
10568+
foreach(attr3; *te.exps)
10569+
dispatch(attr3);
10570+
}
10571+
else
10572+
dispatch(attr2);
10573+
}
10574+
}
10575+
}
10576+
10577+
void symbolForOpChildOfUDAOn(ClassDeclaration cd, Scope* sc, ClassDeclaration contextSymbol = null)
10578+
{
10579+
if (cd.baseclasses is null)
10580+
return;
10581+
10582+
if (contextSymbol is null)
10583+
contextSymbol = cd;
10584+
10585+
if (contextSymbol.inNonRoot)
10586+
return;
10587+
10588+
// Okay this may not make much sense.
10589+
// On a class declaration the base classes include both interfaces and classes,
10590+
// but they won't be normalized to include all parents.
10591+
// Instead we first check the base classes list,
10592+
// and then for each of the interfaces that they inherit we go ahead and see both it, and its parents.
10593+
10594+
foreach(base; *cd.baseclasses)
10595+
{
10596+
symbolForOpUDAOn(contextSymbol, sc, base.sym);
10597+
10598+
foreach(ref bi; base.baseInterfaces)
10599+
{
10600+
symbolForOpUDAOn(contextSymbol, sc, bi.sym);
10601+
symbolForOpChildOfUDAOn(bi.sym, sc, contextSymbol);
10602+
}
10603+
}
10604+
}

compiler/src/dmd/enumsem.d

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ void enumSemantic(Scope* sc, EnumDeclaration ed)
252252
// Set semantic2done here to indicate all members have been processed
253253
// This prevents using the enum in a final switch while being defined
254254
ed.semanticRun = PASS.semantic2done;
255+
256+
symbolForOpUDAOn(ed, sc);
255257
}
256258

257259
Expression getDefaultValue(EnumDeclaration ed, Loc loc)

compiler/src/dmd/frontend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8616,6 +8616,8 @@ struct Id final
86168616
static Identifier* opOpAssign;
86178617
static Identifier* opIndexOpAssign;
86188618
static Identifier* opSliceOpAssign;
8619+
static Identifier* opUDAOn;
8620+
static Identifier* opChildOfUDAOn;
86198621
static Identifier* classNew;
86208622
static Identifier* classDelete;
86218623
static Identifier* apply;

compiler/src/dmd/funcsem.d

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,8 @@ Ldone:
888888

889889
funcdecl.semanticRun = PASS.semanticdone;
890890

891+
symbolForOpUDAOn(funcdecl, sc);
892+
891893
/* Save scope for possible later use (if we need the
892894
* function internals)
893895
*/

compiler/src/dmd/id.d

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ immutable Msgtable[] msgtable =
244244
{ "opOpAssign" },
245245
{ "opIndexOpAssign" },
246246
{ "opSliceOpAssign" },
247+
{ "opUDAOn" },
248+
{ "opChildOfUDAOn" },
247249

248250
{ "classNew", "new" },
249251
{ "classDelete", "delete" },

compiler/test/compilable/opUDAOn.d

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
TEST_OUTPUT:
3+
---
4+
On: globala
5+
On: globalb
6+
On: globalc
7+
On: freefunctiona
8+
On: freefunctionb
9+
On: sfield1a
10+
On: method1a
11+
On: Ca
12+
On: sfield1b
13+
On: method1b
14+
On: Cb
15+
On: sfield2a
16+
On: method2a
17+
On: Sa
18+
On: sfield2b
19+
On: method2b
20+
On: Sb
21+
On: method3a
22+
On: Ia
23+
On: method3b
24+
On: Ib
25+
On: C_Parent
26+
Child: C_Child
27+
On: E
28+
On: I_Parent
29+
Child: I_Child
30+
Child: C_I_Child
31+
Child: C_I2_Child
32+
---
33+
*/
34+
35+
string[] caught1, caught2;
36+
37+
struct UDA {
38+
void opUDAOn(alias symbol)() {
39+
pragma(msg, "On: ", __traits(identifier, symbol));
40+
}
41+
42+
void opChildOfUDAOn(alias symbol)() {
43+
pragma(msg, "Child: ", __traits(identifier, symbol));
44+
}
45+
}
46+
47+
@UDA
48+
int globala;
49+
50+
@UDA()
51+
int globalb;
52+
53+
@UDA
54+
@UDA()
55+
int globalc;
56+
57+
@UDA
58+
void freefunctiona() {
59+
}
60+
61+
@UDA()
62+
void freefunctionb() {
63+
}
64+
65+
@UDA
66+
class Ca {
67+
@UDA
68+
int field1a;
69+
70+
@UDA
71+
static int sfield1a;
72+
73+
@UDA
74+
void method1a() {
75+
}
76+
}
77+
78+
@UDA()
79+
class Cb {
80+
@UDA()
81+
int field1b;
82+
83+
@UDA()
84+
static int sfield1b;
85+
86+
@UDA()
87+
void method1b() {
88+
}
89+
}
90+
91+
@UDA
92+
struct Sa {
93+
@UDA
94+
int field2a;
95+
96+
@UDA
97+
static int sfield2a;
98+
99+
@UDA
100+
void method2a() {
101+
}
102+
}
103+
104+
@UDA()
105+
struct Sb {
106+
@UDA()
107+
int field2b;
108+
109+
@UDA()
110+
static int sfield2b;
111+
112+
@UDA()
113+
void method2b() {
114+
}
115+
}
116+
117+
@UDA
118+
interface Ia {
119+
@UDA
120+
void method3a();
121+
}
122+
123+
@UDA()
124+
interface Ib {
125+
@UDA()
126+
void method3b();
127+
}
128+
129+
@UDA
130+
class C_Parent {
131+
}
132+
133+
class C_Child : C_Parent {
134+
}
135+
136+
@UDA
137+
enum E {
138+
A
139+
}
140+
141+
@UDA
142+
interface I_Parent {
143+
}
144+
145+
interface I_Child : I_Parent {
146+
}
147+
148+
class C_I_Child : I_Parent {
149+
}
150+
151+
class C_I2_Child : I_Child {
152+
}

0 commit comments

Comments
 (0)