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