Skip to content

Commit

Permalink
Merge pull request #8404 from IgorStepanov/uda-enum
Browse files Browse the repository at this point in the history
Fix Issue 9701 - allow UDAs to be attached to enum values
merged-on-behalf-of: Jacob Carlborg <jacob-carlborg@users.noreply.github.com>
  • Loading branch information
dlang-bot authored Jun 27, 2018
2 parents fb22391 + 36d349c commit 972ea45
Show file tree
Hide file tree
Showing 19 changed files with 360 additions and 42 deletions.
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

0 comments on commit 972ea45

Please sign in to comment.