Skip to content

Commit f48bc12

Browse files
JinShilIgorStepanov
authored andcommitted
Fix Issue 9701 - allow UDAs to be attached to enum values
1 parent e4f19f3 commit f48bc12

19 files changed

Lines changed: 356 additions & 42 deletions

changelog/enum_attributes.dd

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
D now supports attributes on enum members
2+
3+
Example
4+
---
5+
template AliasSeq(TList...)
6+
{
7+
alias AliasSeq = TList;
8+
}
9+
10+
enum MyEnum
11+
{
12+
@("uda0") value0,
13+
@disable value1,
14+
deprecated value2 // Deprecation: enum member `main.MyEnum.value2` is deprecated
15+
}
16+
17+
static assert(__traits(getAttributes, MyEnum.value0) == AliasSeq!("uda0"));
18+
19+
void main()
20+
{
21+
auto v1 = MyEnum.value1; // Error: enum member `main.MyEnum.value1` cannot be used because it is annotated with `@disable`
22+
}
23+
---
24+

src/dmd/astbase.d

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,15 @@ struct ASTBase
14241424
this.origType = origType;
14251425
}
14261426

1427+
extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
1428+
StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
1429+
{
1430+
this(loc, id, value, memtype);
1431+
storage_class = stc;
1432+
userAttribDecl = uad;
1433+
// just ignore `dd`
1434+
}
1435+
14271436
override void accept(Visitor v)
14281437
{
14291438
v.visit(this);

src/dmd/declaration.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ extern (C++) abstract class Declaration : Dsymbol
340340
return false;
341341
}
342342
}
343-
error(loc, "is not callable because it is annotated with `@disable`");
343+
error(loc, "cannot be used because it is annotated with `@disable`");
344344
}
345345
}
346346
return true;

src/dmd/denum.d

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module dmd.denum;
1414

1515
import core.stdc.stdio;
1616

17+
import dmd.attrib;
1718
import dmd.gluelayer;
1819
import dmd.declaration;
1920
import dmd.dscope;
@@ -382,6 +383,15 @@ extern (C++) final class EnumMember : VarDeclaration
382383
this.origType = origType;
383384
}
384385

386+
extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
387+
StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
388+
{
389+
this(loc, id, value, memtype);
390+
storage_class = stc;
391+
userAttribDecl = uad;
392+
depdecl = dd;
393+
}
394+
385395
override Dsymbol syntaxCopy(Dsymbol s)
386396
{
387397
assert(!s);
@@ -396,6 +406,9 @@ extern (C++) final class EnumMember : VarDeclaration
396406
Expression getVarExp(const ref Loc loc, Scope* sc)
397407
{
398408
dsymbolSemantic(this, sc);
409+
if (errors)
410+
return new ErrorExp();
411+
checkDisabled(loc, sc);
399412
if (errors)
400413
return new ErrorExp();
401414
Expression e = new VarExp(loc, this);

src/dmd/dsymbolsem.d

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,6 +2169,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
21692169
return errorReturn();
21702170
}
21712171
assert(em.ed);
2172+
21722173
em.ed.dsymbolSemantic(sc);
21732174
if (em.ed.errors)
21742175
return errorReturn();
@@ -2184,8 +2185,16 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
21842185

21852186
em.protection = em.ed.isAnonymous() ? em.ed.protection : Prot(Prot.Kind.public_);
21862187
em.linkage = LINK.d;
2187-
em.storage_class = STC.manifest;
2188-
em.userAttribDecl = em.ed.isAnonymous() ? em.ed.userAttribDecl : null;
2188+
em.storage_class |= STC.manifest;
2189+
2190+
// https://issues.dlang.org/show_bug.cgi?id=9701
2191+
if (em.ed.isAnonymous())
2192+
{
2193+
if (em.userAttribDecl)
2194+
em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl;
2195+
else
2196+
em.userAttribDecl = em.ed.userAttribDecl;
2197+
}
21892198

21902199
// The first enum member is special
21912200
bool first = (em == (*em.ed.members)[0]);

src/dmd/expression.d

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4295,6 +4295,9 @@ extern (C++) final class SymOffExp : SymbolExp
42954295
*/
42964296
extern (C++) final class VarExp : SymbolExp
42974297
{
4298+
// Semantic can be called many times for a one expression.
4299+
// This field need to ensure that deprecation message will be printed only one time.
4300+
bool hasCheckedAttrs;
42984301
extern (D) this(const ref Loc loc, Declaration var, bool hasOverloads = true)
42994302
{
43004303
if (var.isVarDeclaration())
@@ -4304,6 +4307,7 @@ extern (C++) final class VarExp : SymbolExp
43044307
//printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars());
43054308
//if (strcmp(var.ident.toChars(), "func") == 0) assert(0);
43064309
this.type = var.type;
4310+
this.hasCheckedAttrs = false;
43074311
}
43084312

43094313
static VarExp create(Loc loc, Declaration var, bool hasOverloads = true)
@@ -4383,6 +4387,13 @@ extern (C++) final class VarExp : SymbolExp
43834387
{
43844388
v.visit(this);
43854389
}
4390+
4391+
override Expression syntaxCopy()
4392+
{
4393+
auto ret = super.syntaxCopy();
4394+
(cast(VarExp)ret).hasCheckedAttrs = this.hasCheckedAttrs;
4395+
return ret;
4396+
}
43864397
}
43874398

43884399
/***********************************************************

src/dmd/expression.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,10 @@ class SymOffExp : public SymbolExp
568568
class VarExp : public SymbolExp
569569
{
570570
public:
571+
// Semantic can be called many times for a one expression.
572+
// This field need to ensure that deprecation message will be printed only one time.
573+
bool hasCheckedAttrs;
574+
571575
static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true);
572576
bool equals(RootObject *o);
573577
int checkModifiable(Scope *sc, int flag);

src/dmd/expressionsem.d

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,6 +2515,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
25152515
* variables as alias template parameters.
25162516
*/
25172517
//checkAccess(loc, sc, NULL, var);
2518+
if (!e.hasCheckedAttrs && e.var.isEnumMember())
2519+
{
2520+
e.hasCheckedAttrs = true;
2521+
if (e.var.depdecl && !e.var.depdecl._scope)
2522+
{
2523+
e.var.depdecl._scope = sc;
2524+
}
2525+
e.checkDeprecated(sc, e.var);
2526+
2527+
}
25182528

25192529
if (auto vd = e.var.isVarDeclaration())
25202530
{

src/dmd/parse.d

Lines changed: 102 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,23 @@ final class Parser(AST) : Lexer
392392
return new AST.Dsymbols();
393393
}
394394

395+
private StorageClass parseDeprecatedAttribute(ref AST.Expression msg)
396+
{
397+
if (peek(&token).value != TOK.leftParentheses)
398+
return AST.STC.deprecated_;
399+
400+
nextToken();
401+
check(TOK.leftParentheses);
402+
AST.Expression e = parseAssignExp();
403+
check(TOK.rightParentheses);
404+
if (msg)
405+
{
406+
error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
407+
}
408+
msg = e;
409+
return AST.STC.undefined_;
410+
}
411+
395412
AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
396413
{
397414
AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
@@ -811,20 +828,12 @@ final class Parser(AST) : Lexer
811828

812829
case TOK.deprecated_:
813830
{
814-
if (peek(&token).value != TOK.leftParentheses)
831+
AST.Expression e;
832+
if (StorageClass _stc = parseDeprecatedAttribute(pAttrs.depmsg))
815833
{
816-
stc = AST.STC.deprecated_;
834+
stc = _stc;
817835
goto Lstc;
818836
}
819-
nextToken();
820-
check(TOK.leftParentheses);
821-
AST.Expression e = parseAssignExp();
822-
check(TOK.rightParentheses);
823-
if (pAttrs.depmsg)
824-
{
825-
error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", pAttrs.depmsg.toChars(), e.toChars());
826-
}
827-
pAttrs.depmsg = e;
828837
a = parseBlock(pLastDecl, pAttrs);
829838
if (pAttrs.depmsg)
830839
{
@@ -2950,7 +2959,7 @@ final class Parser(AST) : Lexer
29502959
AST.Type memtype;
29512960
auto loc = token.loc;
29522961

2953-
//printf("Parser::parseEnum()\n");
2962+
// printf("Parser::parseEnum()\n");
29542963
nextToken();
29552964
if (token.value == TOK.identifier)
29562965
{
@@ -2977,34 +2986,93 @@ final class Parser(AST) : Lexer
29772986
nextToken();
29782987
else if (token.value == TOK.leftCurly)
29792988
{
2989+
bool isAnonymousEnum = !id;
2990+
29802991
//printf("enum definition\n");
29812992
e.members = new AST.Dsymbols();
29822993
nextToken();
29832994
const(char)* comment = token.blockComment;
29842995
while (token.value != TOK.rightCurly)
29852996
{
2986-
/* Can take the following forms:
2997+
/* Can take the following forms...
29872998
* 1. ident
29882999
* 2. ident = value
29893000
* 3. type ident = value
3001+
* ... prefixed by valid attributes
29903002
*/
29913003
loc = token.loc;
29923004

29933005
AST.Type type = null;
29943006
Identifier ident = null;
2995-
Token* tp = peek(&token);
2996-
if (token.value == TOK.identifier && (tp.value == TOK.assign || tp.value == TOK.comma || tp.value == TOK.rightCurly))
3007+
3008+
AST.Expressions* udas;
3009+
StorageClass stc;
3010+
AST.Expression deprecationMessage;
3011+
enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
3012+
while(token.value != TOK.rightCurly
3013+
&& token.value != TOK.comma
3014+
&& token.value != TOK.assign)
29973015
{
2998-
ident = token.ident;
2999-
type = null;
3000-
nextToken();
3016+
switch(token.value)
3017+
{
3018+
case TOK.at:
3019+
if (StorageClass _stc = parseAttribute(&udas))
3020+
{
3021+
if (_stc == AST.STC.disable)
3022+
stc |= _stc;
3023+
else
3024+
{
3025+
OutBuffer buf;
3026+
AST.stcToBuffer(&buf, _stc);
3027+
error(attributeErrorMessage, buf.peekString());
3028+
}
3029+
nextToken();
3030+
}
3031+
break;
3032+
case TOK.deprecated_:
3033+
if (StorageClass _stc = parseDeprecatedAttribute(deprecationMessage))
3034+
{
3035+
stc |= _stc;
3036+
nextToken();
3037+
}
3038+
break;
3039+
case TOK.identifier:
3040+
Token* tp = peek(&token);
3041+
if (tp.value == TOK.assign || tp.value == TOK.comma || tp.value == TOK.rightCurly)
3042+
{
3043+
ident = token.ident;
3044+
type = null;
3045+
nextToken();
3046+
}
3047+
else
3048+
{
3049+
goto default;
3050+
}
3051+
break;
3052+
default:
3053+
if (isAnonymousEnum)
3054+
{
3055+
type = parseType(&ident, null);
3056+
if (type == AST.Type.terror)
3057+
{
3058+
type = null;
3059+
nextToken();
3060+
}
3061+
}
3062+
else
3063+
{
3064+
error(attributeErrorMessage, token.toChars());
3065+
nextToken();
3066+
}
3067+
break;
3068+
}
30013069
}
3002-
else
3070+
3071+
if (type && type != AST.Type.terror)
30033072
{
3004-
type = parseType(&ident, null);
30053073
if (!ident)
30063074
error("no identifier for declarator `%s`", type.toChars());
3007-
if (id || memtype)
3075+
if (!isAnonymousEnum)
30083076
error("type only allowed if anonymous enum and no enum type");
30093077
}
30103078

@@ -3017,11 +3085,22 @@ final class Parser(AST) : Lexer
30173085
else
30183086
{
30193087
value = null;
3020-
if (type)
3088+
if (type && type != AST.Type.terror && isAnonymousEnum)
30213089
error("if type, there must be an initializer");
30223090
}
30233091

3024-
auto em = new AST.EnumMember(loc, ident, value, type);
3092+
AST.UserAttributeDeclaration uad;
3093+
if (udas)
3094+
uad = new AST.UserAttributeDeclaration(udas, null);
3095+
3096+
AST.DeprecatedDeclaration dd;
3097+
if (deprecationMessage)
3098+
{
3099+
dd = new AST.DeprecatedDeclaration(deprecationMessage, null);
3100+
stc |= AST.STC.deprecated_;
3101+
}
3102+
3103+
auto em = new AST.EnumMember(loc, ident, value, type, stc, uad, dd);
30253104
e.members.push(em);
30263105

30273106
if (token.value == TOK.rightCurly)

src/dmd/traits.d

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import dmd.arraytypes;
2020
import dmd.canthrow;
2121
import dmd.dclass;
2222
import dmd.declaration;
23+
import dmd.denum;
2324
import dmd.dscope;
2425
import dmd.dsymbol;
2526
import dmd.dsymbolsem;
@@ -510,6 +511,8 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
510511
auto y = s.isDeclaration();
511512
static if (is(T == FuncDeclaration))
512513
auto y = s.isFuncDeclaration();
514+
static if (is(T == EnumMember))
515+
auto y = s.isEnumMember();
513516

514517
if (!y || !fp(y))
515518
return False();
@@ -521,6 +524,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
521524
alias isDsymX = isX!Dsymbol;
522525
alias isDeclX = isX!Declaration;
523526
alias isFuncX = isX!FuncDeclaration;
527+
alias isEnumMemX = isX!EnumMember;
524528

525529
if (e.ident == Id.isArithmetic)
526530
{
@@ -634,7 +638,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
634638
if (dim != 1)
635639
return dimError(1);
636640

637-
return isFuncX(f => f.isDisabled());
641+
return isDeclX(f => f.isDisabled());
638642
}
639643
if (e.ident == Id.isAbstractFunction)
640644
{

0 commit comments

Comments
 (0)