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