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 }