/** * Defines initializers of variables, e.g. the array literal in `int[3] x = [0, 1, 2]`. * * Copyright: Copyright (C) 1999-2024 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/init.d, _init.d) * Documentation: https://dlang.org/phobos/dmd_init.html * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/init.d */ module dmd.init; import core.stdc.stdio; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; import dmd.expression; import dmd.identifier; import dmd.location; import dmd.mtype; import dmd.rootobject; import dmd.visitor; enum NeedInterpret : int { INITnointerpret, INITinterpret, } alias INITnointerpret = NeedInterpret.INITnointerpret; alias INITinterpret = NeedInterpret.INITinterpret; /*********************************************************** */ extern (C++) class Initializer : ASTNode { Loc loc; InitKind kind; override DYNCAST dyncast() const { return DYNCAST.initializer; } extern (D) this(const ref Loc loc, InitKind kind) @safe { this.loc = loc; this.kind = kind; } final inout(ErrorInitializer) isErrorInitializer() inout @nogc nothrow pure { // Use void* cast to skip dynamic casting call return kind == InitKind.error ? cast(inout ErrorInitializer)cast(void*)this : null; } final inout(VoidInitializer) isVoidInitializer() inout @nogc nothrow pure { return kind == InitKind.void_ ? cast(inout VoidInitializer)cast(void*)this : null; } final inout(DefaultInitializer) isDefaultInitializer() inout @nogc nothrow pure { return kind == InitKind.default_ ? cast(inout DefaultInitializer)cast(void*)this : null; } final inout(StructInitializer) isStructInitializer() inout @nogc nothrow pure { return kind == InitKind.struct_ ? cast(inout StructInitializer)cast(void*)this : null; } final inout(ArrayInitializer) isArrayInitializer() inout @nogc nothrow pure { return kind == InitKind.array ? cast(inout ArrayInitializer)cast(void*)this : null; } final inout(ExpInitializer) isExpInitializer() inout @nogc nothrow pure { return kind == InitKind.exp ? cast(inout ExpInitializer)cast(void*)this : null; } final inout(CInitializer) isCInitializer() inout @nogc nothrow pure { return kind == InitKind.C_ ? cast(inout CInitializer)cast(void*)this : null; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class VoidInitializer : Initializer { Type type; // type that this will initialize to extern (D) this(const ref Loc loc) @safe { super(loc, InitKind.void_); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * The C23 default initializer `{ }` */ extern (C++) final class DefaultInitializer : Initializer { Type type; // type that this will initialize to extern (D) this(const ref Loc loc) @safe { super(loc, InitKind.default_); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class ErrorInitializer : Initializer { extern (D) this() @safe { super(Loc.initial, InitKind.error); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class StructInitializer : Initializer { Identifiers field; // of Identifier *'s Initializers value; // parallel array of Initializer *'s extern (D) this(const ref Loc loc) { super(loc, InitKind.struct_); } extern (D) void addInit(Identifier field, Initializer value) { //printf("StructInitializer::addInit(field = %p, value = %p)\n", field, value); this.field.push(field); this.value.push(value); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class ArrayInitializer : Initializer { Expressions index; // indices Initializers value; // of Initializer *'s uint dim; // length of array being initialized Type type; // type that array will be used to initialize bool sem; // true if semantic() is run bool isCarray; // C array semantics extern (D) this(const ref Loc loc) { super(loc, InitKind.array); } extern (D) void addInit(Expression index, Initializer value) { this.index.push(index); this.value.push(value); dim = 0; type = null; } bool isAssociativeArray() const pure { foreach (idx; index) { if (idx) return true; } return false; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class ExpInitializer : Initializer { bool expandTuples; Expression exp; extern (D) this(const ref Loc loc, Expression exp) @safe { super(loc, InitKind.exp); this.exp = exp; } override void accept(Visitor v) { v.visit(this); } } /********************************************* * Holds the `designator` for C initializers */ struct Designator { Expression exp; /// [ constant-expression ] Identifier ident; /// . identifier this(Expression exp) @safe { this.exp = exp; } this(Identifier ident) @safe { this.ident = ident; } } /********************************************* * Holds the `designation (opt) initializer` for C initializers */ struct DesigInit { Designators* designatorList; /// designation (opt) Initializer initializer; /// initializer } /******************************** * C11 6.7.9 Initialization * Represents the C initializer-list */ extern (C++) final class CInitializer : Initializer { DesigInits initializerList; /// initializer-list Type type; /// type that array will be used to initialize bool sem; /// true if semantic() is run extern (D) this(const ref Loc loc) { super(loc, InitKind.C_); } override void accept(Visitor v) { v.visit(this); } } /**************************************** * Copy the AST for Initializer. * Params: * inx = Initializer AST to copy * Returns: * the copy */ Initializer syntaxCopy(Initializer inx) { static Initializer visitVoid(VoidInitializer vi) { return new VoidInitializer(vi.loc); } static Initializer visitDefault(DefaultInitializer vi) { return new DefaultInitializer(vi.loc); } static Initializer visitError(ErrorInitializer vi) { return vi; } static Initializer visitExp(ExpInitializer vi) { return new ExpInitializer(vi.loc, vi.exp.syntaxCopy()); } static Initializer visitStruct(StructInitializer vi) { auto si = new StructInitializer(vi.loc); assert(vi.field.length == vi.value.length); si.field.setDim(vi.field.length); si.value.setDim(vi.value.length); foreach (const i; 0 .. vi.field.length) { si.field[i] = vi.field[i]; si.value[i] = vi.value[i].syntaxCopy(); } return si; } static Initializer visitArray(ArrayInitializer vi) { auto ai = new ArrayInitializer(vi.loc); assert(vi.index.length == vi.value.length); ai.index.setDim(vi.index.length); ai.value.setDim(vi.value.length); foreach (const i; 0 .. vi.value.length) { ai.index[i] = vi.index[i] ? vi.index[i].syntaxCopy() : null; ai.value[i] = vi.value[i].syntaxCopy(); } return ai; } static Initializer visitC(CInitializer vi) { auto ci = new CInitializer(vi.loc); ci.initializerList.setDim(vi.initializerList.length); foreach (const i; 0 .. vi.initializerList.length) { DesigInit* cdi = &ci.initializerList[i]; DesigInit* vdi = &ci.initializerList[i]; cdi.initializer = vdi.initializer.syntaxCopy(); if (vdi.designatorList) { cdi.designatorList = new Designators(); cdi.designatorList.setDim(vdi.designatorList.length); foreach (const j; 0 .. vdi.designatorList.length) { Designator* cdid = &(*cdi.designatorList)[j]; Designator* vdid = &(*vdi.designatorList)[j]; cdid.exp = vdid.exp ? vdid.exp.syntaxCopy() : null; cdid.ident = vdid.ident; } } } return ci; } mixin VisitInitializer!Initializer visit; return visit.VisitInitializer(inx); } /*********************************************************** * Visit each Initializer in init. Call a function visit%s(init) for * each node, where %s is the op of the node. Otherwise call visitDefault(init) * for that node. If the visit function returns R.init, continue * visiting each node, otherwise return the value of R. * Params: * Result = return type * init = Initializer tree to traverse * Returns: * Result.init for continue, value of type Result for early exit */ mixin template VisitInitializer(Result) { Result VisitInitializer(Initializer init) { final switch (init.kind) { case InitKind.void_: mixin(visitCase("Void")); break; case InitKind.default_: mixin(visitCase("Default")); break; case InitKind.error: mixin(visitCase("Error")); break; case InitKind.struct_: mixin(visitCase("Struct")); break; case InitKind.array: mixin(visitCase("Array")); break; case InitKind.exp: mixin(visitCase("Exp")); break; case InitKind.C_: mixin(visitCase("C")); break; } static if (is(Result == void)) { } else return Result.init; } } /**************************************** * CTFE-only helper function for VisitInitializer. * Params: * handler = string for the name of the visit handler * Returns: boilerplate code for a case */ string visitCase(string handler) pure @safe { if (__ctfe) { return " auto ix = init.is"~handler~"Initializer(); static if (is(Result == void)) visit"~handler~"(ix); else { Result r = visit"~handler~"(ix); if (r !is Result.init) return r; } "; } assert(0); }