Skip to content
Closed
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
24 changes: 24 additions & 0 deletions changelog/enum_attributes.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
D now supports attributes on enum members

Example
---
template AliasSeq(TList...)
{
alias AliasSeq = TList;
}

enum MyEnum
{
@("uda0") value0,
@disable value1,
deprecated value2 // Deprecation: enum member `main.MyEnum.value2` is deprecated
}

static assert(__traits(getAttributes, MyEnum.value0) == AliasSeq!("uda0"));

void main()
{
auto v1 = MyEnum.value1; // Error: enum member `main.MyEnum.value1` cannot be used because it is annotated with `@disable`
}
---

9 changes: 9 additions & 0 deletions src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,15 @@ struct ASTBase
this.origType = origType;
}

extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
{
this(loc, id, value, memtype);
storage_class = stc;
userAttribDecl = uad;
depdecl = dd;
}

override void accept(Visitor v)
{
v.visit(this);
Expand Down
2 changes: 1 addition & 1 deletion src/dmd/declaration.d
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ extern (C++) abstract class Declaration : Dsymbol
return false;
}
}
error(loc, "is not callable because it is annotated with `@disable`");
error(loc, "cannot be used because it is annotated with `@disable`");
}
}
return true;
Expand Down
14 changes: 14 additions & 0 deletions src/dmd/denum.d
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module dmd.denum;

import core.stdc.stdio;

import dmd.attrib;
import dmd.gluelayer;
import dmd.declaration;
import dmd.dscope;
Expand Down Expand Up @@ -374,6 +375,7 @@ extern (C++) final class EnumMember : VarDeclaration
Type origType;

EnumDeclaration ed;
bool isdeprecated;

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

extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
{
this(loc, id, value, memtype);
storage_class = stc;
userAttribDecl = uad;
depdecl = dd;
}

override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
Expand All @@ -396,6 +407,9 @@ extern (C++) final class EnumMember : VarDeclaration
Expression getVarExp(const ref Loc loc, Scope* sc)
{
dsymbolSemantic(this, sc);
if (errors)
return new ErrorExp();
checkDisabled(loc, sc);
if (errors)
return new ErrorExp();
Expression e = new VarExp(loc, this);
Expand Down
15 changes: 13 additions & 2 deletions src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -2171,6 +2171,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
return errorReturn();
}
assert(em.ed);

em.ed.dsymbolSemantic(sc);
if (em.ed.errors)
return errorReturn();
Expand All @@ -2185,9 +2186,19 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
em.semanticRun = PASS.semantic;

em.protection = em.ed.isAnonymous() ? em.ed.protection : Prot(Prot.Kind.public_);
if (sc.stc & STC.deprecated_ || em.isDeprecated())
em.isdeprecated = true;
em.linkage = LINK.d;
em.storage_class = STC.manifest;
em.userAttribDecl = em.ed.isAnonymous() ? em.ed.userAttribDecl : null;
em.storage_class |= STC.manifest;

// https://issues.dlang.org/show_bug.cgi?id=9701
if (em.ed.isAnonymous())
{
if (em.userAttribDecl)
em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl;
else
em.userAttribDecl = em.ed.userAttribDecl;
}

// The first enum member is special
bool first = (em == (*em.ed.members)[0]);
Expand Down
12 changes: 6 additions & 6 deletions src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -2494,12 +2494,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("VarExp::semantic(%s)\n", e.toChars());
}
if (auto fd = e.var.isFuncDeclaration())
{
//printf("L%d fd = %s\n", __LINE__, f.toChars());
if (!fd.functionSemantic())
return setError();
}

if (!e.type)
e.type = e.var.type;
Expand All @@ -2512,6 +2506,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
*/
//checkAccess(loc, sc, NULL, var);

e.checkDeprecated(sc, e.var);

if (auto vd = e.var.isVarDeclaration())
{
if (vd.checkNestedReference(sc, e.loc))
Expand All @@ -2524,6 +2520,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
else if (auto fd = e.var.isFuncDeclaration())
{
//printf("L%d fd = %s\n", __LINE__, f.toChars());
if (!fd.functionSemantic())
return setError();

// TODO: If fd isn't yet resolved its overload, the checkNestedReference
// call would cause incorrect validation.
// Maybe here should be moved in CallExp, or AddrExp for functions.
Expand Down
122 changes: 99 additions & 23 deletions src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,23 @@ final class Parser(AST) : Lexer
return new AST.Dsymbols();
}

private StorageClass parseDeprecatedAttribute(ref AST.Expression msg)
{
if (peek(&token).value != TOK.leftParentheses)
return AST.STC.deprecated_;

nextToken();
check(TOK.leftParentheses);
AST.Expression e = parseAssignExp();
check(TOK.rightParentheses);
if (msg)
{
error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
}
msg = e;
return AST.STC.undefined_;
}

AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
{
AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
Expand Down Expand Up @@ -811,20 +828,12 @@ final class Parser(AST) : Lexer

case TOK.deprecated_:
{
if (peek(&token).value != TOK.leftParentheses)
AST.Expression e;
if (StorageClass _stc = parseDeprecatedAttribute(pAttrs.depmsg))
{
stc = AST.STC.deprecated_;
stc = _stc;
goto Lstc;
}
nextToken();
check(TOK.leftParentheses);
AST.Expression e = parseAssignExp();
check(TOK.rightParentheses);
if (pAttrs.depmsg)
{
error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", pAttrs.depmsg.toChars(), e.toChars());
}
pAttrs.depmsg = e;
a = parseBlock(pLastDecl, pAttrs);
if (pAttrs.depmsg)
{
Expand Down Expand Up @@ -2955,7 +2964,7 @@ final class Parser(AST) : Lexer
AST.Type memtype;
auto loc = token.loc;

//printf("Parser::parseEnum()\n");
// printf("Parser::parseEnum()\n");
nextToken();
if (token.value == TOK.identifier)
{
Expand All @@ -2982,34 +2991,93 @@ final class Parser(AST) : Lexer
nextToken();
else if (token.value == TOK.leftCurly)
{
bool isAnonymousEnum = !id;

//printf("enum definition\n");
e.members = new AST.Dsymbols();
nextToken();
const(char)* comment = token.blockComment;
while (token.value != TOK.rightCurly)
{
/* Can take the following forms:
/* Can take the following forms...
* 1. ident
* 2. ident = value
* 3. type ident = value
* ... prefixed by valid attributes
*/
loc = token.loc;

AST.Type type = null;
Identifier ident = null;
Token* tp = peek(&token);
if (token.value == TOK.identifier && (tp.value == TOK.assign || tp.value == TOK.comma || tp.value == TOK.rightCurly))

AST.Expressions* udas;
StorageClass stc;
AST.Expression deprecationMessage;
enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
while(token.value != TOK.rightCurly
&& token.value != TOK.comma
&& token.value != TOK.assign)
{
ident = token.ident;
type = null;
nextToken();
switch(token.value)
{
case TOK.at:
if (StorageClass _stc = parseAttribute(&udas))
{
if (_stc == AST.STC.disable)
stc |= _stc;
else
{
OutBuffer buf;
AST.stcToBuffer(&buf, _stc);
error(attributeErrorMessage, buf.peekString());
}
nextToken();
}
break;
case TOK.deprecated_:
if (StorageClass _stc = parseDeprecatedAttribute(deprecationMessage))
{
stc |= _stc;
nextToken();
}
break;
case TOK.identifier:
Token* tp = peek(&token);
if (tp.value == TOK.assign || tp.value == TOK.comma || tp.value == TOK.rightCurly)
{
ident = token.ident;
type = null;
nextToken();
}
else
{
goto default;
}
break;
default:
if (isAnonymousEnum)
{
type = parseType(&ident, null);
if (type == AST.Type.terror)
{
type = null;
nextToken();
}
}
else
{
error(attributeErrorMessage, token.toChars());
nextToken();
}
break;
}
}
else

if (type && type != AST.Type.terror)
{
type = parseType(&ident, null);
if (!ident)
error("no identifier for declarator `%s`", type.toChars());
if (id || memtype)
if (!isAnonymousEnum)
error("type only allowed if anonymous enum and no enum type");
}

Expand All @@ -3022,11 +3090,19 @@ final class Parser(AST) : Lexer
else
{
value = null;
if (type)
if (type && type != AST.Type.terror && isAnonymousEnum)
error("if type, there must be an initializer");
}

auto em = new AST.EnumMember(loc, ident, value, type);
AST.UserAttributeDeclaration uad;
if (udas)
uad = new AST.UserAttributeDeclaration(udas, null);

AST.DeprecatedDeclaration dd;
if (deprecationMessage)
dd = new AST.DeprecatedDeclaration(deprecationMessage, null);

auto em = new AST.EnumMember(loc, ident, value, type, stc, uad, dd);
e.members.push(em);

if (token.value == TOK.rightCurly)
Expand Down
6 changes: 5 additions & 1 deletion src/dmd/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import dmd.arraytypes;
import dmd.canthrow;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
Expand Down Expand Up @@ -510,6 +511,8 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
auto y = s.isDeclaration();
static if (is(T == FuncDeclaration))
auto y = s.isFuncDeclaration();
static if (is(T == EnumMember))
auto y = s.isEnumMember();

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

if (e.ident == Id.isArithmetic)
{
Expand Down Expand Up @@ -634,7 +638,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
if (dim != 1)
return dimError(1);

return isFuncX(f => f.isDisabled());
return isDeclX(f => f.isDisabled());
}
if (e.ident == Id.isAbstractFunction)
{
Expand Down
Loading