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