blob: 55fb6f348566b89cdaf76e4163624dcd60c17dac [file] [log] [blame]
/**
* Defines initializers of variables, e.g. the array literal in `int[3] x = [0, 1, 2]`.
*
* Copyright: Copyright (C) 1999-2025 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/compiler/src/dmd/init.d, _init.d)
* Documentation: https://dlang.org/phobos/dmd_init.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/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;
bool semanticDone = false; /// initializerSemantic has been run on this
override DYNCAST dyncast() const
{
return DYNCAST.initializer;
}
extern (D) this(Loc loc, InitKind kind) @safe
{
this.loc = loc;
this.kind = kind;
}
final inout(ErrorInitializer) isErrorInitializer() inout @nogc nothrow pure @trusted
{
// 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 @trusted
{
return kind == InitKind.void_ ? cast(inout VoidInitializer)cast(void*)this : null;
}
final inout(DefaultInitializer) isDefaultInitializer() inout @nogc nothrow pure @trusted
{
return kind == InitKind.default_ ? cast(inout DefaultInitializer)cast(void*)this : null;
}
final inout(StructInitializer) isStructInitializer() inout @nogc nothrow pure @trusted
{
return kind == InitKind.struct_ ? cast(inout StructInitializer)cast(void*)this : null;
}
final inout(ArrayInitializer) isArrayInitializer() inout @nogc nothrow pure @trusted
{
return kind == InitKind.array ? cast(inout ArrayInitializer)cast(void*)this : null;
}
final inout(ExpInitializer) isExpInitializer() inout @nogc nothrow pure @trusted
{
return kind == InitKind.exp ? cast(inout ExpInitializer)cast(void*)this : null;
}
final inout(CInitializer) isCInitializer() inout @nogc nothrow pure @trusted
{
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(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(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(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 isCarray; // C array semantics
extern (D) this(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(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
extern (D) this(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);
}