2 * Struct and union declarations.
4 * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions)
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
16 import core
.stdc
.stdio
;
19 import dmd
.arraytypes
;
22 import dmd
.declaration
;
26 import dmd
.dsymbolsem
;
29 import dmd
.expression
;
33 import dmd
.identifier
;
43 /***************************************
44 * Search sd for a member function of the form:
45 * `extern (D) string toString();`
47 * sd = struct declaration to search
49 * FuncDeclaration of `toString()` if found, `null` if not
51 extern (C
++) FuncDeclaration
search_toString(StructDeclaration sd
)
53 Dsymbol s
= search_function(sd
, Id
.tostring
);
54 FuncDeclaration fd
= s ? s
.isFuncDeclaration() : null;
57 __gshared TypeFunction tftostring
;
60 tftostring
= new TypeFunction(ParameterList(), Type
.tstring
, LINK
.d
);
61 tftostring
= tftostring
.merge().toTypeFunction();
63 fd
= fd
.overloadExactMatch(tftostring
);
68 /***************************************
69 * Request additional semantic analysis for TypeInfo generation.
72 * t = type that TypeInfo is being generated for
74 extern (C
++) void semanticTypeInfo(Scope
* sc
, Type t
)
80 if (!sc
.needsCodegen())
87 void visitVector(TypeVector t
)
89 semanticTypeInfo(sc
, t
.basetype
);
92 void visitAArray(TypeAArray t
)
94 semanticTypeInfo(sc
, t
.index
);
95 semanticTypeInfo(sc
, t
.next
);
98 void visitStruct(TypeStruct t
)
100 //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars());
101 StructDeclaration sd
= t
.sym
;
103 /* Step 1: create TypeInfoDeclaration
105 if (!sc
) // inline may request TypeInfo.
108 scx
.eSink
= global
.errorSink
;
109 scx
._module
= sd
.getModule();
110 getTypeInfoType(sd
.loc
, t
, &scx
);
111 sd
.requestTypeInfo
= true;
115 // don't yet have to generate TypeInfo instance if
116 // the typeid(T) expression exists in speculative scope.
120 getTypeInfoType(sd
.loc
, t
, sc
);
121 sd
.requestTypeInfo
= true;
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);
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.
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
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
)
142 // semantic3 is already done
144 else if (TemplateInstance ti
= sd
.isInstantiated())
146 if (ti
.minst
&& !ti
.minst
.isRoot())
147 Module
.addDeferredSemantic3(sd
);
153 //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
154 Module
.addDeferredSemantic3(sd
);
159 void visitTuple(TypeTuple t
)
163 foreach (arg
; *t
.arguments
)
165 semanticTypeInfo(sc
, arg
.type
);
170 /* Note structural similarity of this Type walker to that in isSpeculativeType()
173 Type tb
= t
.toBasetype();
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;
184 default: semanticTypeInfo(sc
, tb
.nextOf()); break;
188 enum StructFlags
: int
191 hasPointers
= 0x1, // NB: should use noPointers as in ClassFlags
194 /***********************************************************
195 * All `struct` declarations are an instance of this.
197 extern (C
++) class StructDeclaration
: AggregateDeclaration
199 FuncDeclarations postblits
; // Array of postblit functions
200 FuncDeclaration postblit
; // aggregate postblit
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
208 // ABI-specific type(s) if the struct can be passed in registers
211 structalign_t alignment
; // alignment applied outside of the struct
212 ThreeState ispod
; // if struct is POD
214 // `bool` fields that are compacted into bit fields in a string mixin
215 private extern (D
) static struct BitFields
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
;
234 import dmd
.common
.bitfields
: generateBitFields
;
235 mixin(generateBitFields
!(BitFields
, ushort));
237 extern (D
) this(const ref Loc loc
, Identifier id
, bool inObject
)
240 zeroInit
= false; // assume false until we do semantic processing
241 ispod
= ThreeState
.none
;
242 // For forward references
243 type
= new TypeStruct(this);
247 if (id
== Id
.ModuleInfo
&& !Module
.moduleinfo
)
248 Module
.moduleinfo
= this;
252 static StructDeclaration
create(const ref Loc loc
, Identifier id
, bool inObject
)
254 return new StructDeclaration(loc
, id
, inObject
);
257 override StructDeclaration
syntaxCopy(Dsymbol s
)
259 StructDeclaration sd
=
260 s ?
cast(StructDeclaration
)s
261 : new StructDeclaration(loc
, ident
, false);
262 ScopeDsymbol
.syntaxCopy(sd
);
266 override final Dsymbol
search(const ref Loc loc
, Identifier ident
, int flags
= SearchLocalsOnly
)
268 //printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
269 if (_scope
&& !symtab
)
270 dsymbolSemantic(this, _scope
);
272 if (!members ||
!symtab
) // opaque or semantic() is not yet called
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("is forward referenced when looking for `%s`", ident
.toChars());
280 return ScopeDsymbol
.search(loc
, ident
, flags
);
283 override const(char)* kind() const
288 override final void finalizeSize()
290 //printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
291 assert(sizeok
!= Sizeok
.done
);
293 if (sizeok
== Sizeok
.inProcess
)
297 sizeok
= Sizeok
.inProcess
;
299 //printf("+StructDeclaration::finalizeSize() %s, fields.length = %d, sizeok = %d\n", toChars(), fields.length, sizeok);
301 fields
.setDim(0); // workaround
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
++)
308 Dsymbol s
= (*members
)[i
];
309 s
.setFieldOffset(this, fieldState
, isunion
);
311 if (type
.ty
== Terror
)
322 // A fine mess of what size a zero sized struct should be
323 final switch (classKind
)
332 if (target
.c
.bitFieldStyle
== TargetC
.BitFieldStyle
.MS
)
334 /* Undocumented MS behavior for:
335 * struct S { int :0; };
339 else if (target
.c
.bitFieldStyle
== TargetC
.BitFieldStyle
.DM
)
350 // Round struct size up to next alignsize boundary.
351 // This will ensure that arrays of structs will get their internals
353 if (alignment
.isDefault() || alignment
.isPack())
354 structsize
= (structsize
+ alignsize
- 1) & ~(alignsize
- 1);
356 structsize
= (structsize
+ alignment
.get() - 1) & ~(alignment
.get() - 1);
358 sizeok
= Sizeok
.done
;
360 //printf("-StructDeclaration::finalizeSize() %s, fields.length = %d, structsize = %d\n", toChars(), fields.length, structsize);
365 // Calculate fields[i].overlapped
366 if (checkOverlappedFields())
372 // Determine if struct is all zeros or not
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
384 // Zero size fields are zero initialized
385 if (vd
.type
.size(vd
.loc
) == 0)
388 // Examine init to see if it is all 0s.
389 auto exp
= vd
.getConstInitializer();
390 if (!exp ||
!_isZeroInit(exp
))
396 else if (!vd
.type
.isZeroInit(loc
))
404 argTypes
= target
.toArgTypes(type
);
407 /// Compute cached type properties for `TypeStruct`
408 extern(D
) final void determineTypeProperties()
410 if (computedTypeProperties
)
414 if (vd
.storage_class
& STC
.ref_ || vd
.hasPointers())
415 hasPointerField
= true;
417 if (vd
._init
&& vd
._init
.isVoidInitializer() && vd
.type
.hasPointers())
418 hasVoidInitPointers
= true;
420 if (vd
.storage_class
& STC
.system || vd
.type
.hasSystemFields())
421 hasSystemFields
= true;
423 if (!vd
._init
&& vd
.type
.hasVoidInitPointers())
424 hasVoidInitPointers
= true;
426 if (vd
.type
.hasInvariant())
427 hasFieldWithInvariant
= true;
429 computedTypeProperties
= true;
432 /***************************************
433 * Determine if struct is POD (Plain Old Data).
438 * $(LI no postblits, destructors, or assignment operators)
439 * $(LI no `ref` fields or fields that are themselves non-POD)
441 * The idea being these are compatible with C structs.
444 * true if struct is POD
448 // If we've already determined whether this struct is POD.
449 if (ispod
!= ThreeState
.none
)
450 return (ispod
== ThreeState
.yes
);
452 ispod
= ThreeState
.yes
;
454 if (enclosing || postblit || dtor || hasCopyCtor
)
456 ispod
= ThreeState
.no
;
460 // Recursively check all fields are POD.
461 for (size_t i
= 0; i
< fields
.length
; i
++)
463 VarDeclaration v
= fields
[i
];
464 if (v
.storage_class
& STC
.ref_
)
466 ispod
= ThreeState
.no
;
470 Type tv
= v
.type
.baseElemOf();
471 if (tv
.ty
== Tstruct
)
473 TypeStruct ts
= cast(TypeStruct
)tv
;
474 StructDeclaration sd
= ts
.sym
;
477 ispod
= ThreeState
.no
;
483 return (ispod
== ThreeState
.yes
);
486 /***************************************
487 * Determine if struct has copy construction (copy constructor or postblit)
489 * true if struct has copy construction
491 final bool hasCopyConstruction()
493 return postblit || hasCopyCtor
;
496 override final inout(StructDeclaration
) isStructDeclaration() inout @nogc nothrow pure @safe
501 override void accept(Visitor v
)
506 final uint numArgTypes() const
508 return argTypes
&& argTypes
.arguments ?
cast(uint) argTypes
.arguments
.length
: 0;
511 final Type
argType(uint index
)
513 return index
< numArgTypes() ?
(*argTypes
.arguments
)[index
].type
: null;
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
525 * checkDisabled = if the struct has a regular
526 non-disabled constructor
528 * true, if the struct has a regular (optionally,
529 * not disabled) constructor, false otherwise.
531 final bool hasRegularCtor(bool checkDisabled
= false)
537 overloadApply(ctor
, (Dsymbol s
)
539 if (auto td
= s
.isTemplateDeclaration())
541 if (checkDisabled
&& td
.onemember
)
543 if (auto ctorDecl
= td
.onemember
.isCtorDeclaration())
545 if (ctorDecl
.storage_class
& STC
.disable
)
552 if (auto ctorDecl
= s
.isCtorDeclaration())
554 if (!ctorDecl
.isCpCtor
&& (!checkDisabled ||
!(ctorDecl
.storage_class
& STC
.disable
)))
566 /**********************************
567 * Determine if exp is all binary zeros.
569 * exp = expression to check
571 * true if it's all binary 0
573 bool _isZeroInit(Expression exp
)
578 return exp
.toInteger() == 0;
583 case EXP
.structLiteral
:
585 auto sle
= exp
.isStructLiteralExp();
586 if (sle
.sd
.isNested())
588 const isCstruct
= sle
.sd
.isCsymbol(); // C structs are default initialized to all zeros
589 foreach (i
; 0 .. sle
.sd
.fields
.length
)
591 auto field
= sle
.sd
.fields
[i
];
592 if (field
.type
.size(field
.loc
))
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
))
603 case EXP
.arrayLiteral
:
605 auto ale
= cast(ArrayLiteralExp
)exp
;
607 const dim
= ale
.elements ? ale
.elements
.length
: 0;
609 if (ale
.type
.toBasetype().ty
== Tarray
) // if initializing a dynamic array
612 foreach (i
; 0 .. dim
)
614 if (!_isZeroInit(ale
[i
]))
618 /* Note that true is returned for all T[0]
625 StringExp se
= cast(StringExp
)exp
;
627 if (se
.type
.toBasetype().ty
== Tarray
) // if initializing a dynamic array
630 foreach (i
; 0 .. se
.len
)
632 if (se
.getCodeUnit(i
))
640 auto ve
= cast(VectorExp
) exp
;
641 return _isZeroInit(ve
.e1
);
647 import dmd
.root
.ctfloat
: CTFloat
;
648 return (exp
.toReal() is CTFloat
.zero
) &&
649 (exp
.toImaginary() is CTFloat
.zero
);
657 /***********************************************************
658 * Unions are a variation on structs.
660 extern (C
++) final class UnionDeclaration
: StructDeclaration
662 extern (D
) this(const ref Loc loc
, Identifier id
)
664 super(loc
, id
, false);
667 override UnionDeclaration
syntaxCopy(Dsymbol s
)
670 auto ud
= new UnionDeclaration(loc
, ident
);
671 StructDeclaration
.syntaxCopy(ud
);
675 override const(char)* kind() const
680 override inout(UnionDeclaration
) isUnionDeclaration() inout
685 override void accept(Visitor v
)