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