1 /** 2 * Define `enum` declarations and `enum` members. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/enum.html, Enums) 5 * 6 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/denum.d, _denum.d) 10 * Documentation: https://dlang.org/phobos/dmd_denum.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/denum.d 12 * References: https://dlang.org/spec/enum.html 13 */ 14 15 module dmd.denum; 16 17 import core.stdc.stdio; 18 19 import dmd.attrib; 20 import dmd.gluelayer; 21 import dmd.declaration; 22 import dmd.dscope; 23 import dmd.dsymbol; 24 import dmd.dsymbolsem; 25 import dmd.expression; 26 import dmd.expressionsem; 27 import dmd.globals; 28 import dmd.id; 29 import dmd.identifier; 30 import dmd.init; 31 import dmd.mtype; 32 import dmd.tokens; 33 import dmd.typesem; 34 import dmd.visitor; 35 36 /*********************************************************** 37 * AST node for `EnumDeclaration` 38 * https://dlang.org/spec/enum.html#EnumDeclaration 39 */ 40 extern (C++) final class EnumDeclaration : ScopeDsymbol 41 { 42 /* The separate, and distinct, cases are: 43 * 1. enum { ... } 44 * 2. enum : memtype { ... } 45 * 3. enum id { ... } 46 * 4. enum id : memtype { ... } 47 * 5. enum id : memtype; 48 * 6. enum id; 49 */ 50 Type type; // the TypeEnum 51 Type memtype; // type of the members 52 53 Prot protection; 54 Expression maxval; 55 Expression minval; 56 Expression defaultval; // default initializer 57 bool isdeprecated; 58 bool added; 59 int inuse; 60 61 extern (D) this(const ref Loc loc, Identifier ident, Type memtype) 62 { 63 super(loc, ident); 64 //printf("EnumDeclaration() %s\n", toChars()); 65 type = new TypeEnum(this); 66 this.memtype = memtype; 67 protection = Prot(Prot.Kind.undefined); 68 } 69 70 override Dsymbol syntaxCopy(Dsymbol s) 71 { 72 assert(!s); 73 auto ed = new EnumDeclaration(loc, ident, memtype ? memtype.syntaxCopy() : null); 74 return ScopeDsymbol.syntaxCopy(ed); 75 } 76 77 override void addMember(Scope* sc, ScopeDsymbol sds) 78 { 79 version (none) 80 { 81 printf("EnumDeclaration::addMember() %s\n", toChars()); 82 for (size_t i = 0; i < members.dim; i++) 83 { 84 EnumMember em = (*members)[i].isEnumMember(); 85 printf(" member %s\n", em.toChars()); 86 } 87 } 88 89 /* Anonymous enum members get added to enclosing scope. 90 */ 91 ScopeDsymbol scopesym = isAnonymous() ? sds : this; 92 93 if (!isAnonymous()) 94 { 95 ScopeDsymbol.addMember(sc, sds); 96 if (!symtab) 97 symtab = new DsymbolTable(); 98 } 99 100 if (members) 101 { 102 for (size_t i = 0; i < members.dim; i++) 103 { 104 EnumMember em = (*members)[i].isEnumMember(); 105 em.ed = this; 106 //printf("add %s to scope %s\n", em.toChars(), scopesym.toChars()); 107 em.addMember(sc, isAnonymous() ? scopesym : this); 108 } 109 } 110 added = true; 111 } 112 113 override void setScope(Scope* sc) 114 { 115 if (semanticRun > PASS.init) 116 return; 117 ScopeDsymbol.setScope(sc); 118 } 119 120 override bool oneMember(Dsymbol* ps, Identifier ident) 121 { 122 if (isAnonymous()) 123 return Dsymbol.oneMembers(members, ps, ident); 124 return Dsymbol.oneMember(ps, ident); 125 } 126 127 override Type getType() 128 { 129 return type; 130 } 131 132 override const(char)* kind() const 133 { 134 return "enum"; 135 } 136 137 override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) 138 { 139 //printf("%s.EnumDeclaration::search('%s')\n", toChars(), ident.toChars()); 140 if (_scope) 141 { 142 // Try one last time to resolve this enum 143 dsymbolSemantic(this, _scope); 144 } 145 146 if (!members || !symtab || _scope) 147 { 148 error("is forward referenced when looking for `%s`", ident.toChars()); 149 //*(char*)0=0; 150 return null; 151 } 152 153 Dsymbol s = ScopeDsymbol.search(loc, ident, flags); 154 return s; 155 } 156 157 // is Dsymbol deprecated? 158 override bool isDeprecated() const 159 { 160 return isdeprecated; 161 } 162 163 override Prot prot() pure nothrow @nogc @safe 164 { 165 return protection; 166 } 167 168 /****************************** 169 * Get the value of the .max/.min property as an Expression. 170 * Lazily computes the value and caches it in maxval/minval. 171 * Reports any errors. 172 * Params: 173 * loc = location to use for error messages 174 * id = Id::max or Id::min 175 * Returns: 176 * corresponding value of .max/.min 177 */ 178 Expression getMaxMinValue(const ref Loc loc, Identifier id) 179 { 180 //printf("EnumDeclaration::getMaxValue()\n"); 181 182 static Expression pvalToResult(Expression e, const ref Loc loc) 183 { 184 if (e.op != TOK.error) 185 { 186 e = e.copy(); 187 e.loc = loc; 188 } 189 return e; 190 } 191 192 Expression* pval = (id == Id.max) ? &maxval : &minval; 193 194 Expression errorReturn() 195 { 196 *pval = new ErrorExp(); 197 return *pval; 198 } 199 200 if (inuse) 201 { 202 error(loc, "recursive definition of `.%s` property", id.toChars()); 203 return errorReturn(); 204 } 205 if (*pval) 206 return pvalToResult(*pval, loc); 207 208 if (_scope) 209 dsymbolSemantic(this, _scope); 210 if (errors) 211 return errorReturn(); 212 if (semanticRun == PASS.init || !members) 213 { 214 if (isSpecial()) 215 { 216 /* Allow these special enums to not need a member list 217 */ 218 return memtype.getProperty(_scope, loc, id, 0); 219 } 220 221 error("is forward referenced looking for `.%s`", id.toChars()); 222 return errorReturn(); 223 } 224 if (!(memtype && memtype.isintegral())) 225 { 226 error(loc, "has no `.%s` property because base type `%s` is not an integral type", id.toChars(), memtype ? memtype.toChars() : ""); 227 return errorReturn(); 228 } 229 230 bool first = true; 231 for (size_t i = 0; i < members.dim; i++) 232 { 233 EnumMember em = (*members)[i].isEnumMember(); 234 if (!em) 235 continue; 236 if (em.errors) 237 { 238 errors = true; 239 continue; 240 } 241 242 if (first) 243 { 244 *pval = em.value; 245 first = false; 246 } 247 else 248 { 249 /* In order to work successfully with UDTs, 250 * build expressions to do the comparisons, 251 * and let the semantic analyzer and constant 252 * folder give us the result. 253 */ 254 255 /* Compute: 256 * if (e > maxval) 257 * maxval = e; 258 */ 259 Expression e = em.value; 260 Expression ec = new CmpExp(id == Id.max ? TOK.greaterThan : TOK.lessThan, em.loc, e, *pval); 261 inuse++; 262 ec = ec.expressionSemantic(em._scope); 263 inuse--; 264 ec = ec.ctfeInterpret(); 265 if (ec.op == TOK.error) 266 { 267 errors = true; 268 continue; 269 } 270 if (ec.toInteger()) 271 *pval = e; 272 } 273 } 274 return errors ? errorReturn() : pvalToResult(*pval, loc); 275 } 276 277 /**************** 278 * Determine if enum is a special one. 279 * Returns: 280 * `true` if special 281 */ 282 bool isSpecial() const nothrow @nogc 283 { 284 return isSpecialEnumIdent(ident) && memtype; 285 } 286 287 Expression getDefaultValue(const ref Loc loc) 288 { 289 Expression handleErrors(){ 290 defaultval = new ErrorExp(); 291 return defaultval; 292 } 293 //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); 294 if (defaultval) 295 return defaultval; 296 297 if (_scope) 298 dsymbolSemantic(this, _scope); 299 if (errors) 300 return handleErrors(); 301 if (semanticRun == PASS.init || !members) 302 { 303 if (isSpecial()) 304 { 305 /* Allow these special enums to not need a member list 306 */ 307 return memtype.defaultInit(loc); 308 } 309 310 error(loc, "forward reference of `%s.init`", toChars()); 311 return handleErrors(); 312 } 313 314 foreach (const i; 0 .. members.dim) 315 { 316 EnumMember em = (*members)[i].isEnumMember(); 317 if (em) 318 { 319 defaultval = em.value; 320 return defaultval; 321 } 322 } 323 return handleErrors(); 324 } 325 326 Type getMemtype(const ref Loc loc) 327 { 328 if (_scope) 329 { 330 /* Enum is forward referenced. We don't need to resolve the whole thing, 331 * just the base type 332 */ 333 if (memtype) 334 { 335 Loc locx = loc.isValid() ? loc : this.loc; 336 memtype = memtype.typeSemantic(locx, _scope); 337 } 338 else 339 { 340 if (!isAnonymous() && members) 341 memtype = Type.tint32; 342 } 343 } 344 if (!memtype) 345 { 346 if (!isAnonymous() && members) 347 memtype = Type.tint32; 348 else 349 { 350 Loc locx = loc.isValid() ? loc : this.loc; 351 error(locx, "is forward referenced looking for base type"); 352 return Type.terror; 353 } 354 } 355 return memtype; 356 } 357 358 override inout(EnumDeclaration) isEnumDeclaration() inout 359 { 360 return this; 361 } 362 363 Symbol* sinit; 364 365 override void accept(Visitor v) 366 { 367 v.visit(this); 368 } 369 } 370 371 /*********************************************************** 372 * AST node representing a member of an enum. 373 * https://dlang.org/spec/enum.html#EnumMember 374 * https://dlang.org/spec/enum.html#AnonymousEnumMember 375 */ 376 extern (C++) final class EnumMember : VarDeclaration 377 { 378 /* Can take the following forms: 379 * 1. id 380 * 2. id = value 381 * 3. type id = value 382 */ 383 @property ref value() { return (cast(ExpInitializer)_init).exp; } 384 385 // A cast() is injected to 'value' after dsymbolSemantic(), 386 // but 'origValue' will preserve the original value, 387 // or previous value + 1 if none was specified. 388 Expression origValue; 389 390 Type origType; 391 392 EnumDeclaration ed; 393 394 extern (D) this(const ref Loc loc, Identifier id, Expression value, Type origType) 395 { 396 super(loc, null, id ? id : Id.empty, new ExpInitializer(loc, value)); 397 this.origValue = value; 398 this.origType = origType; 399 } 400 401 extern(D) this(Loc loc, Identifier id, Expression value, Type memtype, 402 StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd) 403 { 404 this(loc, id, value, memtype); 405 storage_class = stc; 406 userAttribDecl = uad; 407 depdecl = dd; 408 } 409 410 override Dsymbol syntaxCopy(Dsymbol s) 411 { 412 assert(!s); 413 return new EnumMember( 414 loc, ident, 415 value ? value.syntaxCopy() : null, 416 origType ? origType.syntaxCopy() : null, 417 storage_class, 418 userAttribDecl ? cast(UserAttributeDeclaration)userAttribDecl.syntaxCopy(s) : null, 419 depdecl ? cast(DeprecatedDeclaration)depdecl.syntaxCopy(s) : null); 420 } 421 422 override const(char)* kind() const 423 { 424 return "enum member"; 425 } 426 427 Expression getVarExp(const ref Loc loc, Scope* sc) 428 { 429 dsymbolSemantic(this, sc); 430 if (errors) 431 return new ErrorExp(); 432 checkDisabled(loc, sc); 433 434 if (depdecl && !depdecl._scope) 435 depdecl._scope = sc; 436 checkDeprecated(loc, sc); 437 438 if (errors) 439 return new ErrorExp(); 440 Expression e = new VarExp(loc, this); 441 return e.expressionSemantic(sc); 442 } 443 444 override inout(EnumMember) isEnumMember() inout 445 { 446 return this; 447 } 448 449 override void accept(Visitor v) 450 { 451 v.visit(this); 452 } 453 } 454 455 /****************************************** 456 * Check for special enum names. 457 * 458 * Special enum names are used by the C++ name mangler to represent 459 * C++ types that are not basic D types. 460 * Params: 461 * ident = identifier to check for specialness 462 * Returns: 463 * `true` if it is special 464 */ 465 bool isSpecialEnumIdent(const Identifier ident) @nogc nothrow 466 { 467 return ident == Id.__c_long || 468 ident == Id.__c_ulong || 469 ident == Id.__c_longlong || 470 ident == Id.__c_ulonglong || 471 ident == Id.__c_long_double || 472 ident == Id.__c_wchar_t; 473 } 474 475