Skip to content
Merged
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 deprecated, @disable and user-defined 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;
// just ignore `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
13 changes: 13 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 @@ -382,6 +383,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 +406,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
13 changes: 11 additions & 2 deletions src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -2169,6 +2169,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 @@ -2184,8 +2185,16 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor

em.protection = em.ed.isAnonymous() ? em.ed.protection : Prot(Prot.Kind.public_);
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
13 changes: 13 additions & 0 deletions src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -4295,6 +4295,11 @@ extern (C++) final class SymOffExp : SymbolExp
*/
extern (C++) final class VarExp : SymbolExp
{
/**
* Semantic can be called multiple times for a single expression.
* This field is needed to ensure the deprecation message will be printed only once.
*/
bool hasCheckedAttrs;
extern (D) this(const ref Loc loc, Declaration var, bool hasOverloads = true)
{
if (var.isVarDeclaration())
Expand All @@ -4304,6 +4309,7 @@ extern (C++) final class VarExp : SymbolExp
//printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars());
//if (strcmp(var.ident.toChars(), "func") == 0) assert(0);
this.type = var.type;
this.hasCheckedAttrs = false;
}

static VarExp create(Loc loc, Declaration var, bool hasOverloads = true)
Expand Down Expand Up @@ -4383,6 +4389,13 @@ extern (C++) final class VarExp : SymbolExp
{
v.visit(this);
}

override Expression syntaxCopy()
{
auto ret = super.syntaxCopy();
(cast(VarExp)ret).hasCheckedAttrs = this.hasCheckedAttrs;
return ret;
}
}

/***********************************************************
Expand Down
6 changes: 6 additions & 0 deletions src/dmd/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,12 @@ class SymOffExp : public SymbolExp
class VarExp : public SymbolExp
{
public:
/**
* Semantic can be called multiple times for a single expression.
* This field is needed to ensure the deprecation message will be printed only once.
*/
bool hasCheckedAttrs;

static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true);
bool equals(RootObject *o);
int checkModifiable(Scope *sc, int flag);
Expand Down
10 changes: 10 additions & 0 deletions src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -2515,6 +2515,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
* variables as alias template parameters.
*/
//checkAccess(loc, sc, NULL, var);
if (!e.hasCheckedAttrs && e.var.isEnumMember())
{
e.hasCheckedAttrs = true;
if (e.var.depdecl && !e.var.depdecl._scope)
{
e.var.depdecl._scope = sc;
}
e.checkDeprecated(sc, e.var);

}

if (auto vd = e.var.isVarDeclaration())
{
Expand Down
125 changes: 102 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 @@ -2950,7 +2959,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 @@ -2977,34 +2986,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 @@ -3017,11 +3085,22 @@ 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);
stc |= AST.STC.deprecated_;
}

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