d: Merge upstream dmd, druntime 4c18eed967, phobos d945686a4.
[official-gcc.git] / gcc / d / dmd / dstruct.d
blob56aad3eece3b07a462a6e427d5c7148fe7a21118
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 core.stdc.stdio;
18 import dmd.aggregate;
19 import dmd.arraytypes;
20 import dmd.astenums;
21 import dmd.attrib;
22 import dmd.declaration;
23 import dmd.dmodule;
24 import dmd.dscope;
25 import dmd.dsymbol;
26 import dmd.dsymbolsem;
27 import dmd.dtemplate;
28 import dmd.errors;
29 import dmd.expression;
30 import dmd.func;
31 import dmd.globals;
32 import dmd.id;
33 import dmd.identifier;
34 import dmd.location;
35 import dmd.mtype;
36 import dmd.opover;
37 import dmd.target;
38 import dmd.tokens;
39 import dmd.typesem;
40 import dmd.typinf;
41 import dmd.visitor;
43 /***************************************
44 * Search sd for a member function of the form:
45 * `extern (D) string toString();`
46 * Params:
47 * sd = struct declaration to search
48 * Returns:
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;
55 if (fd)
57 __gshared TypeFunction tftostring;
58 if (!tftostring)
60 tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d);
61 tftostring = tftostring.merge().toTypeFunction();
63 fd = fd.overloadExactMatch(tftostring);
65 return fd;
68 /***************************************
69 * Request additional semantic analysis for TypeInfo generation.
70 * Params:
71 * sc = context
72 * t = type that TypeInfo is being generated for
74 extern (C++) void semanticTypeInfo(Scope* sc, Type t)
76 if (sc)
78 if (sc.intypeof)
79 return;
80 if (!sc.needsCodegen())
81 return;
84 if (!t)
85 return;
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.
107 Scope scx;
108 scx.eSink = global.errorSink;
109 scx._module = sd.getModule();
110 getTypeInfoType(sd.loc, t, &scx);
111 sd.requestTypeInfo = true;
113 else if (!sc.minst)
115 // don't yet have to generate TypeInfo instance if
116 // the typeid(T) expression exists in speculative scope.
118 else
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.
133 if (!sd.members)
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);
149 else
151 if (sd.inNonRoot())
153 //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
154 Module.addDeferredSemantic3(sd);
159 void visitTuple(TypeTuple t)
161 if (t.arguments)
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();
174 switch (tb.ty)
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;
181 case Tclass:
182 case Tenum: break;
184 default: semanticTypeInfo(sc, tb.nextOf()); break;
188 enum StructFlags : int
190 none = 0x0,
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
209 TypeTuple argTypes;
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)
239 super(loc, id);
240 zeroInit = false; // assume false until we do semantic processing
241 ispod = ThreeState.none;
242 // For forward references
243 type = new TypeStruct(this);
245 if (inObject)
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);
263 return 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(loc, "%s `%s` is forward referenced when looking for `%s`", kind, toPrettyChars, ident.toChars());
277 return null;
280 return ScopeDsymbol.search(loc, ident, flags);
283 override const(char)* kind() const
285 return "struct";
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)
295 return;
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)
313 errors = true;
314 return;
317 if (structsize == 0)
319 hasNoFields = true;
320 alignsize = 1;
322 // A fine mess of what size a zero sized struct should be
323 final switch (classKind)
325 case ClassKind.d:
326 case ClassKind.cpp:
327 structsize = 1;
328 break;
330 case ClassKind.c:
331 case ClassKind.objc:
332 if (target.c.bitFieldStyle == TargetC.BitFieldStyle.MS)
334 /* Undocumented MS behavior for:
335 * struct S { int :0; };
337 structsize = 4;
339 else if (target.c.bitFieldStyle == TargetC.BitFieldStyle.DM)
341 structsize = 0;
342 alignsize = 0;
344 else
345 structsize = 0;
346 break;
350 // Round struct size up to next alignsize boundary.
351 // This will ensure that arrays of structs will get their internals
352 // aligned properly.
353 if (alignment.isDefault() || alignment.isPack())
354 structsize = (structsize + alignsize - 1) & ~(alignsize - 1);
355 else
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);
362 if (errors)
363 return;
365 // Calculate fields[i].overlapped
366 if (checkOverlappedFields())
368 errors = true;
369 return;
372 // Determine if struct is all zeros or not
373 zeroInit = true;
374 foreach (vd; fields)
376 if (vd._init)
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
382 continue;
384 // Zero size fields are zero initialized
385 if (vd.type.size(vd.loc) == 0)
386 continue;
388 // Examine init to see if it is all 0s.
389 auto exp = vd.getConstInitializer();
390 if (!exp || !_isZeroInit(exp))
392 zeroInit = false;
393 break;
396 else if (!vd.type.isZeroInit(loc))
398 zeroInit = false;
399 break;
404 argTypes = target.toArgTypes(type);
407 /// Compute cached type properties for `TypeStruct`
408 extern(D) final void determineTypeProperties()
410 if (computedTypeProperties)
411 return;
412 foreach (vd; fields)
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).
435 * POD is defined as:
436 * $(OL
437 * $(LI not nested)
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.
443 * Returns:
444 * true if struct is POD
446 final bool isPOD()
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;
457 return false;
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;
467 return false;
470 Type tv = v.type.baseElemOf();
471 if (tv.ty == Tstruct)
473 TypeStruct ts = cast(TypeStruct)tv;
474 StructDeclaration sd = ts.sym;
475 if (!sd.isPOD())
477 ispod = ThreeState.no;
478 return false;
483 return (ispod == ThreeState.yes);
486 /***************************************
487 * Determine if struct has copy construction (copy constructor or postblit)
488 * Returns:
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
498 return this;
501 override void accept(Visitor v)
503 v.visit(this);
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
522 * is not disabled.
524 * Params:
525 * checkDisabled = if the struct has a regular
526 non-disabled constructor
527 * Returns:
528 * true, if the struct has a regular (optionally,
529 * not disabled) constructor, false otherwise.
531 final bool hasRegularCtor(bool checkDisabled = false)
533 if (!ctor)
534 return false;
536 bool result;
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)
546 return 0;
549 result = true;
550 return 1;
552 if (auto ctorDecl = s.isCtorDeclaration())
554 if (!ctorDecl.isCpCtor && (!checkDisabled || !(ctorDecl.storage_class & STC.disable)))
556 result = true;
557 return 1;
560 return 0;
562 return result;
566 /**********************************
567 * Determine if exp is all binary zeros.
568 * Params:
569 * exp = expression to check
570 * Returns:
571 * true if it's all binary 0
573 bool _isZeroInit(Expression exp)
575 switch (exp.op)
577 case EXP.int64:
578 return exp.toInteger() == 0;
580 case EXP.null_:
581 return true;
583 case EXP.structLiteral:
585 auto sle = exp.isStructLiteralExp();
586 if (sle.sd.isNested())
587 return false;
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))
597 return false;
600 return true;
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
610 return dim == 0;
612 foreach (i; 0 .. dim)
614 if (!_isZeroInit(ale[i]))
615 return false;
618 /* Note that true is returned for all T[0]
620 return true;
623 case EXP.string_:
625 StringExp se = cast(StringExp)exp;
627 if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array
628 return se.len == 0;
630 foreach (i; 0 .. se.len)
632 if (se.getCodeUnit(i))
633 return false;
635 return true;
638 case EXP.vector:
640 auto ve = cast(VectorExp) exp;
641 return _isZeroInit(ve.e1);
644 case EXP.float64:
645 case EXP.complex80:
647 import dmd.root.ctfloat : CTFloat;
648 return (exp.toReal() is CTFloat.zero) &&
649 (exp.toImaginary() is CTFloat.zero);
652 default:
653 return false;
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)
669 assert(!s);
670 auto ud = new UnionDeclaration(loc, ident);
671 StructDeclaration.syntaxCopy(ud);
672 return ud;
675 override const(char)* kind() const
677 return "union";
680 override inout(UnionDeclaration) isUnionDeclaration() inout
682 return this;
685 override void accept(Visitor v)
687 v.visit(this);