| /** | |
| * Struct and union declarations. | |
| * | |
| * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions) | |
| * | |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dstruct.d, _dstruct.d) | |
| * Documentation: https://dlang.org/phobos/dmd_dstruct.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dstruct.d | |
| */ | |
| module dmd.dstruct; | |
| import dmd.aggregate; | |
| import dmd.arraytypes; | |
| import dmd.astenums; | |
| import dmd.attrib; | |
| import dmd.declaration; | |
| import dmd.dmodule; | |
| import dmd.dscope; | |
| import dmd.dsymbol; | |
| import dmd.dsymbolsem; | |
| import dmd.dtemplate; | |
| import dmd.errors; | |
| import dmd.expression; | |
| import dmd.func; | |
| import dmd.globals; | |
| import dmd.id; | |
| import dmd.identifier; | |
| import dmd.mtype; | |
| import dmd.opover; | |
| import dmd.target; | |
| import dmd.tokens; | |
| import dmd.typesem; | |
| import dmd.typinf; | |
| import dmd.visitor; | |
| /*************************************** | |
| * Search sd for a member function of the form: | |
| * `extern (D) string toString();` | |
| * Params: | |
| * sd = struct declaration to search | |
| * Returns: | |
| * FuncDeclaration of `toString()` if found, `null` if not | |
| */ | |
| extern (C++) FuncDeclaration search_toString(StructDeclaration sd) | |
| { | |
| Dsymbol s = search_function(sd, Id.tostring); | |
| FuncDeclaration fd = s ? s.isFuncDeclaration() : null; | |
| if (fd) | |
| { | |
| __gshared TypeFunction tftostring; | |
| if (!tftostring) | |
| { | |
| tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d); | |
| tftostring = tftostring.merge().toTypeFunction(); | |
| } | |
| fd = fd.overloadExactMatch(tftostring); | |
| } | |
| return fd; | |
| } | |
| /*************************************** | |
| * Request additional semantic analysis for TypeInfo generation. | |
| * Params: | |
| * sc = context | |
| * t = type that TypeInfo is being generated for | |
| */ | |
| extern (C++) void semanticTypeInfo(Scope* sc, Type t) | |
| { | |
| if (sc) | |
| { | |
| if (sc.intypeof) | |
| return; | |
| if (sc.flags & (SCOPE.ctfe | SCOPE.compile)) | |
| return; | |
| } | |
| if (!t) | |
| return; | |
| void visitVector(TypeVector t) | |
| { | |
| semanticTypeInfo(sc, t.basetype); | |
| } | |
| void visitAArray(TypeAArray t) | |
| { | |
| semanticTypeInfo(sc, t.index); | |
| semanticTypeInfo(sc, t.next); | |
| } | |
| void visitStruct(TypeStruct t) | |
| { | |
| //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars()); | |
| StructDeclaration sd = t.sym; | |
| /* Step 1: create TypeInfoDeclaration | |
| */ | |
| if (!sc) // inline may request TypeInfo. | |
| { | |
| Scope scx; | |
| scx._module = sd.getModule(); | |
| getTypeInfoType(sd.loc, t, &scx); | |
| sd.requestTypeInfo = true; | |
| } | |
| else if (!sc.minst) | |
| { | |
| // don't yet have to generate TypeInfo instance if | |
| // the typeid(T) expression exists in speculative scope. | |
| } | |
| else | |
| { | |
| getTypeInfoType(sd.loc, t, sc); | |
| sd.requestTypeInfo = true; | |
| // https://issues.dlang.org/show_bug.cgi?id=15149 | |
| // if the typeid operand type comes from a | |
| // result of auto function, it may be yet speculative. | |
| // unSpeculative(sc, sd); | |
| } | |
| /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later. | |
| * This should be done even if typeid(T) exists in speculative scope. | |
| * Because it may appear later in non-speculative scope. | |
| */ | |
| if (!sd.members) | |
| return; // opaque struct | |
| if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd)) | |
| return; // none of TypeInfo-specific members | |
| // If the struct is in a non-root module, run semantic3 to get | |
| // correct symbols for the member function. | |
| if (sd.semanticRun >= PASS.semantic3) | |
| { | |
| // semantic3 is already done | |
| } | |
| else if (TemplateInstance ti = sd.isInstantiated()) | |
| { | |
| if (ti.minst && !ti.minst.isRoot()) | |
| Module.addDeferredSemantic3(sd); | |
| } | |
| else | |
| { | |
| if (sd.inNonRoot()) | |
| { | |
| //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot()); | |
| Module.addDeferredSemantic3(sd); | |
| } | |
| } | |
| } | |
| void visitTuple(TypeTuple t) | |
| { | |
| if (t.arguments) | |
| { | |
| foreach (arg; *t.arguments) | |
| { | |
| semanticTypeInfo(sc, arg.type); | |
| } | |
| } | |
| } | |
| /* Note structural similarity of this Type walker to that in isSpeculativeType() | |
| */ | |
| Type tb = t.toBasetype(); | |
| switch (tb.ty) | |
| { | |
| case Tvector: visitVector(tb.isTypeVector()); break; | |
| case Taarray: visitAArray(tb.isTypeAArray()); break; | |
| case Tstruct: visitStruct(tb.isTypeStruct()); break; | |
| case Ttuple: visitTuple (tb.isTypeTuple()); break; | |
| case Tclass: | |
| case Tenum: break; | |
| default: semanticTypeInfo(sc, tb.nextOf()); break; | |
| } | |
| } | |
| enum StructFlags : int | |
| { | |
| none = 0x0, | |
| hasPointers = 0x1, // NB: should use noPointers as in ClassFlags | |
| } | |
| /*********************************************************** | |
| * All `struct` declarations are an instance of this. | |
| */ | |
| extern (C++) class StructDeclaration : AggregateDeclaration | |
| { | |
| bool zeroInit; // !=0 if initialize with 0 fill | |
| bool hasIdentityAssign; // true if has identity opAssign | |
| bool hasBlitAssign; // true if opAssign is a blit | |
| bool hasIdentityEquals; // true if has identity opEquals | |
| bool hasNoFields; // has no fields | |
| bool hasCopyCtor; // copy constructor | |
| // Even if struct is defined as non-root symbol, some built-in operations | |
| // (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo. | |
| // For those, today TypeInfo_Struct is generated in COMDAT. | |
| bool requestTypeInfo; | |
| FuncDeclarations postblits; // Array of postblit functions | |
| FuncDeclaration postblit; // aggregate postblit | |
| FuncDeclaration xeq; // TypeInfo_Struct.xopEquals | |
| FuncDeclaration xcmp; // TypeInfo_Struct.xopCmp | |
| FuncDeclaration xhash; // TypeInfo_Struct.xtoHash | |
| extern (C++) __gshared FuncDeclaration xerreq; // object.xopEquals | |
| extern (C++) __gshared FuncDeclaration xerrcmp; // object.xopCmp | |
| structalign_t alignment; // alignment applied outside of the struct | |
| ThreeState ispod; // if struct is POD | |
| // ABI-specific type(s) if the struct can be passed in registers | |
| TypeTuple argTypes; | |
| extern (D) this(const ref Loc loc, Identifier id, bool inObject) | |
| { | |
| super(loc, id); | |
| zeroInit = false; // assume false until we do semantic processing | |
| ispod = ThreeState.none; | |
| // For forward references | |
| type = new TypeStruct(this); | |
| if (inObject) | |
| { | |
| if (id == Id.ModuleInfo && !Module.moduleinfo) | |
| Module.moduleinfo = this; | |
| } | |
| } | |
| static StructDeclaration create(const ref Loc loc, Identifier id, bool inObject) | |
| { | |
| return new StructDeclaration(loc, id, inObject); | |
| } | |
| override StructDeclaration syntaxCopy(Dsymbol s) | |
| { | |
| StructDeclaration sd = | |
| s ? cast(StructDeclaration)s | |
| : new StructDeclaration(loc, ident, false); | |
| ScopeDsymbol.syntaxCopy(sd); | |
| return sd; | |
| } | |
| override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) | |
| { | |
| //printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags); | |
| if (_scope && !symtab) | |
| dsymbolSemantic(this, _scope); | |
| if (!members || !symtab) // opaque or semantic() is not yet called | |
| { | |
| // .stringof is always defined (but may be hidden by some other symbol) | |
| if(ident != Id.stringof && !(flags & IgnoreErrors) && semanticRun < PASS.semanticdone) | |
| error("is forward referenced when looking for `%s`", ident.toChars()); | |
| return null; | |
| } | |
| return ScopeDsymbol.search(loc, ident, flags); | |
| } | |
| override const(char)* kind() const | |
| { | |
| return "struct"; | |
| } | |
| override final void finalizeSize() | |
| { | |
| //printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok); | |
| assert(sizeok != Sizeok.done); | |
| if (sizeok == Sizeok.inProcess) | |
| { | |
| return; | |
| } | |
| sizeok = Sizeok.inProcess; | |
| //printf("+StructDeclaration::finalizeSize() %s, fields.dim = %d, sizeok = %d\n", toChars(), fields.dim, sizeok); | |
| fields.setDim(0); // workaround | |
| // Set the offsets of the fields and determine the size of the struct | |
| FieldState fieldState; | |
| bool isunion = isUnionDeclaration() !is null; | |
| for (size_t i = 0; i < members.dim; i++) | |
| { | |
| Dsymbol s = (*members)[i]; | |
| s.setFieldOffset(this, fieldState, isunion); | |
| } | |
| if (type.ty == Terror) | |
| { | |
| errors = true; | |
| return; | |
| } | |
| if (structsize == 0) | |
| { | |
| hasNoFields = true; | |
| alignsize = 1; | |
| // A fine mess of what size a zero sized struct should be | |
| final switch (classKind) | |
| { | |
| case ClassKind.d: | |
| case ClassKind.cpp: | |
| structsize = 1; | |
| break; | |
| case ClassKind.c: | |
| case ClassKind.objc: | |
| if (target.c.bitFieldStyle == TargetC.BitFieldStyle.MS) | |
| { | |
| /* Undocumented MS behavior for: | |
| * struct S { int :0; }; | |
| */ | |
| structsize = 4; | |
| } | |
| else if (target.c.bitFieldStyle == TargetC.BitFieldStyle.DM) | |
| { | |
| structsize = 0; | |
| alignsize = 0; | |
| } | |
| else | |
| structsize = 0; | |
| break; | |
| } | |
| } | |
| // Round struct size up to next alignsize boundary. | |
| // This will ensure that arrays of structs will get their internals | |
| // aligned properly. | |
| if (alignment.isDefault() || alignment.isPack()) | |
| structsize = (structsize + alignsize - 1) & ~(alignsize - 1); | |
| else | |
| structsize = (structsize + alignment.get() - 1) & ~(alignment.get() - 1); | |
| sizeok = Sizeok.done; | |
| //printf("-StructDeclaration::finalizeSize() %s, fields.dim = %d, structsize = %d\n", toChars(), fields.dim, structsize); | |
| if (errors) | |
| return; | |
| // Calculate fields[i].overlapped | |
| if (checkOverlappedFields()) | |
| { | |
| errors = true; | |
| return; | |
| } | |
| // Determine if struct is all zeros or not | |
| zeroInit = true; | |
| foreach (vd; fields) | |
| { | |
| if (vd._init) | |
| { | |
| if (vd._init.isVoidInitializer()) | |
| /* Treat as 0 for the purposes of putting the initializer | |
| * in the BSS segment, or doing a mass set to 0 | |
| */ | |
| continue; | |
| // Zero size fields are zero initialized | |
| if (vd.type.size(vd.loc) == 0) | |
| continue; | |
| // Examine init to see if it is all 0s. | |
| auto exp = vd.getConstInitializer(); | |
| if (!exp || !_isZeroInit(exp)) | |
| { | |
| zeroInit = false; | |
| break; | |
| } | |
| } | |
| else if (!vd.type.isZeroInit(loc)) | |
| { | |
| zeroInit = false; | |
| break; | |
| } | |
| } | |
| argTypes = target.toArgTypes(type); | |
| } | |
| /*************************************** | |
| * Determine if struct is POD (Plain Old Data). | |
| * | |
| * POD is defined as: | |
| * $(OL | |
| * $(LI not nested) | |
| * $(LI no postblits, destructors, or assignment operators) | |
| * $(LI no `ref` fields or fields that are themselves non-POD) | |
| * ) | |
| * The idea being these are compatible with C structs. | |
| * | |
| * Returns: | |
| * true if struct is POD | |
| */ | |
| final bool isPOD() | |
| { | |
| // If we've already determined whether this struct is POD. | |
| if (ispod != ThreeState.none) | |
| return (ispod == ThreeState.yes); | |
| ispod = ThreeState.yes; | |
| if (enclosing || postblit || dtor || hasCopyCtor) | |
| { | |
| ispod = ThreeState.no; | |
| return false; | |
| } | |
| // Recursively check all fields are POD. | |
| for (size_t i = 0; i < fields.dim; i++) | |
| { | |
| VarDeclaration v = fields[i]; | |
| if (v.storage_class & STC.ref_) | |
| { | |
| ispod = ThreeState.no; | |
| return false; | |
| } | |
| Type tv = v.type.baseElemOf(); | |
| if (tv.ty == Tstruct) | |
| { | |
| TypeStruct ts = cast(TypeStruct)tv; | |
| StructDeclaration sd = ts.sym; | |
| if (!sd.isPOD()) | |
| { | |
| ispod = ThreeState.no; | |
| return false; | |
| } | |
| } | |
| } | |
| return (ispod == ThreeState.yes); | |
| } | |
| override final inout(StructDeclaration) isStructDeclaration() inout @nogc nothrow pure @safe | |
| { | |
| return this; | |
| } | |
| override void accept(Visitor v) | |
| { | |
| v.visit(this); | |
| } | |
| final uint numArgTypes() const | |
| { | |
| return argTypes && argTypes.arguments ? cast(uint) argTypes.arguments.dim : 0; | |
| } | |
| final Type argType(uint index) | |
| { | |
| return index < numArgTypes() ? (*argTypes.arguments)[index].type : null; | |
| } | |
| /*************************************** | |
| * Verifies whether the struct declaration has a | |
| * constructor that is not a copy constructor. | |
| * Optionally, it can check whether the struct | |
| * declaration has a regular constructor, that | |
| * is not disabled. | |
| * | |
| * Params: | |
| * checkDisabled = if the struct has a regular | |
| non-disabled constructor | |
| * Returns: | |
| * true, if the struct has a regular (optionally, | |
| * not disabled) constructor, false otherwise. | |
| */ | |
| final bool hasRegularCtor(bool checkDisabled = false) | |
| { | |
| if (!ctor) | |
| return false; | |
| bool result; | |
| overloadApply(ctor, (Dsymbol s) | |
| { | |
| if (auto td = s.isTemplateDeclaration()) | |
| { | |
| if (checkDisabled && td.onemember) | |
| { | |
| if (auto ctorDecl = td.onemember.isCtorDeclaration()) | |
| { | |
| if (ctorDecl.storage_class & STC.disable) | |
| return 0; | |
| } | |
| } | |
| result = true; | |
| return 1; | |
| } | |
| if (auto ctorDecl = s.isCtorDeclaration()) | |
| { | |
| if (!ctorDecl.isCpCtor && (!checkDisabled || !(ctorDecl.storage_class & STC.disable))) | |
| { | |
| result = true; | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| }); | |
| return result; | |
| } | |
| } | |
| /********************************** | |
| * Determine if exp is all binary zeros. | |
| * Params: | |
| * exp = expression to check | |
| * Returns: | |
| * true if it's all binary 0 | |
| */ | |
| private bool _isZeroInit(Expression exp) | |
| { | |
| switch (exp.op) | |
| { | |
| case EXP.int64: | |
| return exp.toInteger() == 0; | |
| case EXP.null_: | |
| case EXP.false_: | |
| return true; | |
| case EXP.structLiteral: | |
| { | |
| auto sle = cast(StructLiteralExp) exp; | |
| foreach (i; 0 .. sle.sd.fields.dim) | |
| { | |
| auto field = sle.sd.fields[i]; | |
| if (field.type.size(field.loc)) | |
| { | |
| auto e = (*sle.elements)[i]; | |
| if (e ? !_isZeroInit(e) | |
| : !field.type.isZeroInit(field.loc)) | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| case EXP.arrayLiteral: | |
| { | |
| auto ale = cast(ArrayLiteralExp)exp; | |
| const dim = ale.elements ? ale.elements.dim : 0; | |
| if (ale.type.toBasetype().ty == Tarray) // if initializing a dynamic array | |
| return dim == 0; | |
| foreach (i; 0 .. dim) | |
| { | |
| if (!_isZeroInit(ale[i])) | |
| return false; | |
| } | |
| /* Note that true is returned for all T[0] | |
| */ | |
| return true; | |
| } | |
| case EXP.string_: | |
| { | |
| StringExp se = cast(StringExp)exp; | |
| if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array | |
| return se.len == 0; | |
| foreach (i; 0 .. se.len) | |
| { | |
| if (se.getCodeUnit(i)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| case EXP.vector: | |
| { | |
| auto ve = cast(VectorExp) exp; | |
| return _isZeroInit(ve.e1); | |
| } | |
| case EXP.float64: | |
| case EXP.complex80: | |
| { | |
| import dmd.root.ctfloat : CTFloat; | |
| return (exp.toReal() is CTFloat.zero) && | |
| (exp.toImaginary() is CTFloat.zero); | |
| } | |
| default: | |
| return false; | |
| } | |
| } | |
| /*********************************************************** | |
| * Unions are a variation on structs. | |
| */ | |
| extern (C++) final class UnionDeclaration : StructDeclaration | |
| { | |
| extern (D) this(const ref Loc loc, Identifier id) | |
| { | |
| super(loc, id, false); | |
| } | |
| override UnionDeclaration syntaxCopy(Dsymbol s) | |
| { | |
| assert(!s); | |
| auto ud = new UnionDeclaration(loc, ident); | |
| StructDeclaration.syntaxCopy(ud); | |
| return ud; | |
| } | |
| override const(char)* kind() const | |
| { | |
| return "union"; | |
| } | |
| override inout(UnionDeclaration) isUnionDeclaration() inout | |
| { | |
| return this; | |
| } | |
| override void accept(Visitor v) | |
| { | |
| v.visit(this); | |
| } | |
| } |