From 887557562ef3b16e404de8b1c964ba638cff5f83 Mon Sep 17 00:00:00 2001 From: k-hara Date: Sun, 4 Oct 2015 23:49:02 +0900 Subject: [PATCH 1/3] Resurrect line spaces in denum.d --- src/denum.d | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/denum.d b/src/denum.d index 89fc7c5d3182..251aa7770062 100644 --- a/src/denum.d +++ b/src/denum.d @@ -78,15 +78,18 @@ public: printf(" member %s\n", em.toChars()); } } + /* Anonymous enum members get added to enclosing scope. */ ScopeDsymbol scopesym = isAnonymous() ? sds : this; + if (!isAnonymous()) { ScopeDsymbol.addMember(sc, sds); if (!symtab) symtab = new DsymbolTable(); } + if (members) { for (size_t i = 0; i < members.dim; i++) @@ -122,6 +125,7 @@ public: return; } uint dprogress_save = Module.dprogress; + Scope* scx = null; if (_scope) { @@ -129,20 +133,26 @@ public: scx = _scope; // save so we don't make redundant copies _scope = null; } + parent = sc.parent; type = type.semantic(loc, sc); + protection = sc.protection; if (sc.stc & STCdeprecated) isdeprecated = true; userAttribDecl = sc.userAttribDecl; + semanticRun = PASSsemantic; + if (!members && !memtype) // enum ident; { semanticRun = PASSsemanticdone; return; } + if (!symtab) symtab = new DsymbolTable(); + /* The separate, and distinct, cases are: * 1. enum { ... } * 2. enum : memtype { ... } @@ -151,9 +161,11 @@ public: * 5. enum ident : memtype; * 6. enum ident; */ + if (memtype) { memtype = memtype.semantic(loc, sc); + /* Check to see if memtype is forward referenced */ if (memtype.ty == Tenum) @@ -191,16 +203,21 @@ public: return; } } + semanticRun = PASSsemanticdone; + if (!members) // enum ident : memtype; return; + if (members.dim == 0) { error("enum %s must have at least one member", toChars()); errors = true; return; } + Module.dprogress++; + Scope* sce; if (isAnonymous()) sce = sc; @@ -211,6 +228,7 @@ public: } sce = sce.startCTFE(); sce.setNoFree(); // needed for getMaxMinValue() + /* Each enum member gets the sce scope */ for (size_t i = 0; i < members.dim; i++) @@ -219,6 +237,7 @@ public: if (em) em._scope = sce; } + if (!added) { /* addMember() is not called when the EnumDeclaration appears as a function statement, @@ -247,6 +266,7 @@ public: // Otherwise enum members are in the EnumDeclaration's symbol table scopesym = this; } + for (size_t i = 0; i < members.dim; i++) { EnumMember em = (*members)[i].isEnumMember(); @@ -257,6 +277,7 @@ public: } } } + for (size_t i = 0; i < members.dim; i++) { EnumMember em = (*members)[i].isEnumMember(); @@ -264,6 +285,7 @@ public: em.semantic(em._scope); } //printf("defaultval = %lld\n", defaultval); + //if (defaultval) printf("defaultval: %s %s\n", defaultval->toChars(), defaultval->type->toChars()); //printf("members = %s\n", members->toChars()); } @@ -293,12 +315,14 @@ public: // Try one last time to resolve this enum semantic(_scope); } + if (!members || !symtab || _scope) { error("is forward referenced when looking for '%s'", ident.toChars()); //*(char*)0=0; return null; } + Dsymbol s = ScopeDsymbol.search(loc, ident, flags); return s; } @@ -323,7 +347,9 @@ public: { //printf("EnumDeclaration::getMaxValue()\n"); bool first = true; + Expression* pval = (id == Id.max) ? &maxval : &minval; + if (inuse) { error(loc, "recursive definition of .%s property", id.toChars()); @@ -331,6 +357,7 @@ public: } if (*pval) goto Ldone; + if (_scope) semantic(_scope); if (errors) @@ -345,6 +372,7 @@ public: error(loc, "has no .%s property because base type %s is not an integral type", id.toChars(), memtype ? memtype.toChars() : ""); goto Lerrors; } + for (size_t i = 0; i < members.dim; i++) { EnumMember em = (*members)[i].isEnumMember(); @@ -352,6 +380,7 @@ public: continue; if (em.errors) goto Lerrors; + Expression e = em.value; if (first) { @@ -365,6 +394,7 @@ public: * and let the semantic analyzer and constant * folder give us the result. */ + /* Compute: * if (e > maxval) * maxval = e; @@ -388,6 +418,7 @@ public: } return e; } + Lerrors: *pval = new ErrorExp(); return *pval; @@ -398,6 +429,7 @@ public: //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); if (defaultval) return defaultval; + if (_scope) semantic(_scope); if (errors) @@ -407,6 +439,7 @@ public: error(loc, "forward reference of %s.init", toChars()); goto Lerrors; } + for (size_t i = 0; i < members.dim; i++) { EnumMember em = (*members)[i].isEnumMember(); @@ -415,6 +448,7 @@ public: defaultval = em.value; return defaultval; } + Lerrors: defaultval = new ErrorExp(); return defaultval; @@ -524,16 +558,20 @@ public: goto Lerrors; if (errors || semanticRun >= PASSsemanticdone) return; + semanticRun = PASSsemantic; if (_scope) sc = _scope; + // The first enum member is special bool first = (this == (*ed.members)[0]); + if (type) { type = type.semantic(loc, sc); assert(value); // "type id;" is not a valid enum member declaration } + if (value) { Expression e = value; @@ -562,6 +600,7 @@ public: EnumMember em = (*ed.members)[i].isEnumMember(); if (!em || em == this || em.semanticRun < PASSsemanticdone || em.type) continue; + //printf("[%d] em = %s, em->semanticRun = %d\n", i, toChars(), em->semanticRun); Expression ev = em.value; ev = ev.implicitCastTo(sc, ed.memtype); @@ -578,12 +617,15 @@ public: } } } + if (ed.memtype && !type) { e = e.implicitCastTo(sc, ed.memtype); e = e.ctfeInterpret(); + // save origValue for better json output origValue = e; + if (!ed.isAnonymous()) e = e.castTo(sc, ed.type); } @@ -592,6 +634,7 @@ public: e = e.implicitCastTo(sc, type); e = e.ctfeInterpret(); assert(ed.isAnonymous()); + // save origValue for better json output origValue = e; } @@ -611,8 +654,10 @@ public: Expression e = new IntegerExp(loc, 0, Type.tint32); e = e.implicitCastTo(sc, t); e = e.ctfeInterpret(); + // save origValue for better json output origValue = e; + if (!ed.isAnonymous()) e = e.castTo(sc, ed.type); value = e; @@ -638,11 +683,14 @@ public: emprev.semantic(emprev._scope); // resolve it if (emprev.errors) goto Lerrors; + Expression eprev = emprev.value; Type tprev = eprev.type.equals(ed.type) ? ed.memtype : eprev.type; + Expression emax = tprev.getProperty(ed.loc, Id.max, 0); emax = emax.semantic(sc); emax = emax.ctfeInterpret(); + // Set value to (eprev + 1). // But first check that (eprev != emax) assert(eprev); @@ -660,6 +708,7 @@ public: e = e.semantic(sc); e = e.castTo(sc, eprev.type); e = e.ctfeInterpret(); + // save origValue (without cast) for better json output if (e.op != TOKerror) // avoid duplicate diagnostics { @@ -668,6 +717,7 @@ public: origValue = origValue.semantic(sc); origValue = origValue.ctfeInterpret(); } + if (e.op == TOKerror) goto Lerrors; if (e.type.isfloating()) @@ -684,6 +734,7 @@ public: } value = e; } + assert(origValue); semanticRun = PASSsemanticdone; } @@ -697,8 +748,10 @@ public: { assert(value); vd = new VarDeclaration(loc, type, ident, new ExpInitializer(loc, value.copy())); + vd.storage_class = STCmanifest; vd.semantic(sc); + vd.protection = ed.isAnonymous() ? ed.protection : Prot(PROTpublic); vd.parent = ed.isAnonymous() ? ed.parent : ed; vd.userAttribDecl = ed.isAnonymous() ? ed.userAttribDecl : null; From 9e1e61d6e9d7713f8d64def9c468c0771bb6efff Mon Sep 17 00:00:00 2001 From: k-hara Date: Sun, 4 Oct 2015 23:52:08 +0900 Subject: [PATCH 2/3] fix Issue 15150 - Public selective import causes conflict By making `EnumMember` to the subclass of `VarDeclaration`, the symbol itself can be representation of the enum member name. --- src/declaration.d | 2 +- src/denum.d | 60 +++++++++++++--------------- src/enum.h | 11 ++--- src/iasm.c | 2 +- src/json.d | 4 +- src/mtype.d | 39 ++++++++++-------- src/tocvdebug.c | 4 +- src/visitor.d | 2 +- src/visitor.h | 2 +- test/compilable/imports/test15150a.d | 6 +++ test/compilable/imports/test15150b.d | 3 ++ test/compilable/test15150.d | 8 ++++ test/fail_compilation/fail10528.d | 4 +- 13 files changed, 83 insertions(+), 64 deletions(-) create mode 100644 test/compilable/imports/test15150a.d create mode 100644 test/compilable/imports/test15150b.d create mode 100644 test/compilable/test15150.d diff --git a/src/declaration.d b/src/declaration.d index eb378631baf5..89571fed1d47 100644 --- a/src/declaration.d +++ b/src/declaration.d @@ -1898,7 +1898,7 @@ public: sem = Semantic2Done; } - override final const(char)* kind() + override const(char)* kind() { return "variable"; } diff --git a/src/denum.d b/src/denum.d index 251aa7770062..fa6797a30fe8 100644 --- a/src/denum.d +++ b/src/denum.d @@ -8,6 +8,7 @@ module ddmd.denum; +import core.stdc.stdio; import ddmd.access; import ddmd.backend; import ddmd.declaration; @@ -97,7 +98,7 @@ public: EnumMember em = (*members)[i].isEnumMember(); em.ed = this; //printf("add %s to scope %s\n", em->toChars(), scopesym->toChars()); - em.addMember(sc, scopesym); + em.addMember(sc, isAnonymous() ? scopesym : this); } } added = true; @@ -499,7 +500,7 @@ public: /*********************************************************** */ -extern (C++) final class EnumMember : Dsymbol +extern (C++) final class EnumMember : VarDeclaration { public: /* Can take the following forms: @@ -507,31 +508,28 @@ public: * 2. id = value * 3. type id = value */ - Expression value; + @property ref value() { return (cast(ExpInitializer)_init).exp; } // A cast() is injected to 'value' after semantic(), // but 'origValue' will preserve the original value, // or previous value + 1 if none was specified. Expression origValue; - Type type; + Type origType; EnumDeclaration ed; - VarDeclaration vd; - extern (D) this(Loc loc, Identifier id, Expression value, Type type) + extern (D) this(Loc loc, Identifier id, Expression value, Type origType) { - super(id); - this.value = value; + super(loc, null, id ? id : Id.empty, new ExpInitializer(loc, value)); this.origValue = value; - this.type = type; - this.loc = loc; + this.origType = origType; } override Dsymbol syntaxCopy(Dsymbol s) { assert(!s); - return new EnumMember(loc, ident, value ? value.syntaxCopy() : null, type ? type.syntaxCopy() : null); + return new EnumMember(loc, ident, value ? value.syntaxCopy() : null, origType ? origType.syntaxCopy() : null); } override const(char)* kind() @@ -559,16 +557,22 @@ public: if (errors || semanticRun >= PASSsemanticdone) return; - semanticRun = PASSsemantic; if (_scope) sc = _scope; + protection = ed.isAnonymous() ? ed.protection : Prot(PROTpublic); + storage_class = STCmanifest; + userAttribDecl = ed.isAnonymous() ? ed.userAttribDecl : null; + + semanticRun = PASSsemantic; + // The first enum member is special bool first = (this == (*ed.members)[0]); - if (type) + if (origType) { - type = type.semantic(loc, sc); + origType = origType.semantic(loc, sc); + type = origType; assert(value); // "type id;" is not a valid enum member declaration } @@ -598,10 +602,10 @@ public: for (size_t i = 0; i < ed.members.dim; i++) { EnumMember em = (*ed.members)[i].isEnumMember(); - if (!em || em == this || em.semanticRun < PASSsemanticdone || em.type) + if (!em || em == this || em.semanticRun < PASSsemanticdone || em.origType) continue; - //printf("[%d] em = %s, em->semanticRun = %d\n", i, toChars(), em->semanticRun); + //printf("[%d] em = %s, em.semanticRun = %d\n", i, toChars(), em.semanticRun); Expression ev = em.value; ev = ev.implicitCastTo(sc, ed.memtype); ev = ev.ctfeInterpret(); @@ -618,7 +622,7 @@ public: } } - if (ed.memtype && !type) + if (ed.memtype && !origType) { e = e.implicitCastTo(sc, ed.memtype); e = e.ctfeInterpret(); @@ -629,9 +633,9 @@ public: if (!ed.isAnonymous()) e = e.castTo(sc, ed.type); } - else if (type) + else if (origType) { - e = e.implicitCastTo(sc, type); + e = e.implicitCastTo(sc, origType); e = e.ctfeInterpret(); assert(ed.isAnonymous()); @@ -703,6 +707,7 @@ public: emprev.ed.toChars(), emprev.toChars(), ed.memtype.toChars()); goto Lerrors; } + // Now set e to (eprev + 1) e = new AddExp(loc, eprev, new IntegerExp(loc, 1, Type.tint32)); e = e.semantic(sc); @@ -734,6 +739,8 @@ public: } value = e; } + if (!origType) + type = value.type; assert(origValue); semanticRun = PASSsemanticdone; @@ -744,20 +751,7 @@ public: semantic(sc); if (errors) return new ErrorExp(); - if (!vd) - { - assert(value); - vd = new VarDeclaration(loc, type, ident, new ExpInitializer(loc, value.copy())); - - vd.storage_class = STCmanifest; - vd.semantic(sc); - - vd.protection = ed.isAnonymous() ? ed.protection : Prot(PROTpublic); - vd.parent = ed.isAnonymous() ? ed.parent : ed; - vd.userAttribDecl = ed.isAnonymous() ? ed.userAttribDecl : null; - } - checkAccess(loc, sc, null, vd); - Expression e = new VarExp(loc, vd); + Expression e = new VarExp(loc, this); return e.semantic(sc); } diff --git a/src/enum.h b/src/enum.h index 3abb79e47ac1..3cfa200d181f 100644 --- a/src/enum.h +++ b/src/enum.h @@ -18,6 +18,7 @@ #include "root.h" #include "dsymbol.h" +#include "declaration.h" #include "tokens.h" class Identifier; @@ -70,7 +71,7 @@ class EnumDeclaration : public ScopeDsymbol }; -class EnumMember : public Dsymbol +class EnumMember : public VarDeclaration { public: /* Can take the following forms: @@ -78,17 +79,17 @@ class EnumMember : public Dsymbol * 2. id = value * 3. type id = value */ - Expression *value; + Expression *&value(); + // A cast() is injected to 'value' after semantic(), // but 'origValue' will preserve the original value, // or previous value + 1 if none was specified. Expression *origValue; - Type *type; + Type *origType; EnumDeclaration *ed; - VarDeclaration *vd; - EnumMember(Loc loc, Identifier *id, Expression *value, Type *type); + EnumMember(Loc loc, Identifier *id, Expression *value, Type *origType); Dsymbol *syntaxCopy(Dsymbol *s); const char *kind(); void semantic(Scope *sc); diff --git a/src/iasm.c b/src/iasm.c index 0eb3e3a344b5..ea037e3faf2d 100644 --- a/src/iasm.c +++ b/src/iasm.c @@ -2335,7 +2335,7 @@ static void asm_merge_symbol(OPND *o1, Dsymbol *s) em = s->isEnumMember(); if (em) { - o1->disp = em->value->toInteger(); + o1->disp = em->value()->toInteger(); return; } o1->s = s; // a C identifier diff --git a/src/json.d b/src/json.d index 6d403853486b..123fb85c782b 100644 --- a/src/json.d +++ b/src/json.d @@ -745,8 +745,8 @@ public: override void visit(EnumMember s) { objectStart(); - jsonProperties(s); - property("type", "deco", s.type); + jsonProperties(cast(Dsymbol)s); + property("type", "deco", s.origType); objectEnd(); } diff --git a/src/mtype.d b/src/mtype.d index 6644dffdbaff..029ba099b617 100644 --- a/src/mtype.d +++ b/src/mtype.d @@ -7050,6 +7050,13 @@ public: L2: s = sm.toAlias(); } + + if (auto em = s.isEnumMember()) + { + // It's not a type, it's an expression + *pe = em.getVarExp(loc, sc); + return; + } if (VarDeclaration v = s.isVarDeclaration()) { /* This is mostly same with DsymbolExp::semantic(), but we cannot use it @@ -7088,12 +7095,6 @@ public: return; } } - if (EnumMember em = s.isEnumMember()) - { - // It's not a type, it's an expression - *pe = em.getVarExp(loc, sc); - return; - } L1: Type t = s.getType(); if (!t) @@ -7265,6 +7266,8 @@ public: resolve(loc, sc, &e, &t, &s); if (t && t.ty != Tident) s = t.toDsymbol(sc); + if (e) + s = getDsymbol(e); return s; } @@ -7834,6 +7837,12 @@ public: if (!s.isFuncDeclaration()) // because of overloading s.checkDeprecated(e.loc, sc); s = s.toAlias(); + + if (auto em = s.isEnumMember()) + { + return em.getVarExp(e.loc, sc); + } + VarDeclaration v = s.isVarDeclaration(); if (v && (!v.type || !v.type.deco)) { @@ -7860,11 +7869,7 @@ public: { return new TypeExp(e.loc, s.getType()); } - EnumMember em = s.isEnumMember(); - if (em) - { - return em.getVarExp(e.loc, sc); - } + TemplateMixin tm = s.isTemplateMixin(); if (tm) { @@ -8690,6 +8695,12 @@ public: if (!s.isFuncDeclaration()) // because of overloading s.checkDeprecated(e.loc, sc); s = s.toAlias(); + + if (auto em = s.isEnumMember()) + { + return em.getVarExp(e.loc, sc); + } + VarDeclaration v = s.isVarDeclaration(); if (v && (!v.type || !v.type.deco)) { @@ -8716,11 +8727,7 @@ public: { return new TypeExp(e.loc, s.getType()); } - EnumMember em = s.isEnumMember(); - if (em) - { - return em.getVarExp(e.loc, sc); - } + TemplateMixin tm = s.isTemplateMixin(); if (tm) { diff --git a/src/tocvdebug.c b/src/tocvdebug.c index 859ef02aaba9..622b7c0f1e0b 100644 --- a/src/tocvdebug.c +++ b/src/tocvdebug.c @@ -159,7 +159,7 @@ unsigned cv4_Denum(EnumDeclaration *e) { EnumMember *sf = (*e->members)[i]->isEnumMember(); if (sf) { - dinteger_t value = sf->value->toInteger(); + dinteger_t value = sf->value()->toInteger(); unsigned fnamelen1 = fnamelen; // store only member's simple name @@ -233,7 +233,7 @@ unsigned cv4_Denum(EnumDeclaration *e) if (fieldi > nfields) break; // chop off the rest - dinteger_t value = sf->value->toInteger(); + dinteger_t value = sf->value()->toInteger(); TOWORD(dt->data + j,(config.fulltypes == CV8) ? LF_ENUMERATE_V3 : LF_ENUMERATE); unsigned attribute = 0; TOWORD(dt->data + j + 2,attribute); diff --git a/src/visitor.d b/src/visitor.d index b0649f64dab8..7817a7928bd3 100644 --- a/src/visitor.d +++ b/src/visitor.d @@ -380,7 +380,7 @@ public: void visit(EnumMember s) { - visit(cast(Dsymbol)s); + visit(cast(VarDeclaration)s); } void visit(Import s) diff --git a/src/visitor.h b/src/visitor.h index 9f425fc9872a..4658ede069b6 100644 --- a/src/visitor.h +++ b/src/visitor.h @@ -371,7 +371,7 @@ class Visitor virtual void visit(StaticAssert *s) { visit((Dsymbol *)s); } virtual void visit(DebugSymbol *s) { visit((Dsymbol *)s); } virtual void visit(VersionSymbol *s) { visit((Dsymbol *)s); } - virtual void visit(EnumMember *s) { visit((Dsymbol *)s); } + virtual void visit(EnumMember *s) { visit((VarDeclaration *)s); } virtual void visit(Import *s) { visit((Dsymbol *)s); } virtual void visit(OverloadSet *s) { visit((Dsymbol *)s); } virtual void visit(LabelDsymbol *s) { visit((Dsymbol *)s); } diff --git a/test/compilable/imports/test15150a.d b/test/compilable/imports/test15150a.d new file mode 100644 index 000000000000..fe3b8ab06bea --- /dev/null +++ b/test/compilable/imports/test15150a.d @@ -0,0 +1,6 @@ +module imports.test15150a; + +enum +{ + x +} diff --git a/test/compilable/imports/test15150b.d b/test/compilable/imports/test15150b.d new file mode 100644 index 000000000000..24b0928cc793 --- /dev/null +++ b/test/compilable/imports/test15150b.d @@ -0,0 +1,3 @@ +module imports.test15150b; + +import imports.test15150a : x; diff --git a/test/compilable/test15150.d b/test/compilable/test15150.d new file mode 100644 index 000000000000..3a00b80348be --- /dev/null +++ b/test/compilable/test15150.d @@ -0,0 +1,8 @@ +// PERMUTE_ARGS: + +module test15150; + +import imports.test15150a; +import imports.test15150b; + +enum y = x; diff --git a/test/fail_compilation/fail10528.d b/test/fail_compilation/fail10528.d index a1ebedeabb49..80199e2c438c 100644 --- a/test/fail_compilation/fail10528.d +++ b/test/fail_compilation/fail10528.d @@ -3,8 +3,8 @@ TEST_OUTPUT: --- fail_compilation/fail10528.d(19): Error: module fail10528 variable a10528.a is private fail_compilation/fail10528.d(20): Error: variable a10528.a is not accessible from module fail10528 -fail_compilation/fail10528.d(22): Error: variable a10528.b is not accessible from module fail10528 -fail_compilation/fail10528.d(23): Error: variable a10528.b is not accessible from module fail10528 +fail_compilation/fail10528.d(22): Error: module fail10528 enum member a10528.b is private +fail_compilation/fail10528.d(23): Error: enum member a10528.b is not accessible from module fail10528 fail_compilation/fail10528.d(25): Error: variable a10528.S.c is not accessible from module fail10528 fail_compilation/fail10528.d(26): Error: variable a10528.S.c is not accessible from module fail10528 fail_compilation/fail10528.d(28): Error: variable a10528.C.d is not accessible from module fail10528 From f9aa4c971b194a8b5246efb3361c0e23239c9fa5 Mon Sep 17 00:00:00 2001 From: k-hara Date: Tue, 6 Oct 2015 14:40:38 +0900 Subject: [PATCH 3/3] Keep backward compatibility of ddoc generation for template with anonymous enum --- src/doc.d | 3 +++ test/compilable/ddoc9.d | 5 +++++ test/compilable/extra-files/ddoc9.html | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/doc.d b/src/doc.d index add896e25638..ae418e8d2ba2 100644 --- a/src/doc.d +++ b/src/doc.d @@ -10,6 +10,7 @@ module ddmd.doc; import core.stdc.ctype; import core.stdc.stdlib; +import core.stdc.stdio; import core.stdc.string; import core.stdc.time; import ddmd.aggregate; @@ -329,6 +330,8 @@ extern (C++) static Dsymbol getEponymousMember(TemplateDeclaration td) return ad; if (FuncDeclaration fd = td.onemember.isFuncDeclaration()) return fd; + if (auto em = td.onemember.isEnumMember()) + return null; // Keep backward compatibility. See compilable/ddoc9.d if (VarDeclaration vd = td.onemember.isVarDeclaration()) return td.constraint ? null : vd; return null; diff --git a/test/compilable/ddoc9.d b/test/compilable/ddoc9.d index ab42c09aec5a..21f312db44a6 100644 --- a/test/compilable/ddoc9.d +++ b/test/compilable/ddoc9.d @@ -19,3 +19,8 @@ struct Struct(T) { } /// Union Documentation union Union(T) { } +/// Template documentation with anonymous enum +template TemplateWithAnonEnum(T) +{ + enum { TemplateWithAnonEnum = 1 } +} diff --git a/test/compilable/extra-files/ddoc9.html b/test/compilable/extra-files/ddoc9.html index c9433691a471..c5e8d1681698 100644 --- a/test/compilable/extra-files/ddoc9.html +++ b/test/compilable/extra-files/ddoc9.html @@ -27,6 +27,10 @@

ddoc9

Union Documentation

+
+
template TemplateWithAnonEnum(T)
+
Template documentation with anonymous enum

+