Skip to content

Commit f2bfb6b

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

19 files changed

Lines changed: 360 additions & 48 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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ struct ASTBase
296296
Identifier ident;
297297
UnitTestDeclaration ddocUnittest;
298298
UserAttributeDeclaration userAttribDecl;
299+
DeprecatedDeclaration depdecl;
299300
Dsymbol parent;
300301

301302
const(char)* comment;
@@ -1424,6 +1425,15 @@ struct ASTBase
14241425
this.origType = origType;
14251426
}
14261427

1428+
extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
1429+
StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
1430+
{
1431+
this(loc, id, value, memtype);
1432+
storage_class = stc;
1433+
userAttribDecl = uad;
1434+
depdecl = dd;
1435+
}
1436+
14271437
override void accept(Visitor v)
14281438
{
14291439
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: 14 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;
@@ -374,6 +375,7 @@ extern (C++) final class EnumMember : VarDeclaration
374375
Type origType;
375376

376377
EnumDeclaration ed;
378+
bool isdeprecated;
377379

378380
extern (D) this(const ref Loc loc, Identifier id, Expression value, Type origType)
379381
{
@@ -382,6 +384,15 @@ extern (C++) final class EnumMember : VarDeclaration
382384
this.origType = origType;
383385
}
384386

387+
extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
388+
StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
389+
{
390+
this(loc, id, value, memtype);
391+
storage_class = stc;
392+
userAttribDecl = uad;
393+
depdecl = dd;
394+
}
395+
385396
override Dsymbol syntaxCopy(Dsymbol s)
386397
{
387398
assert(!s);
@@ -396,6 +407,9 @@ extern (C++) final class EnumMember : VarDeclaration
396407
Expression getVarExp(const ref Loc loc, Scope* sc)
397408
{
398409
dsymbolSemantic(this, sc);
410+
if (errors)
411+
return new ErrorExp();
412+
checkDisabled(loc, sc);
399413
if (errors)
400414
return new ErrorExp();
401415
Expression e = new VarExp(loc, this);

src/dmd/dsymbolsem.d

Lines changed: 13 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();
@@ -2183,9 +2184,19 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
21832184
em.semanticRun = PASS.semantic;
21842185

21852186
em.protection = em.ed.isAnonymous() ? em.ed.protection : Prot(Prot.Kind.public_);
2187+
if (sc.stc & STC.deprecated_ || em.isDeprecated())
2188+
em.isdeprecated = true;
21862189
em.linkage = LINK.d;
2187-
em.storage_class = STC.manifest;
2188-
em.userAttribDecl = em.ed.isAnonymous() ? em.ed.userAttribDecl : null;
2190+
em.storage_class |= STC.manifest;
2191+
2192+
// https://issues.dlang.org/show_bug.cgi?id=9701
2193+
if (em.ed.isAnonymous())
2194+
{
2195+
if (em.userAttribDecl)
2196+
em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl;
2197+
else
2198+
em.userAttribDecl = em.ed.userAttribDecl;
2199+
}
21892200

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

src/dmd/expression.d

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4295,6 +4295,7 @@ extern (C++) final class SymOffExp : SymbolExp
42954295
*/
42964296
extern (C++) final class VarExp : SymbolExp
42974297
{
4298+
bool hasCheckedAttrs;
42984299
extern (D) this(const ref Loc loc, Declaration var, bool hasOverloads = true)
42994300
{
43004301
if (var.isVarDeclaration())
@@ -4304,6 +4305,7 @@ extern (C++) final class VarExp : SymbolExp
43044305
//printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars());
43054306
//if (strcmp(var.ident.toChars(), "func") == 0) assert(0);
43064307
this.type = var.type;
4308+
this.hasCheckedAttrs = false;
43074309
}
43084310

43094311
static VarExp create(Loc loc, Declaration var, bool hasOverloads = true)
@@ -4383,6 +4385,13 @@ extern (C++) final class VarExp : SymbolExp
43834385
{
43844386
v.visit(this);
43854387
}
4388+
4389+
override Expression syntaxCopy()
4390+
{
4391+
auto ret = super.syntaxCopy();
4392+
(cast(VarExp)ret).hasCheckedAttrs = this.hasCheckedAttrs;
4393+
return ret;
4394+
}
43864395
}
43874396

43884397
/***********************************************************

src/dmd/expression.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,8 @@ class SymOffExp : public SymbolExp
568568
class VarExp : public SymbolExp
569569
{
570570
public:
571+
bool hasCheckedAttrs;
572+
571573
static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true);
572574
bool equals(RootObject *o);
573575
int checkModifiable(Scope *sc, int flag);

src/dmd/expressionsem.d

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,12 +2498,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
24982498
{
24992499
printf("VarExp::semantic(%s)\n", e.toChars());
25002500
}
2501-
if (auto fd = e.var.isFuncDeclaration())
2502-
{
2503-
//printf("L%d fd = %s\n", __LINE__, f.toChars());
2504-
if (!fd.functionSemantic())
2505-
return setError();
2506-
}
25072501

25082502
if (!e.type)
25092503
e.type = e.var.type;
@@ -2515,6 +2509,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
25152509
* variables as alias template parameters.
25162510
*/
25172511
//checkAccess(loc, sc, NULL, var);
2512+
if (!e.hasCheckedAttrs && e.var.isEnumMember())
2513+
{
2514+
e.hasCheckedAttrs = true;
2515+
if (e.var.depdecl && !e.var.depdecl._scope)
2516+
{
2517+
e.var.depdecl._scope = sc;
2518+
}
2519+
e.checkDeprecated(sc, e.var);
2520+
2521+
}
25182522

25192523
if (auto vd = e.var.isVarDeclaration())
25202524
{
@@ -2528,6 +2532,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
25282532
}
25292533
else if (auto fd = e.var.isFuncDeclaration())
25302534
{
2535+
//printf("L%d fd = %s\n", __LINE__, f.toChars());
2536+
if (!fd.functionSemantic())
2537+
return setError();
2538+
25312539
// TODO: If fd isn't yet resolved its overload, the checkNestedReference
25322540
// call would cause incorrect validation.
25332541
// Maybe here should be moved in CallExp, or AddrExp for functions.

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)

0 commit comments

Comments
 (0)