Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions changelog/dmd.opUDAOn.dd
Original file line number Diff line number Diff line change
@@ -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() {
}
```
128 changes: 128 additions & 0 deletions compiler/src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -5006,6 +5008,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
}

symbolForOpUDAOn(sd, sc);

if (global.errors != errors)
{
// The type is no good.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
}
}
2 changes: 2 additions & 0 deletions compiler/src/dmd/enumsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/funcsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,8 @@ Ldone:

funcdecl.semanticRun = PASS.semanticdone;

symbolForOpUDAOn(funcdecl, sc);

/* Save scope for possible later use (if we need the
* function internals)
*/
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/id.d
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ immutable Msgtable[] msgtable =
{ "opOpAssign" },
{ "opIndexOpAssign" },
{ "opSliceOpAssign" },
{ "opUDAOn" },
{ "opChildOfUDAOn" },

{ "classNew", "new" },
{ "classDelete", "delete" },
Expand Down
152 changes: 152 additions & 0 deletions compiler/test/compilable/opUDAOn.d
Original file line number Diff line number Diff line change
@@ -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 {
}
Loading