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-2023 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 https://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.astenums;
20 import dmd.attrib;
21 import dmd.errors;
22 import dmd.gluelayer;
23 import dmd.declaration;
24 import dmd.dscope;
25 import dmd.dsymbol;
26 import dmd.dsymbolsem;
27 import dmd.expression;
28 import dmd.id;
29 import dmd.identifier;
30 import dmd.init;
31 import dmd.location;
32 import dmd.mtype;
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 
58     // `bool` fields that are compacted into bit fields in a string mixin
59     private extern (D) static struct BitFields
60     {
61         bool isdeprecated;
62         bool added;
63         bool inuse;
64     }
65 
66     import dmd.common.bitfields : generateBitFields;
67     mixin(generateBitFields!(BitFields, ubyte));
68 
69     extern (D) this(const ref Loc loc, Identifier ident, Type memtype)
70     {
71         super(loc, ident);
72         //printf("EnumDeclaration() %p %s : %s\n", this, toChars(), memtype.toChars());
73         type = new TypeEnum(this);
74         this.memtype = memtype;
75         visibility = Visibility(Visibility.Kind.undefined);
76     }
77 
78     override EnumDeclaration syntaxCopy(Dsymbol s)
79     {
80         assert(!s);
81         auto ed = new EnumDeclaration(loc, ident, memtype ? memtype.syntaxCopy() : null);
82         ScopeDsymbol.syntaxCopy(ed);
83         return ed;
84     }
85 
86     override void addMember(Scope* sc, ScopeDsymbol sds)
87     {
88         version (none)
89         {
90             printf("EnumDeclaration::addMember() %s\n", toChars());
91             for (size_t i = 0; i < members.length; i++)
92             {
93                 EnumMember em = (*members)[i].isEnumMember();
94                 printf("    member %s\n", em.toChars());
95             }
96         }
97         if (!isAnonymous())
98         {
99             ScopeDsymbol.addMember(sc, sds);
100         }
101 
102         addEnumMembersToSymtab(this, sc, sds);
103     }
104 
105     override void setScope(Scope* sc)
106     {
107         if (semanticRun > PASS.initial)
108             return;
109         ScopeDsymbol.setScope(sc);
110     }
111 
112     override bool oneMember(Dsymbol* ps, Identifier ident)
113     {
114         if (isAnonymous())
115             return Dsymbol.oneMembers(members, ps, ident);
116         return Dsymbol.oneMember(ps, ident);
117     }
118 
119     override Type getType()
120     {
121         return type;
122     }
123 
124     override const(char)* kind() const
125     {
126         return "enum";
127     }
128 
129     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
130     {
131         //printf("%s.EnumDeclaration::search('%s')\n", toChars(), ident.toChars());
132         if (_scope)
133         {
134             // Try one last time to resolve this enum
135             dsymbolSemantic(this, _scope);
136         }
137 
138         Dsymbol s = ScopeDsymbol.search(loc, ident, flags);
139         return s;
140     }
141 
142     // is Dsymbol deprecated?
143     override bool isDeprecated() const
144     {
145         return isdeprecated;
146     }
147 
148     override Visibility visible() pure nothrow @nogc @safe
149     {
150         return visibility;
151     }
152 
153 
154     /****************
155      * Determine if enum is a special one.
156      * Returns:
157      *  `true` if special
158      */
159     bool isSpecial() const nothrow @nogc
160     {
161         return isSpecialEnumIdent(ident) && memtype;
162     }
163 
164     Expression getDefaultValue(const ref Loc loc)
165     {
166         Expression handleErrors(){
167             defaultval = ErrorExp.get();
168             return defaultval;
169         }
170         //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars());
171         // https://issues.dlang.org/show_bug.cgi?id=23904
172         // Return defaultval only if it is not ErrorExp.
173         // A speculative context may set defaultval to ErrorExp;
174         // subsequent non-speculative contexts need to be able
175         // to print the error.
176         if (defaultval && !defaultval.isErrorExp())
177             return defaultval;
178 
179         if (isCsymbol())
180             return memtype.defaultInit(loc, true);
181 
182         if (_scope)
183             dsymbolSemantic(this, _scope);
184         if (errors)
185             return handleErrors();
186         if (!members)
187         {
188             if (isSpecial())
189             {
190                 /* Allow these special enums to not need a member list
191                  */
192                 return defaultval = memtype.defaultInit(loc);
193             }
194 
195             error(loc, "%s `%s` is opaque and has no default initializer", kind, toPrettyChars);
196             return handleErrors();
197         }
198 
199         foreach (const i; 0 .. members.length)
200         {
201             EnumMember em = (*members)[i].isEnumMember();
202             if (em)
203             {
204                 if (em.semanticRun < PASS.semanticdone)
205                 {
206                     error(loc, "%s `%s` forward reference of `%s.init`", kind, toPrettyChars, toChars());
207                     return handleErrors();
208                 }
209 
210                 defaultval = em.value;
211                 return defaultval;
212             }
213         }
214         return handleErrors();
215     }
216 
217     Type getMemtype(const ref Loc loc)
218     {
219         if (_scope)
220         {
221             /* Enum is forward referenced. We don't need to resolve the whole thing,
222              * just the base type
223              */
224             if (memtype)
225             {
226                 Loc locx = loc.isValid() ? loc : this.loc;
227                 memtype = memtype.typeSemantic(locx, _scope);
228             }
229             else
230             {
231                 // Run semantic to get the type from a possible first member value
232                 dsymbolSemantic(this, _scope);
233             }
234         }
235         if (!memtype)
236         {
237             if (!isAnonymous() && (members || semanticRun >= PASS.semanticdone))
238                 memtype = Type.tint32;
239             else
240             {
241                 Loc locx = loc.isValid() ? loc : this.loc;
242                 error(locx, "is forward referenced looking for base type");
243                 return Type.terror;
244             }
245         }
246         return memtype;
247     }
248 
249     override inout(EnumDeclaration) isEnumDeclaration() inout
250     {
251         return this;
252     }
253 
254     Symbol* sinit;
255 
256     override void accept(Visitor v)
257     {
258         v.visit(this);
259     }
260 }
261 
262 /***********************************************************
263  * AST node representing a member of an enum.
264  * https://dlang.org/spec/enum.html#EnumMember
265  * https://dlang.org/spec/enum.html#AnonymousEnumMember
266  */
267 extern (C++) final class EnumMember : VarDeclaration
268 {
269     /* Can take the following forms:
270      *  1. id
271      *  2. id = value
272      *  3. type id = value
273      */
274     @property ref value() { return (cast(ExpInitializer)_init).exp; }
275 
276     // A cast() is injected to 'value' after dsymbolSemantic(),
277     // but 'origValue' will preserve the original value,
278     // or previous value + 1 if none was specified.
279     Expression origValue;
280 
281     Type origType;
282 
283     EnumDeclaration ed;
284 
285     extern (D) this(const ref Loc loc, Identifier id, Expression value, Type origType)
286     {
287         super(loc, null, id ? id : Id.empty, new ExpInitializer(loc, value));
288         this.origValue = value;
289         this.origType = origType;
290     }
291 
292     extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
293         StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
294     {
295         this(loc, id, value, memtype);
296         storage_class = stc;
297         userAttribDecl = uad;
298         depdecl = dd;
299     }
300 
301     override EnumMember syntaxCopy(Dsymbol s)
302     {
303         assert(!s);
304         return new EnumMember(
305             loc, ident,
306             value ? value.syntaxCopy() : null,
307             origType ? origType.syntaxCopy() : null,
308             storage_class,
309             userAttribDecl ? userAttribDecl.syntaxCopy(s) : null,
310             depdecl ? depdecl.syntaxCopy(s) : null);
311     }
312 
313     override const(char)* kind() const
314     {
315         return "enum member";
316     }
317 
318     override inout(EnumMember) isEnumMember() inout
319     {
320         return this;
321     }
322 
323     override void accept(Visitor v)
324     {
325         v.visit(this);
326     }
327 }
328 
329 /******************************************
330  * Check for special enum names.
331  *
332  * Special enum names are used by the C++ name mangler to represent
333  * C++ types that are not basic D types.
334  * Params:
335  *      ident = identifier to check for specialness
336  * Returns:
337  *      `true` if it is special
338  */
339 bool isSpecialEnumIdent(const Identifier ident) @nogc nothrow
340 {
341     return  ident == Id.__c_long ||
342             ident == Id.__c_ulong ||
343             ident == Id.__c_longlong ||
344             ident == Id.__c_ulonglong ||
345             ident == Id.__c_long_double ||
346             ident == Id.__c_wchar_t ||
347             ident == Id.__c_complex_float ||
348             ident == Id.__c_complex_double ||
349             ident == Id.__c_complex_real;
350 }