1 /**
2  * Struct and union declarations.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions)
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/dstruct.d, _dstruct.d)
10  * Documentation:  https://dlang.org/phobos/dmd_dstruct.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dstruct.d
12  */
13 
14 module dmd.dstruct;
15 
16 import core.stdc.stdio;
17 
18 import dmd.aggregate;
19 import dmd.arraytypes;
20 import dmd.astenums;
21 import dmd.attrib;
22 import dmd.declaration;
23 import dmd.dmodule;
24 import dmd.dscope;
25 import dmd.dsymbol;
26 import dmd.dsymbolsem;
27 import dmd.dtemplate;
28 import dmd.errors;
29 import dmd.expression;
30 import dmd.func;
31 import dmd.globals;
32 import dmd.id;
33 import dmd.identifier;
34 import dmd.location;
35 import dmd.mtype;
36 import dmd.opover;
37 import dmd.target;
38 import dmd.tokens;
39 import dmd.typesem;
40 import dmd.typinf;
41 import dmd.visitor;
42 
43 /***************************************
44  * Search sd for a member function of the form:
45  *   `extern (D) string toString();`
46  * Params:
47  *   sd = struct declaration to search
48  * Returns:
49  *   FuncDeclaration of `toString()` if found, `null` if not
50  */
51 extern (C++) FuncDeclaration search_toString(StructDeclaration sd)
52 {
53     Dsymbol s = search_function(sd, Id.tostring);
54     FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
55     if (fd)
56     {
57         __gshared TypeFunction tftostring;
58         if (!tftostring)
59         {
60             tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d);
61             tftostring = tftostring.merge().toTypeFunction();
62         }
63         fd = fd.overloadExactMatch(tftostring);
64     }
65     return fd;
66 }
67 
68 /***************************************
69  * Request additional semantic analysis for TypeInfo generation.
70  * Params:
71  *      sc = context
72  *      t = type that TypeInfo is being generated for
73  */
74 extern (D) void semanticTypeInfo(Scope* sc, Type t)
75 {
76     if (sc)
77     {
78         if (sc.intypeof)
79             return;
80         if (!sc.needsCodegen())
81             return;
82     }
83 
84     if (!t)
85         return;
86 
87     void visitVector(TypeVector t)
88     {
89         semanticTypeInfo(sc, t.basetype);
90     }
91 
92     void visitAArray(TypeAArray t)
93     {
94         semanticTypeInfo(sc, t.index);
95         semanticTypeInfo(sc, t.next);
96     }
97 
98     void visitStruct(TypeStruct t)
99     {
100         //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars());
101         StructDeclaration sd = t.sym;
102 
103         /* Step 1: create TypeInfoDeclaration
104          */
105         if (!sc) // inline may request TypeInfo.
106         {
107             Scope scx;
108             scx.eSink = global.errorSink;
109             scx._module = sd.getModule();
110             getTypeInfoType(sd.loc, t, &scx);
111             sd.requestTypeInfo = true;
112         }
113         else if (!sc.minst)
114         {
115             // don't yet have to generate TypeInfo instance if
116             // the typeid(T) expression exists in speculative scope.
117         }
118         else
119         {
120             getTypeInfoType(sd.loc, t, sc);
121             sd.requestTypeInfo = true;
122 
123             // https://issues.dlang.org/show_bug.cgi?id=15149
124             // if the typeid operand type comes from a
125             // result of auto function, it may be yet speculative.
126             // unSpeculative(sc, sd);
127         }
128 
129         /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later.
130          * This should be done even if typeid(T) exists in speculative scope.
131          * Because it may appear later in non-speculative scope.
132          */
133         if (!sd.members)
134             return; // opaque struct
135         if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd))
136             return; // none of TypeInfo-specific members
137 
138         // If the struct is in a non-root module, run semantic3 to get
139         // correct symbols for the member function.
140         if (sd.semanticRun >= PASS.semantic3)
141         {
142             // semantic3 is already done
143         }
144         else if (TemplateInstance ti = sd.isInstantiated())
145         {
146             if (ti.minst && !ti.minst.isRoot())
147                 Module.addDeferredSemantic3(sd);
148         }
149         else
150         {
151             if (sd.inNonRoot())
152             {
153                 //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
154                 Module.addDeferredSemantic3(sd);
155             }
156         }
157     }
158 
159     void visitTuple(TypeTuple t)
160     {
161         if (t.arguments)
162         {
163             foreach (arg; *t.arguments)
164             {
165                 semanticTypeInfo(sc, arg.type);
166             }
167         }
168     }
169 
170     /* Note structural similarity of this Type walker to that in isSpeculativeType()
171      */
172 
173     Type tb = t.toBasetype();
174     switch (tb.ty)
175     {
176         case Tvector:   visitVector(tb.isTypeVector()); break;
177         case Taarray:   visitAArray(tb.isTypeAArray()); break;
178         case Tstruct:   visitStruct(tb.isTypeStruct()); break;
179         case Ttuple:    visitTuple (tb.isTypeTuple());  break;
180 
181         case Tclass:
182         case Tenum:     break;
183 
184         default:        semanticTypeInfo(sc, tb.nextOf()); break;
185     }
186 }
187 
188 enum StructFlags : int
189 {
190     none        = 0x0,
191     hasPointers = 0x1, // NB: should use noPointers as in ClassFlags
192 }
193 
194 /***********************************************************
195  * All `struct` declarations are an instance of this.
196  */
197 extern (C++) class StructDeclaration : AggregateDeclaration
198 {
199     FuncDeclarations postblits; // Array of postblit functions
200     FuncDeclaration postblit;   // aggregate postblit
201 
202     FuncDeclaration xeq;        // TypeInfo_Struct.xopEquals
203     FuncDeclaration xcmp;       // TypeInfo_Struct.xopCmp
204     FuncDeclaration xhash;      // TypeInfo_Struct.xtoHash
205     extern (C++) __gshared FuncDeclaration xerreq;   // object.xopEquals
206     extern (C++) __gshared FuncDeclaration xerrcmp;  // object.xopCmp
207 
208     // ABI-specific type(s) if the struct can be passed in registers
209     TypeTuple argTypes;
210 
211     structalign_t alignment;    // alignment applied outside of the struct
212     ThreeState ispod;           // if struct is POD
213 
214     // `bool` fields that are compacted into bit fields in a string mixin
215     private extern (D) static struct BitFields
216     {
217         bool zeroInit;              // !=0 if initialize with 0 fill
218         bool hasIdentityAssign;     // true if has identity opAssign
219         bool hasBlitAssign;         // true if opAssign is a blit
220         bool hasIdentityEquals;     // true if has identity opEquals
221         bool hasNoFields;           // has no fields
222         bool hasCopyCtor;           // copy constructor
223         bool hasPointerField;       // members with indirections
224         bool hasVoidInitPointers;   // void-initialized unsafe fields
225         bool hasSystemFields;      // @system members
226         bool hasFieldWithInvariant; // invariants
227         bool computedTypeProperties;// the above 3 fields are computed
228         // Even if struct is defined as non-root symbol, some built-in operations
229         // (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
230         // For those, today TypeInfo_Struct is generated in COMDAT.
231         bool requestTypeInfo;
232     }
233 
234     import dmd.common.bitfields : generateBitFields;
235     mixin(generateBitFields!(BitFields, ushort));
236 
237     extern (D) this(const ref Loc loc, Identifier id, bool inObject)
238     {
239         super(loc, id);
240         zeroInit = false; // assume false until we do semantic processing
241         ispod = ThreeState.none;
242         // For forward references
243         type = new TypeStruct(this);
244 
245         if (inObject)
246         {
247             if (id == Id.ModuleInfo && !Module.moduleinfo)
248                 Module.moduleinfo = this;
249         }
250     }
251 
252     static StructDeclaration create(const ref Loc loc, Identifier id, bool inObject)
253     {
254         return new StructDeclaration(loc, id, inObject);
255     }
256 
257     override StructDeclaration syntaxCopy(Dsymbol s)
258     {
259         StructDeclaration sd =
260             s ? cast(StructDeclaration)s
261               : new StructDeclaration(loc, ident, false);
262         ScopeDsymbol.syntaxCopy(sd);
263         return sd;
264     }
265 
266     override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
267     {
268         //printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
269         if (_scope && !symtab)
270             dsymbolSemantic(this, _scope);
271 
272         if (!members || !symtab) // opaque or semantic() is not yet called
273         {
274             // .stringof is always defined (but may be hidden by some other symbol)
275             if(ident != Id.stringof && !(flags & IgnoreErrors) && semanticRun < PASS.semanticdone)
276                 .error(loc, "%s `%s` is forward referenced when looking for `%s`", kind, toPrettyChars, ident.toChars());
277             return null;
278         }
279 
280         return ScopeDsymbol.search(loc, ident, flags);
281     }
282 
283     override const(char)* kind() const
284     {
285         return "struct";
286     }
287 
288     override final void finalizeSize()
289     {
290         //printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
291         assert(sizeok != Sizeok.done);
292 
293         if (sizeok == Sizeok.inProcess)
294         {
295             return;
296         }
297         sizeok = Sizeok.inProcess;
298 
299         //printf("+StructDeclaration::finalizeSize() %s, fields.length = %d, sizeok = %d\n", toChars(), fields.length, sizeok);
300 
301         fields.setDim(0);   // workaround
302 
303         // Set the offsets of the fields and determine the size of the struct
304         FieldState fieldState;
305         bool isunion = isUnionDeclaration() !is null;
306         for (size_t i = 0; i < members.length; i++)
307         {
308             Dsymbol s = (*members)[i];
309             s.setFieldOffset(this, fieldState, isunion);
310         }
311         if (type.ty == Terror)
312         {
313             errors = true;
314             return;
315         }
316 
317         if (structsize == 0)
318         {
319             hasNoFields = true;
320             alignsize = 1;
321 
322             // A fine mess of what size a zero sized struct should be
323             final switch (classKind)
324             {
325                 case ClassKind.d:
326                 case ClassKind.cpp:
327                     structsize = 1;
328                     break;
329 
330                 case ClassKind.c:
331                 case ClassKind.objc:
332                     if (target.c.bitFieldStyle == TargetC.BitFieldStyle.MS)
333                     {
334                         /* Undocumented MS behavior for:
335                          *   struct S { int :0; };
336                          */
337                         structsize = 4;
338                     }
339                     else if (target.c.bitFieldStyle == TargetC.BitFieldStyle.DM)
340                     {
341                         structsize = 0;
342                         alignsize = 0;
343                     }
344                     else
345                         structsize = 0;
346                     break;
347             }
348         }
349 
350         // Round struct size up to next alignsize boundary.
351         // This will ensure that arrays of structs will get their internals
352         // aligned properly.
353         if (alignment.isDefault() || alignment.isPack())
354             structsize = (structsize + alignsize - 1) & ~(alignsize - 1);
355         else
356             structsize = (structsize + alignment.get() - 1) & ~(alignment.get() - 1);
357 
358         sizeok = Sizeok.done;
359 
360         //printf("-StructDeclaration::finalizeSize() %s, fields.length = %d, structsize = %d\n", toChars(), cast(int)fields.length, cast(int)structsize);
361 
362         if (errors)
363             return;
364 
365         // Calculate fields[i].overlapped
366         if (checkOverlappedFields())
367         {
368             errors = true;
369             return;
370         }
371 
372         // Determine if struct is all zeros or not
373         zeroInit = true;
374         foreach (vd; fields)
375         {
376             if (vd._init)
377             {
378                 if (vd._init.isVoidInitializer())
379                     /* Treat as 0 for the purposes of putting the initializer
380                      * in the BSS segment, or doing a mass set to 0
381                      */
382                     continue;
383 
384                 // Zero size fields are zero initialized
385                 if (vd.type.size(vd.loc) == 0)
386                     continue;
387 
388                 // Examine init to see if it is all 0s.
389                 auto exp = vd.getConstInitializer();
390                 if (!exp || !_isZeroInit(exp))
391                 {
392                     zeroInit = false;
393                     break;
394                 }
395             }
396             else if (!vd.type.isZeroInit(loc))
397             {
398                 zeroInit = false;
399                 break;
400             }
401         }
402 
403 
404         argTypes = target.toArgTypes(type);
405     }
406 
407     /// Compute cached type properties for `TypeStruct`
408     extern(D) final void determineTypeProperties()
409     {
410         if (computedTypeProperties)
411             return;
412         foreach (vd; fields)
413         {
414             if (vd.storage_class & STC.ref_ || vd.hasPointers())
415                 hasPointerField = true;
416 
417             if (vd._init && vd._init.isVoidInitializer() && vd.type.hasPointers())
418                 hasVoidInitPointers = true;
419 
420             if (vd.storage_class & STC.system || vd.type.hasSystemFields())
421                 hasSystemFields = true;
422 
423             if (!vd._init && vd.type.hasVoidInitPointers())
424                 hasVoidInitPointers = true;
425 
426             if (vd.type.hasInvariant())
427                 hasFieldWithInvariant = true;
428         }
429         computedTypeProperties = true;
430     }
431 
432     /***************************************
433      * Determine if struct is POD (Plain Old Data).
434      *
435      * POD is defined as:
436      *      $(OL
437      *      $(LI not nested)
438      *      $(LI no postblits, destructors, or assignment operators)
439      *      $(LI no `ref` fields or fields that are themselves non-POD)
440      *      )
441      * The idea being these are compatible with C structs.
442      *
443      * Returns:
444      *     true if struct is POD
445      */
446     final bool isPOD()
447     {
448         // If we've already determined whether this struct is POD.
449         if (ispod != ThreeState.none)
450             return (ispod == ThreeState.yes);
451 
452         ispod = ThreeState.yes;
453 
454         if (enclosing || postblit || dtor || hasCopyCtor)
455         {
456             ispod = ThreeState.no;
457             return false;
458         }
459 
460         // Recursively check all fields are POD.
461         for (size_t i = 0; i < fields.length; i++)
462         {
463             VarDeclaration v = fields[i];
464             if (v.storage_class & STC.ref_)
465             {
466                 ispod = ThreeState.no;
467                 return false;
468             }
469 
470             Type tv = v.type.baseElemOf();
471             if (tv.ty == Tstruct)
472             {
473                 TypeStruct ts = cast(TypeStruct)tv;
474                 StructDeclaration sd = ts.sym;
475                 if (!sd.isPOD())
476                 {
477                     ispod = ThreeState.no;
478                     return false;
479                 }
480             }
481         }
482 
483         return (ispod == ThreeState.yes);
484     }
485 
486     /***************************************
487      * Determine if struct has copy construction (copy constructor or postblit)
488      * Returns:
489      *     true if struct has copy construction
490      */
491     final bool hasCopyConstruction()
492     {
493         return postblit || hasCopyCtor;
494     }
495 
496     override final inout(StructDeclaration) isStructDeclaration() inout @nogc nothrow pure @safe
497     {
498         return this;
499     }
500 
501     override void accept(Visitor v)
502     {
503         v.visit(this);
504     }
505 
506     final uint numArgTypes() const
507     {
508         return argTypes && argTypes.arguments ? cast(uint) argTypes.arguments.length : 0;
509     }
510 
511     final Type argType(uint index)
512     {
513         return index < numArgTypes() ? (*argTypes.arguments)[index].type : null;
514     }
515 
516 
517     /***************************************
518      * Verifies whether the struct declaration has a
519      * constructor that is not a copy constructor.
520      * Optionally, it can check whether the struct
521      * declaration has a regular constructor, that
522      * is not disabled.
523      *
524      * Params:
525      *      checkDisabled = if the struct has a regular
526                             non-disabled constructor
527      * Returns:
528      *      true, if the struct has a regular (optionally,
529      *      not disabled) constructor, false otherwise.
530      */
531     final bool hasRegularCtor(bool checkDisabled = false)
532     {
533         if (!ctor)
534             return false;
535 
536         bool result;
537         overloadApply(ctor, (Dsymbol s)
538         {
539             if (auto td = s.isTemplateDeclaration())
540             {
541                 if (checkDisabled && td.onemember)
542                 {
543                     if (auto ctorDecl = td.onemember.isCtorDeclaration())
544                     {
545                         if (ctorDecl.storage_class & STC.disable)
546                             return 0;
547                     }
548                 }
549                 result = true;
550                 return 1;
551             }
552             if (auto ctorDecl = s.isCtorDeclaration())
553             {
554                 if (!ctorDecl.isCpCtor && (!checkDisabled || !(ctorDecl.storage_class & STC.disable)))
555                 {
556                     result = true;
557                     return 1;
558                 }
559             }
560             return 0;
561         });
562         return result;
563     }
564 }
565 
566 /**********************************
567  * Determine if exp is all binary zeros.
568  * Params:
569  *      exp = expression to check
570  * Returns:
571  *      true if it's all binary 0
572  */
573 bool _isZeroInit(Expression exp)
574 {
575     switch (exp.op)
576     {
577         case EXP.int64:
578             return exp.toInteger() == 0;
579 
580         case EXP.null_:
581             return true;
582 
583         case EXP.structLiteral:
584         {
585             auto sle = exp.isStructLiteralExp();
586             if (sle.sd.isNested())
587                 return false;
588             const isCstruct = sle.sd.isCsymbol();  // C structs are default initialized to all zeros
589             foreach (i; 0 .. sle.sd.fields.length)
590             {
591                 auto field = sle.sd.fields[i];
592                 if (field.type.size(field.loc))
593                 {
594                     auto e = sle.elements && i < sle.elements.length ? (*sle.elements)[i] : null;
595                     if (e ? !_isZeroInit(e)
596                           : !isCstruct && !field.type.isZeroInit(field.loc))
597                         return false;
598                 }
599             }
600             return true;
601         }
602 
603         case EXP.arrayLiteral:
604         {
605             auto ale = cast(ArrayLiteralExp)exp;
606 
607             const dim = ale.elements ? ale.elements.length : 0;
608 
609             if (ale.type.toBasetype().ty == Tarray) // if initializing a dynamic array
610                 return dim == 0;
611 
612             foreach (i; 0 .. dim)
613             {
614                 if (!_isZeroInit(ale[i]))
615                     return false;
616             }
617 
618             /* Note that true is returned for all T[0]
619              */
620             return true;
621         }
622 
623         case EXP.string_:
624         {
625             StringExp se = cast(StringExp)exp;
626 
627             if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array
628                 return se.len == 0;
629 
630             foreach (i; 0 .. se.len)
631             {
632                 if (se.getCodeUnit(i))
633                     return false;
634             }
635             return true;
636         }
637 
638         case EXP.vector:
639         {
640             auto ve = cast(VectorExp) exp;
641             return _isZeroInit(ve.e1);
642         }
643 
644         case EXP.float64:
645         case EXP.complex80:
646         {
647             import dmd.root.ctfloat : CTFloat;
648             return (exp.toReal()      is CTFloat.zero) &&
649                    (exp.toImaginary() is CTFloat.zero);
650         }
651 
652         default:
653             return false;
654     }
655 }
656 
657 /***********************************************************
658  * Unions are a variation on structs.
659  */
660 extern (C++) final class UnionDeclaration : StructDeclaration
661 {
662     extern (D) this(const ref Loc loc, Identifier id)
663     {
664         super(loc, id, false);
665     }
666 
667     override UnionDeclaration syntaxCopy(Dsymbol s)
668     {
669         assert(!s);
670         auto ud = new UnionDeclaration(loc, ident);
671         StructDeclaration.syntaxCopy(ud);
672         return ud;
673     }
674 
675     override const(char)* kind() const
676     {
677         return "union";
678     }
679 
680     override inout(UnionDeclaration) isUnionDeclaration() inout
681     {
682         return this;
683     }
684 
685     override void accept(Visitor v)
686     {
687         v.visit(this);
688     }
689 }