/* Compiler implementation of the D programming language * Copyright (C) 1999-2021 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 "attrib.h" #include "mtype.h" #include "scope.h" #include "id.h" #include "expression.h" #include "module.h" #include "declaration.h" #include "init.h" bool isSpecialEnumIdent(const Identifier *ident) { return ident == Id::__c_long || ident == Id::__c_ulong || ident == Id::__c_longlong || ident == Id::__c_ulonglong || ident == Id::__c_long_double || ident == Id::__c_wchar_t || ident == Id::__c_complex_float || ident == Id::__c_complex_double || ident == Id::__c_complex_real; } /********************************* 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(Prot::undefined); 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->length; 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; } /****************************** * 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) dsymbolSemantic(this, _scope); if (errors) goto Lerrors; if (!members) { if (isSpecial()) { /* Allow these special enums to not need a member list */ return memtype->getProperty(loc, id, 0); } error(loc, "is opaque and has no `.%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->length; i++) { EnumMember *em = (*members)[i]->isEnumMember(); if (!em) continue; if (em->errors) { errors = true; continue; } if (em->semanticRun < PASSsemanticdone) { em->error("is forward referenced looking for `.%s`", id->toChars()); errors = true; continue; } if (first) { *pval = em->value(); 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 *e = em->value(); Expression *ec = new CmpExp(id == Id::max ? TOKgt : TOKlt, em->loc, e, *pval); inuse++; ec = expressionSemantic(ec, em->_scope); inuse--; ec = ec->ctfeInterpret(); if (ec->op == TOKerror) { errors = true; continue; } if (ec->toInteger()) *pval = e; } } if (errors) goto Lerrors; 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 isSpecialEnumIdent(ident) && memtype; } Expression *EnumDeclaration::getDefaultValue(Loc loc) { //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); if (defaultval) return defaultval; if (_scope) dsymbolSemantic(this, _scope); if (errors) goto Lerrors; if (!members) { if (isSpecial()) { /* Allow these special enums to not need a member list */ defaultval = memtype->defaultInit(loc); return defaultval; } error(loc, "is opaque and has no default initializer"); goto Lerrors; } for (size_t i = 0; i < members->length; i++) { EnumMember *em = (*members)[i]->isEnumMember(); if (em) { if (em->semanticRun < PASSsemanticdone) { error(loc, "forward reference of `%s.init`", toChars()); goto Lerrors; } 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 = typeSemantic(memtype, loc, _scope); } if (!memtype) { if (!isAnonymous() && (members || semanticRun >= PASSsemanticdone)) 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 dsymbolSemantic(this, _scope); } 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; } EnumMember::EnumMember(Loc loc, Identifier *id, Expression *value, Type *memType, StorageClass stc, UserAttributeDeclaration *uad, DeprecatedDeclaration *dd) : VarDeclaration(loc, NULL, id ? id : Id::empty, new ExpInitializer(loc, value)) { this->ed = NULL; this->origValue = value; this->origType = memType; this->storage_class = stc; this->userAttribDecl = uad; this->depdecl = dd; } 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"; } Expression *EnumMember::getVarExp(Loc loc, Scope *sc) { dsymbolSemantic(this, sc); if (errors) return new ErrorExp(); checkDisabled(loc, sc); if (depdecl && !depdecl->_scope) depdecl->_scope = sc; checkDeprecated(loc, sc); if (errors) return new ErrorExp(); Expression *e = new VarExp(loc, this); return expressionSemantic(e, sc); }