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 = ErrorExp.get();
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 = ErrorExp.get();
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 ErrorExp.get();
432         checkDisabled(loc, sc);
433 
434         if (depdecl && !depdecl._scope)
435             depdecl._scope = sc;
436         checkDeprecated(loc, sc);
437 
438         if (errors)
439             return ErrorExp.get();
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 }