| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved |
| * written by Walter Bright |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| * https://github.com/D-Programming-Language/dmd/blob/master/src/enum.c |
| */ |
| |
| #include "root/dsystem.h" |
| #include "root/root.h" |
| |
| #include "errors.h" |
| #include "enum.h" |
| #include "mtype.h" |
| #include "scope.h" |
| #include "id.h" |
| #include "expression.h" |
| #include "module.h" |
| #include "declaration.h" |
| #include "init.h" |
| |
| Expression *semantic(Expression *e, Scope *sc); |
| |
| /********************************* EnumDeclaration ****************************/ |
| |
| EnumDeclaration::EnumDeclaration(Loc loc, Identifier *id, Type *memtype) |
| : ScopeDsymbol(id) |
| { |
| //printf("EnumDeclaration() %s\n", toChars()); |
| this->loc = loc; |
| type = new TypeEnum(this); |
| this->memtype = memtype; |
| maxval = NULL; |
| minval = NULL; |
| defaultval = NULL; |
| sinit = NULL; |
| isdeprecated = false; |
| protection = Prot(PROTundefined); |
| parent = NULL; |
| added = false; |
| inuse = 0; |
| } |
| |
| Dsymbol *EnumDeclaration::syntaxCopy(Dsymbol *s) |
| { |
| assert(!s); |
| EnumDeclaration *ed = new EnumDeclaration(loc, ident, |
| memtype ? memtype->syntaxCopy() : NULL); |
| return ScopeDsymbol::syntaxCopy(ed); |
| } |
| |
| void EnumDeclaration::setScope(Scope *sc) |
| { |
| if (semanticRun > PASSinit) |
| return; |
| ScopeDsymbol::setScope(sc); |
| } |
| |
| void EnumDeclaration::addMember(Scope *sc, ScopeDsymbol *sds) |
| { |
| /* Anonymous enum members get added to enclosing scope. |
| */ |
| ScopeDsymbol *scopesym = isAnonymous() ? sds : this; |
| |
| if (!isAnonymous()) |
| { |
| ScopeDsymbol::addMember(sc, sds); |
| |
| if (!symtab) |
| symtab = new DsymbolTable(); |
| } |
| |
| if (members) |
| { |
| for (size_t i = 0; i < members->dim; i++) |
| { |
| EnumMember *em = (*members)[i]->isEnumMember(); |
| em->ed = this; |
| //printf("add %s to scope %s\n", em->toChars(), scopesym->toChars()); |
| em->addMember(sc, isAnonymous() ? scopesym : this); |
| } |
| } |
| added = true; |
| } |
| |
| |
| void EnumDeclaration::semantic(Scope *sc) |
| { |
| //printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc->scopesym, sc->scopesym->toChars(), toChars()); |
| //printf("EnumDeclaration::semantic() %p %s\n", this, toChars()); |
| if (semanticRun >= PASSsemanticdone) |
| return; // semantic() already completed |
| if (semanticRun == PASSsemantic) |
| { |
| assert(memtype); |
| ::error(loc, "circular reference to enum base type %s", memtype->toChars()); |
| errors = true; |
| semanticRun = PASSsemanticdone; |
| return; |
| } |
| unsigned dprogress_save = Module::dprogress; |
| |
| Scope *scx = NULL; |
| if (_scope) |
| { |
| sc = _scope; |
| scx = _scope; // save so we don't make redundant copies |
| _scope = NULL; |
| } |
| |
| parent = sc->parent; |
| type = type->semantic(loc, sc); |
| |
| protection = sc->protection; |
| if (sc->stc & STCdeprecated) |
| isdeprecated = true; |
| userAttribDecl = sc->userAttribDecl; |
| |
| semanticRun = PASSsemantic; |
| |
| if (!members && !memtype) // enum ident; |
| { |
| semanticRun = PASSsemanticdone; |
| return; |
| } |
| |
| if (!symtab) |
| symtab = new DsymbolTable(); |
| |
| /* The separate, and distinct, cases are: |
| * 1. enum { ... } |
| * 2. enum : memtype { ... } |
| * 3. enum ident { ... } |
| * 4. enum ident : memtype { ... } |
| * 5. enum ident : memtype; |
| * 6. enum ident; |
| */ |
| |
| if (memtype) |
| { |
| memtype = memtype->semantic(loc, sc); |
| |
| /* Check to see if memtype is forward referenced |
| */ |
| if (memtype->ty == Tenum) |
| { |
| EnumDeclaration *sym = (EnumDeclaration *)memtype->toDsymbol(sc); |
| if (!sym->memtype || !sym->members || !sym->symtab || sym->_scope) |
| { |
| // memtype is forward referenced, so try again later |
| _scope = scx ? scx : sc->copy(); |
| _scope->setNoFree(); |
| _scope->_module->addDeferredSemantic(this); |
| Module::dprogress = dprogress_save; |
| //printf("\tdeferring %s\n", toChars()); |
| semanticRun = PASSinit; |
| return; |
| } |
| } |
| if (memtype->ty == Tvoid) |
| { |
| error("base type must not be void"); |
| memtype = Type::terror; |
| } |
| if (memtype->ty == Terror) |
| { |
| errors = true; |
| if (members) |
| { |
| for (size_t i = 0; i < members->dim; i++) |
| { |
| Dsymbol *s = (*members)[i]; |
| s->errors = true; // poison all the members |
| } |
| } |
| semanticRun = PASSsemanticdone; |
| return; |
| } |
| } |
| |
| semanticRun = PASSsemanticdone; |
| |
| if (!members) // enum ident : memtype; |
| return; |
| |
| if (members->dim == 0) |
| { |
| error("enum %s must have at least one member", toChars()); |
| errors = true; |
| return; |
| } |
| |
| Module::dprogress++; |
| |
| Scope *sce; |
| if (isAnonymous()) |
| sce = sc; |
| else |
| { |
| sce = sc->push(this); |
| sce->parent = this; |
| } |
| sce = sce->startCTFE(); |
| sce->setNoFree(); // needed for getMaxMinValue() |
| |
| /* Each enum member gets the sce scope |
| */ |
| for (size_t i = 0; i < members->dim; i++) |
| { |
| EnumMember *em = (*members)[i]->isEnumMember(); |
| if (em) |
| em->_scope = sce; |
| } |
| |
| if (!added) |
| { |
| /* addMember() is not called when the EnumDeclaration appears as a function statement, |
| * so we have to do what addMember() does and install the enum members in the right symbol |
| * table |
| */ |
| ScopeDsymbol *scopesym = NULL; |
| if (isAnonymous()) |
| { |
| /* Anonymous enum members get added to enclosing scope. |
| */ |
| for (Scope *sct = sce; 1; sct = sct->enclosing) |
| { |
| assert(sct); |
| if (sct->scopesym) |
| { |
| scopesym = sct->scopesym; |
| if (!sct->scopesym->symtab) |
| sct->scopesym->symtab = new DsymbolTable(); |
| break; |
| } |
| } |
| } |
| else |
| { |
| // Otherwise enum members are in the EnumDeclaration's symbol table |
| scopesym = this; |
| } |
| |
| for (size_t i = 0; i < members->dim; i++) |
| { |
| EnumMember *em = (*members)[i]->isEnumMember(); |
| if (em) |
| { |
| em->ed = this; |
| em->addMember(sc, scopesym); |
| } |
| } |
| } |
| |
| for (size_t i = 0; i < members->dim; i++) |
| { |
| EnumMember *em = (*members)[i]->isEnumMember(); |
| if (em) |
| em->semantic(em->_scope); |
| } |
| //printf("defaultval = %lld\n", defaultval); |
| |
| //if (defaultval) printf("defaultval: %s %s\n", defaultval->toChars(), defaultval->type->toChars()); |
| //printf("members = %s\n", members->toChars()); |
| } |
| |
| /****************************** |
| * Get the value of the .max/.min property as an Expression |
| * Input: |
| * id Id::max or Id::min |
| */ |
| |
| Expression *EnumDeclaration::getMaxMinValue(Loc loc, Identifier *id) |
| { |
| //printf("EnumDeclaration::getMaxValue()\n"); |
| bool first = true; |
| |
| Expression **pval = (id == Id::max) ? &maxval : &minval; |
| |
| if (inuse) |
| { |
| error(loc, "recursive definition of .%s property", id->toChars()); |
| goto Lerrors; |
| } |
| if (*pval) |
| goto Ldone; |
| |
| if (_scope) |
| semantic(_scope); |
| if (errors) |
| goto Lerrors; |
| if (semanticRun == PASSinit || !members) |
| { |
| if (isSpecial()) |
| { |
| /* Allow these special enums to not need a member list |
| */ |
| return memtype->getProperty(loc, id, 0); |
| } |
| |
| error("is forward referenced looking for .%s", id->toChars()); |
| goto Lerrors; |
| } |
| if (!(memtype && memtype->isintegral())) |
| { |
| error(loc, "has no .%s property because base type %s is not an integral type", |
| id->toChars(), |
| memtype ? memtype->toChars() : ""); |
| goto Lerrors; |
| } |
| |
| for (size_t i = 0; i < members->dim; i++) |
| { |
| EnumMember *em = (*members)[i]->isEnumMember(); |
| if (!em) |
| continue; |
| if (em->errors) |
| goto Lerrors; |
| |
| Expression *e = em->value(); |
| if (first) |
| { |
| *pval = e; |
| first = false; |
| } |
| else |
| { |
| /* In order to work successfully with UDTs, |
| * build expressions to do the comparisons, |
| * and let the semantic analyzer and constant |
| * folder give us the result. |
| */ |
| |
| /* Compute: |
| * if (e > maxval) |
| * maxval = e; |
| */ |
| Expression *ec = new CmpExp(id == Id::max ? TOKgt : TOKlt, em->loc, e, *pval); |
| inuse++; |
| ec = ::semantic(ec, em->_scope); |
| inuse--; |
| ec = ec->ctfeInterpret(); |
| if (ec->toInteger()) |
| *pval = e; |
| } |
| } |
| Ldone: |
| { |
| Expression *e = *pval; |
| if (e->op != TOKerror) |
| { |
| e = e->copy(); |
| e->loc = loc; |
| } |
| return e; |
| } |
| |
| Lerrors: |
| *pval = new ErrorExp(); |
| return *pval; |
| } |
| |
| /**************** |
| * Determine if enum is a 'special' one. |
| * Returns: |
| * true if special |
| */ |
| bool EnumDeclaration::isSpecial() const |
| { |
| return (ident == Id::__c_long || |
| ident == Id::__c_ulong || |
| ident == Id::__c_longlong || |
| ident == Id::__c_ulonglong || |
| ident == Id::__c_long_double) && memtype; |
| } |
| |
| Expression *EnumDeclaration::getDefaultValue(Loc loc) |
| { |
| //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); |
| if (defaultval) |
| return defaultval; |
| |
| if (_scope) |
| semantic(_scope); |
| if (errors) |
| goto Lerrors; |
| if (semanticRun == PASSinit || !members) |
| { |
| if (isSpecial()) |
| { |
| /* Allow these special enums to not need a member list |
| */ |
| return memtype->defaultInit(loc); |
| } |
| |
| error(loc, "forward reference of %s.init", toChars()); |
| goto Lerrors; |
| } |
| |
| for (size_t i = 0; i < members->dim; i++) |
| { |
| EnumMember *em = (*members)[i]->isEnumMember(); |
| if (em) |
| { |
| defaultval = em->value(); |
| return defaultval; |
| } |
| } |
| |
| Lerrors: |
| defaultval = new ErrorExp(); |
| return defaultval; |
| } |
| |
| Type *EnumDeclaration::getMemtype(Loc loc) |
| { |
| if (loc.linnum == 0) |
| loc = this->loc; |
| if (_scope) |
| { |
| /* Enum is forward referenced. We don't need to resolve the whole thing, |
| * just the base type |
| */ |
| if (memtype) |
| memtype = memtype->semantic(loc, _scope); |
| else |
| { |
| if (!isAnonymous() && members) |
| memtype = Type::tint32; |
| } |
| } |
| if (!memtype) |
| { |
| if (!isAnonymous() && members) |
| memtype = Type::tint32; |
| else |
| { |
| error(loc, "is forward referenced looking for base type"); |
| return Type::terror; |
| } |
| } |
| return memtype; |
| } |
| |
| bool EnumDeclaration::oneMember(Dsymbol **ps, Identifier *ident) |
| { |
| if (isAnonymous()) |
| return Dsymbol::oneMembers(members, ps, ident); |
| return Dsymbol::oneMember(ps, ident); |
| } |
| |
| Type *EnumDeclaration::getType() |
| { |
| return type; |
| } |
| |
| const char *EnumDeclaration::kind() const |
| { |
| return "enum"; |
| } |
| |
| bool EnumDeclaration::isDeprecated() |
| { |
| return isdeprecated; |
| } |
| |
| Prot EnumDeclaration::prot() |
| { |
| return protection; |
| } |
| |
| Dsymbol *EnumDeclaration::search(const Loc &loc, Identifier *ident, int flags) |
| { |
| //printf("%s.EnumDeclaration::search('%s')\n", toChars(), ident->toChars()); |
| if (_scope) |
| { |
| // Try one last time to resolve this enum |
| semantic(_scope); |
| } |
| |
| if (!members || !symtab || _scope) |
| { |
| error("is forward referenced when looking for '%s'", ident->toChars()); |
| //*(char*)0=0; |
| return NULL; |
| } |
| |
| Dsymbol *s = ScopeDsymbol::search(loc, ident, flags); |
| return s; |
| } |
| |
| /********************************* EnumMember ****************************/ |
| |
| EnumMember::EnumMember(Loc loc, Identifier *id, Expression *value, Type *origType) |
| : VarDeclaration(loc, NULL, id ? id : Id::empty, new ExpInitializer(loc, value)) |
| { |
| this->ed = NULL; |
| this->origValue = value; |
| this->origType = origType; |
| } |
| |
| Expression *&EnumMember::value() |
| { |
| return ((ExpInitializer*)_init)->exp; |
| } |
| |
| Dsymbol *EnumMember::syntaxCopy(Dsymbol *s) |
| { |
| assert(!s); |
| return new EnumMember(loc, ident, |
| value() ? value()->syntaxCopy() : NULL, |
| origType ? origType->syntaxCopy() : NULL); |
| } |
| |
| const char *EnumMember::kind() const |
| { |
| return "enum member"; |
| } |
| |
| void EnumMember::semantic(Scope *sc) |
| { |
| //printf("EnumMember::semantic() %s\n", toChars()); |
| if (errors || semanticRun >= PASSsemanticdone) |
| return; |
| if (semanticRun == PASSsemantic) |
| { |
| error("circular reference to enum member"); |
| Lerrors: |
| errors = true; |
| semanticRun = PASSsemanticdone; |
| return; |
| } |
| assert(ed); |
| ed->semantic(sc); |
| if (ed->errors) |
| goto Lerrors; |
| |
| if (errors || semanticRun >= PASSsemanticdone) |
| return; |
| |
| if (_scope) |
| sc = _scope; |
| if (!sc) |
| return; |
| |
| semanticRun = PASSsemantic; |
| |
| protection = ed->isAnonymous() ? ed->protection : Prot(PROTpublic); |
| linkage = LINKd; |
| storage_class = STCmanifest; |
| userAttribDecl = ed->isAnonymous() ? ed->userAttribDecl : NULL; |
| |
| // The first enum member is special |
| bool first = (this == (*ed->members)[0]); |
| |
| if (origType) |
| { |
| origType = origType->semantic(loc, sc); |
| type = origType; |
| assert(value()); // "type id;" is not a valid enum member declaration |
| } |
| |
| if (value()) |
| { |
| Expression *e = value(); |
| assert(e->dyncast() == DYNCAST_EXPRESSION); |
| e = ::semantic(e, sc); |
| e = resolveProperties(sc, e); |
| e = e->ctfeInterpret(); |
| if (e->op == TOKerror) |
| goto Lerrors; |
| if (first && !ed->memtype && !ed->isAnonymous()) |
| { |
| ed->memtype = e->type; |
| if (ed->memtype->ty == Terror) |
| { |
| ed->errors = true; |
| goto Lerrors; |
| } |
| if (ed->memtype->ty != Terror) |
| { |
| /* Bugzilla 11746: All of named enum members should have same type |
| * with the first member. If the following members were referenced |
| * during the first member semantic, their types should be unified. |
| */ |
| for (size_t i = 0; i < ed->members->dim; i++) |
| { |
| EnumMember *em = (*ed->members)[i]->isEnumMember(); |
| if (!em || em == this || em->semanticRun < PASSsemanticdone || em->origType) |
| continue; |
| |
| //printf("[%d] em = %s, em->semanticRun = %d\n", i, toChars(), em->semanticRun); |
| Expression *ev = em->value(); |
| ev = ev->implicitCastTo(sc, ed->memtype); |
| ev = ev->ctfeInterpret(); |
| ev = ev->castTo(sc, ed->type); |
| if (ev->op == TOKerror) |
| ed->errors = true; |
| em->value() = ev; |
| } |
| if (ed->errors) |
| { |
| ed->memtype = Type::terror; |
| goto Lerrors; |
| } |
| } |
| } |
| |
| if (ed->memtype && !origType) |
| { |
| e = e->implicitCastTo(sc, ed->memtype); |
| e = e->ctfeInterpret(); |
| |
| // save origValue for better json output |
| origValue = e; |
| |
| if (!ed->isAnonymous()) |
| { |
| e = e->castTo(sc, ed->type); |
| e = e->ctfeInterpret(); |
| } |
| } |
| else if (origType) |
| { |
| e = e->implicitCastTo(sc, origType); |
| e = e->ctfeInterpret(); |
| assert(ed->isAnonymous()); |
| |
| // save origValue for better json output |
| origValue = e; |
| } |
| value() = e; |
| } |
| else if (first) |
| { |
| Type *t; |
| if (ed->memtype) |
| t = ed->memtype; |
| else |
| { |
| t = Type::tint32; |
| if (!ed->isAnonymous()) |
| ed->memtype = t; |
| } |
| Expression *e = new IntegerExp(loc, 0, Type::tint32); |
| e = e->implicitCastTo(sc, t); |
| e = e->ctfeInterpret(); |
| |
| // save origValue for better json output |
| origValue = e; |
| |
| if (!ed->isAnonymous()) |
| { |
| e = e->castTo(sc, ed->type); |
| e = e->ctfeInterpret(); |
| } |
| value() = e; |
| } |
| else |
| { |
| /* Find the previous enum member, |
| * and set this to be the previous value + 1 |
| */ |
| EnumMember *emprev = NULL; |
| for (size_t i = 0; i < ed->members->dim; i++) |
| { |
| EnumMember *em = (*ed->members)[i]->isEnumMember(); |
| if (em) |
| { |
| if (em == this) |
| break; |
| emprev = em; |
| } |
| } |
| assert(emprev); |
| if (emprev->semanticRun < PASSsemanticdone) // if forward reference |
| emprev->semantic(emprev->_scope); // resolve it |
| if (emprev->errors) |
| goto Lerrors; |
| |
| Expression *eprev = emprev->value(); |
| Type *tprev = eprev->type->equals(ed->type) ? ed->memtype : eprev->type; |
| |
| Expression *emax = tprev->getProperty(ed->loc, Id::max, 0); |
| emax = ::semantic(emax, sc); |
| emax = emax->ctfeInterpret(); |
| |
| // Set value to (eprev + 1). |
| // But first check that (eprev != emax) |
| assert(eprev); |
| Expression *e = new EqualExp(TOKequal, loc, eprev, emax); |
| e = ::semantic(e, sc); |
| e = e->ctfeInterpret(); |
| if (e->toInteger()) |
| { |
| error("initialization with (%s.%s + 1) causes overflow for type '%s'", emprev->ed->toChars(), emprev->toChars(), ed->type->toBasetype()->toChars()); |
| goto Lerrors; |
| } |
| |
| // Now set e to (eprev + 1) |
| e = new AddExp(loc, eprev, new IntegerExp(loc, 1, Type::tint32)); |
| e = ::semantic(e, sc); |
| e = e->castTo(sc, eprev->type); |
| e = e->ctfeInterpret(); |
| |
| // save origValue (without cast) for better json output |
| if (e->op != TOKerror) // avoid duplicate diagnostics |
| { |
| assert(emprev->origValue); |
| origValue = new AddExp(loc, emprev->origValue, new IntegerExp(loc, 1, Type::tint32)); |
| origValue = ::semantic(origValue, sc); |
| origValue = origValue->ctfeInterpret(); |
| } |
| |
| if (e->op == TOKerror) |
| goto Lerrors; |
| if (e->type->isfloating()) |
| { |
| // Check that e != eprev (not always true for floats) |
| Expression *etest = new EqualExp(TOKequal, loc, e, eprev); |
| etest = ::semantic(etest, sc); |
| etest = etest->ctfeInterpret(); |
| if (etest->toInteger()) |
| { |
| error("has inexact value, due to loss of precision"); |
| goto Lerrors; |
| } |
| } |
| value() = e; |
| } |
| if (!origType) |
| type = value()->type; |
| |
| assert(origValue); |
| semanticRun = PASSsemanticdone; |
| } |
| |
| Expression *EnumMember::getVarExp(Loc loc, Scope *sc) |
| { |
| semantic(sc); |
| if (errors) |
| return new ErrorExp(); |
| Expression *e = new VarExp(loc, this); |
| return ::semantic(e, sc); |
| } |