d: Merge upstream dmd, druntime 4ca4140e58, phobos 454dff14d.
[official-gcc.git] / gcc / d / dmd / dstruct.d
blob3268d5667de88675c4df6f5dd9eddadc6c5b358a
1 /**
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
14 module dmd.dstruct;
16 import dmd.aggregate;
17 import dmd.arraytypes;
18 import dmd.astenums;
19 import dmd.attrib;
20 import dmd.declaration;
21 import dmd.dmodule;
22 import dmd.dscope;
23 import dmd.dsymbol;
24 import dmd.dsymbolsem;
25 import dmd.dtemplate;
26 import dmd.errors;
27 import dmd.expression;
28 import dmd.func;
29 import dmd.globals;
30 import dmd.id;
31 import dmd.identifier;
32 import dmd.location;
33 import dmd.mtype;
34 import dmd.opover;
35 import dmd.target;
36 import dmd.tokens;
37 import dmd.typesem;
38 import dmd.typinf;
39 import dmd.visitor;
41 /***************************************
42 * Search sd for a member function of the form:
43 * `extern (D) string toString();`
44 * Params:
45 * sd = struct declaration to search
46 * Returns:
47 * FuncDeclaration of `toString()` if found, `null` if not
49 extern (C++) FuncDeclaration search_toString(StructDeclaration sd)
51 Dsymbol s = search_function(sd, Id.tostring);
52 FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
53 if (fd)
55 __gshared TypeFunction tftostring;
56 if (!tftostring)
58 tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d);
59 tftostring = tftostring.merge().toTypeFunction();
61 fd = fd.overloadExactMatch(tftostring);
63 return fd;
66 /***************************************
67 * Request additional semantic analysis for TypeInfo generation.
68 * Params:
69 * sc = context
70 * t = type that TypeInfo is being generated for
72 extern (C++) void semanticTypeInfo(Scope* sc, Type t)
74 if (sc)
76 if (sc.intypeof)
77 return;
78 if (sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock))
79 return;
82 if (!t)
83 return;
85 void visitVector(TypeVector t)
87 semanticTypeInfo(sc, t.basetype);
90 void visitAArray(TypeAArray t)
92 semanticTypeInfo(sc, t.index);
93 semanticTypeInfo(sc, t.next);
96 void visitStruct(TypeStruct t)
98 //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars());
99 StructDeclaration sd = t.sym;
101 /* Step 1: create TypeInfoDeclaration
103 if (!sc) // inline may request TypeInfo.
105 Scope scx;
106 scx._module = sd.getModule();
107 getTypeInfoType(sd.loc, t, &scx);
108 sd.requestTypeInfo = true;
110 else if (!sc.minst)
112 // don't yet have to generate TypeInfo instance if
113 // the typeid(T) expression exists in speculative scope.
115 else
117 getTypeInfoType(sd.loc, t, sc);
118 sd.requestTypeInfo = true;
120 // https://issues.dlang.org/show_bug.cgi?id=15149
121 // if the typeid operand type comes from a
122 // result of auto function, it may be yet speculative.
123 // unSpeculative(sc, sd);
126 /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later.
127 * This should be done even if typeid(T) exists in speculative scope.
128 * Because it may appear later in non-speculative scope.
130 if (!sd.members)
131 return; // opaque struct
132 if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd))
133 return; // none of TypeInfo-specific members
135 // If the struct is in a non-root module, run semantic3 to get
136 // correct symbols for the member function.
137 if (sd.semanticRun >= PASS.semantic3)
139 // semantic3 is already done
141 else if (TemplateInstance ti = sd.isInstantiated())
143 if (ti.minst && !ti.minst.isRoot())
144 Module.addDeferredSemantic3(sd);
146 else
148 if (sd.inNonRoot())
150 //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
151 Module.addDeferredSemantic3(sd);
156 void visitTuple(TypeTuple t)
158 if (t.arguments)
160 foreach (arg; *t.arguments)
162 semanticTypeInfo(sc, arg.type);
167 /* Note structural similarity of this Type walker to that in isSpeculativeType()
170 Type tb = t.toBasetype();
171 switch (tb.ty)
173 case Tvector: visitVector(tb.isTypeVector()); break;
174 case Taarray: visitAArray(tb.isTypeAArray()); break;
175 case Tstruct: visitStruct(tb.isTypeStruct()); break;
176 case Ttuple: visitTuple (tb.isTypeTuple()); break;
178 case Tclass:
179 case Tenum: break;
181 default: semanticTypeInfo(sc, tb.nextOf()); break;
185 enum StructFlags : int
187 none = 0x0,
188 hasPointers = 0x1, // NB: should use noPointers as in ClassFlags
191 /***********************************************************
192 * All `struct` declarations are an instance of this.
194 extern (C++) class StructDeclaration : AggregateDeclaration
196 FuncDeclarations postblits; // Array of postblit functions
197 FuncDeclaration postblit; // aggregate postblit
199 FuncDeclaration xeq; // TypeInfo_Struct.xopEquals
200 FuncDeclaration xcmp; // TypeInfo_Struct.xopCmp
201 FuncDeclaration xhash; // TypeInfo_Struct.xtoHash
202 extern (C++) __gshared FuncDeclaration xerreq; // object.xopEquals
203 extern (C++) __gshared FuncDeclaration xerrcmp; // object.xopCmp
205 // ABI-specific type(s) if the struct can be passed in registers
206 TypeTuple argTypes;
208 structalign_t alignment; // alignment applied outside of the struct
209 ThreeState ispod; // if struct is POD
211 // `bool` fields that are compacted into bit fields in a string mixin
212 private extern (D) static struct BitFields
214 bool zeroInit; // !=0 if initialize with 0 fill
215 bool hasIdentityAssign; // true if has identity opAssign
216 bool hasBlitAssign; // true if opAssign is a blit
217 bool hasIdentityEquals; // true if has identity opEquals
218 bool hasNoFields; // has no fields
219 bool hasCopyCtor; // copy constructor
220 bool hasPointerField; // members with indirections
221 bool hasVoidInitPointers; // void-initialized unsafe fields
222 bool hasSystemFields; // @system members
223 bool hasFieldWithInvariant; // invariants
224 bool computedTypeProperties;// the above 3 fields are computed
225 // Even if struct is defined as non-root symbol, some built-in operations
226 // (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
227 // For those, today TypeInfo_Struct is generated in COMDAT.
228 bool requestTypeInfo;
231 import dmd.common.bitfields : generateBitFields;
232 mixin(generateBitFields!(BitFields, ushort));
234 extern (D) this(const ref Loc loc, Identifier id, bool inObject)
236 super(loc, id);
237 zeroInit = false; // assume false until we do semantic processing
238 ispod = ThreeState.none;
239 // For forward references
240 type = new TypeStruct(this);
242 if (inObject)
244 if (id == Id.ModuleInfo && !Module.moduleinfo)
245 Module.moduleinfo = this;
249 static StructDeclaration create(const ref Loc loc, Identifier id, bool inObject)
251 return new StructDeclaration(loc, id, inObject);
254 override StructDeclaration syntaxCopy(Dsymbol s)
256 StructDeclaration sd =
257 s ? cast(StructDeclaration)s
258 : new StructDeclaration(loc, ident, false);
259 ScopeDsymbol.syntaxCopy(sd);
260 return sd;
263 override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
265 //printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
266 if (_scope && !symtab)
267 dsymbolSemantic(this, _scope);
269 if (!members || !symtab) // opaque or semantic() is not yet called
271 // .stringof is always defined (but may be hidden by some other symbol)
272 if(ident != Id.stringof && !(flags & IgnoreErrors) && semanticRun < PASS.semanticdone)
273 error("is forward referenced when looking for `%s`", ident.toChars());
274 return null;
277 return ScopeDsymbol.search(loc, ident, flags);
280 override const(char)* kind() const
282 return "struct";
285 override final void finalizeSize()
287 //printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
288 assert(sizeok != Sizeok.done);
290 if (sizeok == Sizeok.inProcess)
292 return;
294 sizeok = Sizeok.inProcess;
296 //printf("+StructDeclaration::finalizeSize() %s, fields.length = %d, sizeok = %d\n", toChars(), fields.length, sizeok);
298 fields.setDim(0); // workaround
300 // Set the offsets of the fields and determine the size of the struct
301 FieldState fieldState;
302 bool isunion = isUnionDeclaration() !is null;
303 for (size_t i = 0; i < members.length; i++)
305 Dsymbol s = (*members)[i];
306 s.setFieldOffset(this, fieldState, isunion);
308 if (type.ty == Terror)
310 errors = true;
311 return;
314 if (structsize == 0)
316 hasNoFields = true;
317 alignsize = 1;
319 // A fine mess of what size a zero sized struct should be
320 final switch (classKind)
322 case ClassKind.d:
323 case ClassKind.cpp:
324 structsize = 1;
325 break;
327 case ClassKind.c:
328 case ClassKind.objc:
329 if (target.c.bitFieldStyle == TargetC.BitFieldStyle.MS)
331 /* Undocumented MS behavior for:
332 * struct S { int :0; };
334 structsize = 4;
336 else if (target.c.bitFieldStyle == TargetC.BitFieldStyle.DM)
338 structsize = 0;
339 alignsize = 0;
341 else
342 structsize = 0;
343 break;
347 // Round struct size up to next alignsize boundary.
348 // This will ensure that arrays of structs will get their internals
349 // aligned properly.
350 if (alignment.isDefault() || alignment.isPack())
351 structsize = (structsize + alignsize - 1) & ~(alignsize - 1);
352 else
353 structsize = (structsize + alignment.get() - 1) & ~(alignment.get() - 1);
355 sizeok = Sizeok.done;
357 //printf("-StructDeclaration::finalizeSize() %s, fields.length = %d, structsize = %d\n", toChars(), fields.length, structsize);
359 if (errors)
360 return;
362 // Calculate fields[i].overlapped
363 if (checkOverlappedFields())
365 errors = true;
366 return;
369 // Determine if struct is all zeros or not
370 zeroInit = true;
371 foreach (vd; fields)
373 if (vd._init)
375 if (vd._init.isVoidInitializer())
376 /* Treat as 0 for the purposes of putting the initializer
377 * in the BSS segment, or doing a mass set to 0
379 continue;
381 // Zero size fields are zero initialized
382 if (vd.type.size(vd.loc) == 0)
383 continue;
385 // Examine init to see if it is all 0s.
386 auto exp = vd.getConstInitializer();
387 if (!exp || !_isZeroInit(exp))
389 zeroInit = false;
390 break;
393 else if (!vd.type.isZeroInit(loc))
395 zeroInit = false;
396 break;
401 argTypes = target.toArgTypes(type);
404 /// Compute cached type properties for `TypeStruct`
405 extern(D) final void determineTypeProperties()
407 if (computedTypeProperties)
408 return;
409 foreach (vd; fields)
411 if (vd.storage_class & STC.ref_ || vd.hasPointers())
412 hasPointerField = true;
414 if (vd._init && vd._init.isVoidInitializer() && vd.type.hasPointers())
415 hasVoidInitPointers = true;
417 if (vd.storage_class & STC.system || vd.type.hasSystemFields())
418 hasSystemFields = true;
420 if (!vd._init && vd.type.hasVoidInitPointers())
421 hasVoidInitPointers = true;
423 if (vd.type.hasInvariant())
424 hasFieldWithInvariant = true;
426 computedTypeProperties = true;
429 /***************************************
430 * Determine if struct is POD (Plain Old Data).
432 * POD is defined as:
433 * $(OL
434 * $(LI not nested)
435 * $(LI no postblits, destructors, or assignment operators)
436 * $(LI no `ref` fields or fields that are themselves non-POD)
438 * The idea being these are compatible with C structs.
440 * Returns:
441 * true if struct is POD
443 final bool isPOD()
445 // If we've already determined whether this struct is POD.
446 if (ispod != ThreeState.none)
447 return (ispod == ThreeState.yes);
449 ispod = ThreeState.yes;
451 if (enclosing || postblit || dtor || hasCopyCtor)
453 ispod = ThreeState.no;
454 return false;
457 // Recursively check all fields are POD.
458 for (size_t i = 0; i < fields.length; i++)
460 VarDeclaration v = fields[i];
461 if (v.storage_class & STC.ref_)
463 ispod = ThreeState.no;
464 return false;
467 Type tv = v.type.baseElemOf();
468 if (tv.ty == Tstruct)
470 TypeStruct ts = cast(TypeStruct)tv;
471 StructDeclaration sd = ts.sym;
472 if (!sd.isPOD())
474 ispod = ThreeState.no;
475 return false;
480 return (ispod == ThreeState.yes);
483 /***************************************
484 * Determine if struct has copy construction (copy constructor or postblit)
485 * Returns:
486 * true if struct has copy construction
488 final bool hasCopyConstruction()
490 return postblit || hasCopyCtor;
493 override final inout(StructDeclaration) isStructDeclaration() inout @nogc nothrow pure @safe
495 return this;
498 override void accept(Visitor v)
500 v.visit(this);
503 final uint numArgTypes() const
505 return argTypes && argTypes.arguments ? cast(uint) argTypes.arguments.length : 0;
508 final Type argType(uint index)
510 return index < numArgTypes() ? (*argTypes.arguments)[index].type : null;
514 /***************************************
515 * Verifies whether the struct declaration has a
516 * constructor that is not a copy constructor.
517 * Optionally, it can check whether the struct
518 * declaration has a regular constructor, that
519 * is not disabled.
521 * Params:
522 * checkDisabled = if the struct has a regular
523 non-disabled constructor
524 * Returns:
525 * true, if the struct has a regular (optionally,
526 * not disabled) constructor, false otherwise.
528 final bool hasRegularCtor(bool checkDisabled = false)
530 if (!ctor)
531 return false;
533 bool result;
534 overloadApply(ctor, (Dsymbol s)
536 if (auto td = s.isTemplateDeclaration())
538 if (checkDisabled && td.onemember)
540 if (auto ctorDecl = td.onemember.isCtorDeclaration())
542 if (ctorDecl.storage_class & STC.disable)
543 return 0;
546 result = true;
547 return 1;
549 if (auto ctorDecl = s.isCtorDeclaration())
551 if (!ctorDecl.isCpCtor && (!checkDisabled || !(ctorDecl.storage_class & STC.disable)))
553 result = true;
554 return 1;
557 return 0;
559 return result;
563 /**********************************
564 * Determine if exp is all binary zeros.
565 * Params:
566 * exp = expression to check
567 * Returns:
568 * true if it's all binary 0
570 private bool _isZeroInit(Expression exp)
572 switch (exp.op)
574 case EXP.int64:
575 return exp.toInteger() == 0;
577 case EXP.null_:
578 case EXP.false_:
579 return true;
581 case EXP.structLiteral:
583 auto sle = cast(StructLiteralExp) exp;
584 foreach (i; 0 .. sle.sd.fields.length)
586 auto field = sle.sd.fields[i];
587 if (field.type.size(field.loc))
589 auto e = (*sle.elements)[i];
590 if (e ? !_isZeroInit(e)
591 : !field.type.isZeroInit(field.loc))
592 return false;
595 return true;
598 case EXP.arrayLiteral:
600 auto ale = cast(ArrayLiteralExp)exp;
602 const dim = ale.elements ? ale.elements.length : 0;
604 if (ale.type.toBasetype().ty == Tarray) // if initializing a dynamic array
605 return dim == 0;
607 foreach (i; 0 .. dim)
609 if (!_isZeroInit(ale[i]))
610 return false;
613 /* Note that true is returned for all T[0]
615 return true;
618 case EXP.string_:
620 StringExp se = cast(StringExp)exp;
622 if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array
623 return se.len == 0;
625 foreach (i; 0 .. se.len)
627 if (se.getCodeUnit(i))
628 return false;
630 return true;
633 case EXP.vector:
635 auto ve = cast(VectorExp) exp;
636 return _isZeroInit(ve.e1);
639 case EXP.float64:
640 case EXP.complex80:
642 import dmd.root.ctfloat : CTFloat;
643 return (exp.toReal() is CTFloat.zero) &&
644 (exp.toImaginary() is CTFloat.zero);
647 default:
648 return false;
652 /***********************************************************
653 * Unions are a variation on structs.
655 extern (C++) final class UnionDeclaration : StructDeclaration
657 extern (D) this(const ref Loc loc, Identifier id)
659 super(loc, id, false);
662 override UnionDeclaration syntaxCopy(Dsymbol s)
664 assert(!s);
665 auto ud = new UnionDeclaration(loc, ident);
666 StructDeclaration.syntaxCopy(ud);
667 return ud;
670 override const(char)* kind() const
672 return "union";
675 override inout(UnionDeclaration) isUnionDeclaration() inout
677 return this;
680 override void accept(Visitor v)
682 v.visit(this);