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