aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd/initsem.d
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/initsem.d')
-rw-r--r--gcc/d/dmd/initsem.d1268
1 files changed, 1268 insertions, 0 deletions
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
new file mode 100644
index 0000000..ae8bde2
--- /dev/null
+++ b/gcc/d/dmd/initsem.d
@@ -0,0 +1,1268 @@
+/**
+ * Semantic analysis of initializers.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/initsem.d, _initsem.d)
+ * Documentation: https://dlang.org/phobos/dmd_initsem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/initsem.d
+ */
+
+module dmd.initsem;
+
+import core.stdc.stdio;
+import core.checkedint;
+
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.dcast;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.opover;
+import dmd.statement;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+
+/********************************
+ * If possible, convert array initializer to associative array initializer.
+ *
+ * Params:
+ * ai = array initializer to be converted
+ *
+ * Returns:
+ * The converted associative array initializer or ErrorExp if `ai`
+ * is not an associative array initializer.
+ */
+Expression toAssocArrayLiteral(ArrayInitializer ai)
+{
+ Expression e;
+ //printf("ArrayInitializer::toAssocArrayInitializer()\n");
+ //static int i; if (++i == 2) assert(0);
+ const dim = ai.value.dim;
+ auto keys = new Expressions(dim);
+ auto values = new Expressions(dim);
+ for (size_t i = 0; i < dim; i++)
+ {
+ e = ai.index[i];
+ if (!e)
+ goto Lno;
+ (*keys)[i] = e;
+ Initializer iz = ai.value[i];
+ if (!iz)
+ goto Lno;
+ e = iz.initializerToExpression();
+ if (!e)
+ goto Lno;
+ (*values)[i] = e;
+ }
+ e = new AssocArrayLiteralExp(ai.loc, keys, values);
+ return e;
+Lno:
+ error(ai.loc, "not an associative array initializer");
+ return ErrorExp.get();
+}
+
+/******************************************
+ * Perform semantic analysis on init.
+ * Params:
+ * init = Initializer AST node
+ * sc = context
+ * tx = type that the initializer needs to become. If tx is an incomplete
+ * type and the initializer completes it, it is updated to be the
+ * complete type. ImportC has incomplete types
+ * needInterpret = if CTFE needs to be run on this,
+ * such as if it is the initializer for a const declaration
+ * Returns:
+ * `Initializer` with completed semantic analysis, `ErrorInitializer` if errors
+ * were encountered
+ */
+extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedInterpret needInterpret)
+{
+ Type t = tx;
+
+ static Initializer err()
+ {
+ return new ErrorInitializer();
+ }
+
+ Initializer visitVoid(VoidInitializer i)
+ {
+ i.type = t;
+ return i;
+ }
+
+ Initializer visitError(ErrorInitializer i)
+ {
+ return i;
+ }
+
+ Initializer visitStruct(StructInitializer i)
+ {
+ //printf("StructInitializer::semantic(t = %s) %s\n", t.toChars(), i.toChars());
+ /* This works by replacing the StructInitializer with an ExpInitializer.
+ */
+ t = t.toBasetype();
+ if (t.ty == Tsarray && t.nextOf().toBasetype().ty == Tstruct)
+ t = t.nextOf().toBasetype();
+ if (auto ts = t.isTypeStruct())
+ {
+ StructDeclaration sd = ts.sym;
+ // check if the sd has a regular ctor (user defined non-copy ctor)
+ // that is not disabled.
+ if (sd.hasRegularCtor(true))
+ {
+ error(i.loc, "%s `%s` has constructors, cannot use `{ initializers }`, use `%s( initializers )` instead", sd.kind(), sd.toChars(), sd.toChars());
+ return err();
+ }
+ sd.size(i.loc);
+ if (sd.sizeok != Sizeok.done)
+ return err();
+ const nfields = sd.nonHiddenFields();
+ //expandTuples for non-identity arguments?
+ auto elements = new Expressions(nfields);
+ auto elems = (*elements)[];
+ foreach (ref elem; elems)
+ elem = null;
+
+ // Run semantic for explicitly given initializers
+ // TODO: this part is slightly different from StructLiteralExp::semantic.
+ bool errors = false;
+ size_t fieldi = 0;
+ foreach (j, id; i.field[])
+ {
+ if (id)
+ {
+ /* Determine `fieldi` that `id` matches
+ */
+ Dsymbol s = sd.search(i.loc, id);
+ if (!s)
+ {
+ s = sd.search_correct(id);
+ const initLoc = i.value[j].loc;
+ if (s)
+ error(initLoc, "`%s` is not a member of `%s`, did you mean %s `%s`?", id.toChars(), sd.toChars(), s.kind(), s.toChars());
+ else
+ error(initLoc, "`%s` is not a member of `%s`", id.toChars(), sd.toChars());
+ return err();
+ }
+ s.checkDeprecated(i.loc, sc);
+ s = s.toAlias();
+
+ // Find out which field index `s` is
+ for (fieldi = 0; 1; fieldi++)
+ {
+ if (fieldi >= nfields)
+ {
+ error(i.loc, "`%s.%s` is not a per-instance initializable field", sd.toChars(), s.toChars());
+ return err();
+ }
+ if (s == sd.fields[fieldi])
+ break;
+ }
+ }
+ else if (fieldi >= nfields)
+ {
+ error(i.loc, "too many initializers for `%s`", sd.toChars());
+ return err();
+ }
+
+ VarDeclaration vd = sd.fields[fieldi];
+ if (elems[fieldi])
+ {
+ error(i.loc, "duplicate initializer for field `%s`", vd.toChars());
+ errors = true;
+ continue;
+ }
+
+ // Check for @safe violations
+ if (vd.type.hasPointers)
+ {
+ if ((t.alignment() < target.ptrsize ||
+ (vd.offset & (target.ptrsize - 1))) &&
+ sc.func && sc.func.setUnsafe())
+ {
+ error(i.loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code",
+ sd.toChars(), vd.toChars());
+ errors = true;
+ }
+ }
+
+ // Check for overlapping initializations (can happen with unions)
+ foreach (k, v2; sd.fields[0 .. nfields])
+ {
+ if (vd.isOverlappedWith(v2) && elems[k])
+ {
+ error(i.loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
+ errors = true;
+ continue;
+ }
+ }
+
+ // Convert initializer to Expression `ex`
+ assert(sc);
+ auto tm = vd.type.addMod(t.mod);
+ auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
+ auto ex = iz.initializerToExpression();
+ if (ex.op == TOK.error)
+ {
+ errors = true;
+ continue;
+ }
+
+ i.value[j] = iz;
+ elems[fieldi] = doCopyOrMove(sc, ex);
+ ++fieldi;
+ }
+ if (errors)
+ return err();
+
+ // Make a StructLiteralExp out of elements[]
+ auto sle = new StructLiteralExp(i.loc, sd, elements, t);
+ if (!sd.fill(i.loc, elements, false))
+ return err();
+ sle.type = t;
+ auto ie = new ExpInitializer(i.loc, sle);
+ return ie.initializerSemantic(sc, t, needInterpret);
+ }
+ else if ((t.ty == Tdelegate || t.isPtrToFunction()) && i.value.dim == 0)
+ {
+ const tok = (t.ty == Tdelegate) ? TOK.delegate_ : TOK.function_;
+ /* Rewrite as empty delegate literal { }
+ */
+ Type tf = new TypeFunction(ParameterList(), null, LINK.d);
+ auto fd = new FuncLiteralDeclaration(i.loc, Loc.initial, tf, tok, null);
+ fd.fbody = new CompoundStatement(i.loc, new Statements());
+ fd.endloc = i.loc;
+ Expression e = new FuncExp(i.loc, fd);
+ auto ie = new ExpInitializer(i.loc, e);
+ return ie.initializerSemantic(sc, t, needInterpret);
+ }
+ if (t.ty != Terror)
+ error(i.loc, "a struct is not a valid initializer for a `%s`", t.toChars());
+ return err();
+ }
+
+ Initializer visitArray(ArrayInitializer i)
+ {
+ uint length;
+ const(uint) amax = 0x80000000;
+ bool errors = false;
+ //printf("ArrayInitializer::semantic(%s)\n", t.toChars());
+ if (i.sem) // if semantic() already run
+ {
+ return i;
+ }
+ i.sem = true;
+ t = t.toBasetype();
+ switch (t.ty)
+ {
+ case Tsarray:
+ case Tarray:
+ break;
+ case Tvector:
+ t = (cast(TypeVector)t).basetype;
+ break;
+ case Taarray:
+ case Tstruct: // consider implicit constructor call
+ {
+ Expression e;
+ // note: MyStruct foo = [1:2, 3:4] is correct code if MyStruct has a this(int[int])
+ if (t.ty == Taarray || i.isAssociativeArray())
+ e = i.toAssocArrayLiteral();
+ else
+ e = i.initializerToExpression();
+ // Bugzilla 13987
+ if (!e)
+ {
+ error(i.loc, "cannot use array to initialize `%s`", t.toChars());
+ return err();
+ }
+ auto ei = new ExpInitializer(e.loc, e);
+ return ei.initializerSemantic(sc, t, needInterpret);
+ }
+ case Tpointer:
+ if (t.nextOf().ty != Tfunction)
+ break;
+ goto default;
+ default:
+ error(i.loc, "cannot use array to initialize `%s`", t.toChars());
+ return err();
+ }
+ i.type = t;
+ length = 0;
+ for (size_t j = 0; j < i.index.dim; j++)
+ {
+ Expression idx = i.index[j];
+ if (idx)
+ {
+ sc = sc.startCTFE();
+ idx = idx.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ idx = idx.ctfeInterpret();
+ i.index[j] = idx;
+ const uinteger_t idxvalue = idx.toInteger();
+ if (idxvalue >= amax)
+ {
+ error(i.loc, "array index %llu overflow", idxvalue);
+ errors = true;
+ }
+ length = cast(uint)idxvalue;
+ if (idx.op == TOK.error)
+ errors = true;
+ }
+ Initializer val = i.value[j];
+ ExpInitializer ei = val.isExpInitializer();
+ if (ei && !idx)
+ ei.expandTuples = true;
+ auto tn = t.nextOf();
+ val = val.initializerSemantic(sc, tn, needInterpret);
+ if (val.isErrorInitializer())
+ errors = true;
+ ei = val.isExpInitializer();
+ // found a tuple, expand it
+ if (ei && ei.exp.op == TOK.tuple)
+ {
+ TupleExp te = cast(TupleExp)ei.exp;
+ i.index.remove(j);
+ i.value.remove(j);
+ for (size_t k = 0; k < te.exps.dim; ++k)
+ {
+ Expression e = (*te.exps)[k];
+ i.index.insert(j + k, cast(Expression)null);
+ i.value.insert(j + k, new ExpInitializer(e.loc, e));
+ }
+ j--;
+ continue;
+ }
+ else
+ {
+ i.value[j] = val;
+ }
+ length++;
+ if (length == 0)
+ {
+ error(i.loc, "array dimension overflow");
+ return err();
+ }
+ if (length > i.dim)
+ i.dim = length;
+ }
+ if (t.ty == Tsarray)
+ {
+ uinteger_t edim = (cast(TypeSArray)t).dim.toInteger();
+ if (i.dim > edim)
+ {
+ error(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
+ return err();
+ }
+ }
+ if (errors)
+ return err();
+
+ const sz = t.nextOf().size();
+ bool overflow;
+ const max = mulu(i.dim, sz, overflow);
+ if (overflow || max >= amax)
+ {
+ error(i.loc, "array dimension %llu exceeds max of %llu", ulong(i.dim), ulong(amax / sz));
+ return err();
+ }
+ return i;
+ }
+
+ Initializer visitExp(ExpInitializer i)
+ {
+ //printf("ExpInitializer::semantic(%s), type = %s\n", i.exp.toChars(), t.toChars());
+ if (needInterpret)
+ sc = sc.startCTFE();
+ i.exp = i.exp.expressionSemantic(sc);
+ i.exp = resolveProperties(sc, i.exp);
+ if (needInterpret)
+ sc = sc.endCTFE();
+ if (i.exp.op == TOK.error)
+ return err();
+ uint olderrors = global.errors;
+ /* Save the expression before ctfe
+ * Otherwise the error message would contain for example "&[0][0]" instead of "new int"
+ * Regression: https://issues.dlang.org/show_bug.cgi?id=21687
+ */
+ Expression currExp = i.exp;
+ if (needInterpret)
+ {
+ // If the result will be implicitly cast, move the cast into CTFE
+ // to avoid premature truncation of polysemous types.
+ // eg real [] x = [1.1, 2.2]; should use real precision.
+ if (i.exp.implicitConvTo(t))
+ {
+ i.exp = i.exp.implicitCastTo(sc, t);
+ }
+ if (!global.gag && olderrors != global.errors)
+ {
+ return i;
+ }
+ i.exp = i.exp.ctfeInterpret();
+ if (i.exp.op == TOK.voidExpression)
+ error(i.loc, "variables cannot be initialized with an expression of type `void`. Use `void` initialization instead.");
+ }
+ else
+ {
+ i.exp = i.exp.optimize(WANTvalue);
+ }
+ if (!global.gag && olderrors != global.errors)
+ {
+ return i; // Failed, suppress duplicate error messages
+ }
+ if (i.exp.type.ty == Ttuple && (cast(TypeTuple)i.exp.type).arguments.dim == 0)
+ {
+ Type et = i.exp.type;
+ i.exp = new TupleExp(i.exp.loc, new Expressions());
+ i.exp.type = et;
+ }
+ if (i.exp.op == TOK.type)
+ {
+ i.exp.error("initializer must be an expression, not `%s`", i.exp.toChars());
+ return err();
+ }
+ // Make sure all pointers are constants
+ if (needInterpret && hasNonConstPointers(i.exp))
+ {
+ i.exp.error("cannot use non-constant CTFE pointer in an initializer `%s`", currExp.toChars());
+ return err();
+ }
+ Type tb = t.toBasetype();
+ Type ti = i.exp.type.toBasetype();
+ if (i.exp.op == TOK.tuple && i.expandTuples && !i.exp.implicitConvTo(t))
+ {
+ return new ExpInitializer(i.loc, i.exp);
+ }
+ /* Look for case of initializing a static array with a too-short
+ * string literal, such as:
+ * char[5] foo = "abc";
+ * Allow this by doing an explicit cast, which will lengthen the string
+ * literal.
+ */
+ if (i.exp.op == TOK.string_ && tb.ty == Tsarray)
+ {
+ StringExp se = cast(StringExp)i.exp;
+ Type typeb = se.type.toBasetype();
+ TY tynto = tb.nextOf().ty;
+ if (!se.committed &&
+ (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar &&
+ se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger())
+ {
+ i.exp = se.castTo(sc, t);
+ goto L1;
+ }
+ }
+
+ /* C11 6.7.9-14..15
+ * Initialize an array of unknown size with a string.
+ * ImportC regards Tarray as an array of unknown size.
+ * Change to static array of known size
+ */
+ if (sc.flags & SCOPE.Cfile && i.exp.op == TOK.string_ && tb.ty == Tarray)
+ {
+ StringExp se = i.exp.isStringExp();
+ auto ts = new TypeSArray(tb.nextOf(), new IntegerExp(Loc.initial, se.len + 1, Type.tsize_t));
+ t = typeSemantic(ts, Loc.initial, sc);
+ i.exp.type = t;
+ tx = t;
+ }
+
+ // Look for implicit constructor call
+ if (tb.ty == Tstruct && !(ti.ty == Tstruct && tb.toDsymbol(sc) == ti.toDsymbol(sc)) && !i.exp.implicitConvTo(t))
+ {
+ StructDeclaration sd = (cast(TypeStruct)tb).sym;
+ if (sd.ctor)
+ {
+ // Rewrite as S().ctor(exp)
+ Expression e;
+ e = new StructLiteralExp(i.loc, sd, null);
+ e = new DotIdExp(i.loc, e, Id.ctor);
+ e = new CallExp(i.loc, e, i.exp);
+ e = e.expressionSemantic(sc);
+ if (needInterpret)
+ i.exp = e.ctfeInterpret();
+ else
+ i.exp = e.optimize(WANTvalue);
+ }
+ else if (search_function(sd, Id.call))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=1547
+ *
+ * Look for static opCall
+ *
+ * Rewrite as:
+ * i.exp = typeof(sd).opCall(arguments)
+ */
+
+ Expression e = typeDotIdExp(i.loc, sd.type, Id.call);
+ e = new CallExp(i.loc, e, i.exp);
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ if (needInterpret)
+ i.exp = e.ctfeInterpret();
+ else
+ i.exp = e.optimize(WANTvalue);
+ }
+ }
+ // Look for the case of statically initializing an array
+ // with a single member.
+ if (tb.ty == Tsarray && !tb.nextOf().equals(ti.toBasetype().nextOf()) && i.exp.implicitConvTo(tb.nextOf()))
+ {
+ /* If the variable is not actually used in compile time, array creation is
+ * redundant. So delay it until invocation of toExpression() or toDt().
+ */
+ t = tb.nextOf();
+ }
+ if (i.exp.implicitConvTo(t))
+ {
+ i.exp = i.exp.implicitCastTo(sc, t);
+ }
+ else
+ {
+ // Look for mismatch of compile-time known length to emit
+ // better diagnostic message, as same as AssignExp::semantic.
+ if (tb.ty == Tsarray && i.exp.implicitConvTo(tb.nextOf().arrayOf()) > MATCH.nomatch)
+ {
+ uinteger_t dim1 = (cast(TypeSArray)tb).dim.toInteger();
+ uinteger_t dim2 = dim1;
+ if (i.exp.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp ale = cast(ArrayLiteralExp)i.exp;
+ dim2 = ale.elements ? ale.elements.dim : 0;
+ }
+ else if (i.exp.op == TOK.slice)
+ {
+ Type tx = toStaticArrayType(cast(SliceExp)i.exp);
+ if (tx)
+ dim2 = (cast(TypeSArray)tx).dim.toInteger();
+ }
+ if (dim1 != dim2)
+ {
+ i.exp.error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2);
+ i.exp = ErrorExp.get();
+ }
+ }
+ i.exp = i.exp.implicitCastTo(sc, t);
+ }
+ L1:
+ if (i.exp.op == TOK.error)
+ {
+ return i;
+ }
+ if (needInterpret)
+ i.exp = i.exp.ctfeInterpret();
+ else
+ i.exp = i.exp.optimize(WANTvalue);
+ //printf("-ExpInitializer::semantic(): "); i.exp.print();
+ return i;
+ }
+
+ Initializer visitC(CInitializer ci)
+ {
+ if (ci.sem) // if semantic() already run
+ return ci;
+ //printf("CInitializer::semantic() (%s) %s\n", t.toChars(), ci.toChars());
+ ci.sem = true;
+ t = t.toBasetype();
+ ci.type = t; // later passes will need this
+
+ auto dil = ci.initializerList[];
+ size_t i = 0; // index into dil[]
+ const uint amax = 0x8000_0000;
+ bool errors;
+
+ /* If `{ expression }` return the expression initializer
+ */
+ ExpInitializer isBraceExpression()
+ {
+ return (dil.length == 1 && !dil[0].designatorList)
+ ? dil[0].initializer.isExpInitializer()
+ : null;
+ }
+
+ /* Convert struct initializer into ExpInitializer
+ */
+ Initializer structs(TypeStruct ts)
+ {
+ //printf("structs %s\n", ts.toChars());
+ StructDeclaration sd = ts.sym;
+ sd.size(ci.loc);
+ if (sd.sizeok != Sizeok.done)
+ {
+ errors = true;
+ return err();
+ }
+ const nfields = sd.nonHiddenFields();
+ auto elements = new Expressions(nfields);
+ auto elems = (*elements)[];
+ foreach (ref elem; elems)
+ elem = null;
+
+ FieldLoop:
+ for (size_t fieldi = 0; fieldi < nfields; ++fieldi)
+ {
+ if (i == dil.length)
+ break;
+
+ auto di = dil[i];
+ if (di.designatorList)
+ {
+ error(ci.loc, "C designator-list not supported yet");
+ errors = true;
+ break;
+ }
+
+ VarDeclaration vd = sd.fields[fieldi];
+
+ // Check for overlapping initializations (can happen with unions)
+ foreach (k, v2; sd.fields[0 .. nfields])
+ {
+ if (vd.isOverlappedWith(v2) && elems[k])
+ {
+ continue FieldLoop; // skip it
+ }
+ }
+
+ ++i;
+
+ // Convert initializer to Expression `ex`
+ assert(sc);
+ auto tm = vd.type.addMod(ts.mod);
+ auto iz = di.initializer.initializerSemantic(sc, tm, needInterpret);
+ auto ex = iz.initializerToExpression();
+ if (ex.op == TOK.error)
+ {
+ errors = true;
+ continue;
+ }
+
+ elems[fieldi] = ex;
+ }
+ if (errors)
+ return err();
+
+ // Make a StructLiteralExp out of elements[]
+ Type tx = ts;
+ auto sle = new StructLiteralExp(ci.loc, sd, elements, tx);
+ if (!sd.fill(ci.loc, elements, false))
+ return err();
+ sle.type = tx;
+ auto ie = new ExpInitializer(ci.loc, sle);
+ return ie.initializerSemantic(sc, tx, needInterpret);
+ }
+
+ if (auto ts = t.isTypeStruct())
+ {
+ auto ei = structs(ts);
+ if (errors)
+ return err();
+ if (i < dil.length)
+ {
+ error(ci.loc, "%d extra initializer(s) for `struct %s`", cast(int)(dil.length - i), ts.toChars());
+ return err();
+ }
+ return ei;
+ }
+
+ auto tsa = t.isTypeSArray();
+ auto ta = t.isTypeDArray();
+ if (!(tsa || ta))
+ {
+ /* Not an array. See if it is `{ exp }` which can be
+ * converted to an ExpInitializer
+ */
+ if (ExpInitializer ei = isBraceExpression())
+ {
+ return ei.initializerSemantic(sc, t, needInterpret);
+ }
+
+ error(ci.loc, "C non-array initializer (%s) %s not supported yet", t.toChars(), ci.toChars());
+ return err();
+ }
+
+ /* If it's an array of integral being initialized by `{ string }`
+ * replace with `string`
+ */
+ auto tn = t.nextOf();
+ if (tn.isintegral())
+ {
+ if (ExpInitializer ei = isBraceExpression())
+ {
+ if (ei.exp.isStringExp())
+ return ei.initializerSemantic(sc, t, needInterpret);
+ }
+ }
+
+ /* Support recursion to handle un-braced array initializers
+ * Params:
+ * t = element type
+ * dim = max number of elements
+ * Returns:
+ * # of elements in array
+ */
+ size_t array(Type t, size_t dim)
+ {
+ //printf(" type %s i %d dim %d dil.length = %d\n", t.toChars(), cast(int)i, cast(int)dim, cast(int)dil.length);
+ auto tn = t.nextOf().toBasetype();
+ if (auto tna = tn.isTypeDArray())
+ {
+ // C11 6.2.5-20 "element type shall be complete whenever the array type is specified"
+ error(ci.loc, "incomplete element type `%s` not allowed", tna.toChars());
+ errors = true;
+ return 1;
+ }
+ if (i == dil.length)
+ return 0;
+ size_t n;
+ auto tnsa = tn.isTypeSArray();
+ const nelems = tnsa ? cast(size_t)tnsa.dim.toInteger() : 0;
+
+ foreach (j; 0 .. dim)
+ {
+ auto di = dil[i];
+ if (di.designatorList)
+ {
+ error(ci.loc, "C designator-list not supported yet");
+ errors = true;
+ break;
+ }
+ if (tnsa && di.initializer.isExpInitializer())
+ {
+ // no braces enclosing array initializer, so recurse
+ array(tnsa, nelems);
+ }
+ else if (auto tns = tn.isTypeStruct())
+ {
+ dil[n].initializer = structs(tns);
+ }
+ else
+ {
+ ++i;
+ auto tnx = tn; // in case initializerSemantic tries to change it
+ di.initializer = di.initializer.initializerSemantic(sc, tnx, needInterpret);
+ if (di.initializer.isErrorInitializer())
+ errors = true;
+ assert(tnx == tn); // sub-types should not be modified
+ }
+ ++n;
+ if (i == dil.length)
+ break;
+ }
+ //printf(" n: %d i: %d\n", cast(int)n, cast(int)i);
+ return n;
+ }
+
+ size_t dim = ta ? dil.length : cast(size_t)tsa.dim.toInteger();
+ auto n = array(t, dim);
+
+ if (errors)
+ return err();
+
+ if (ta) // array of unknown length
+ {
+ // Change to array of known length
+ tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, n, Type.tsize_t));
+ tx = tsa; // rewrite caller's type
+ ci.type = tsa; // remember for later passes
+ }
+ const uinteger_t edim = tsa.dim.toInteger();
+ if (i < dil.length)
+ {
+ error(ci.loc, "%d extra initializer(s) for static array length of %d", cast(int)(dil.length - i), cast(int)edim);
+ return err();
+ }
+
+ const sz = tn.size(); // element size
+ bool overflow;
+ const max = mulu(edim, sz, overflow);
+ if (overflow || max >= amax)
+ {
+ error(ci.loc, "array dimension %llu exceeds max of %llu", ulong(edim), ulong(amax / sz));
+ return err();
+ }
+
+ return ci;
+ }
+
+ final switch (init.kind)
+ {
+ case InitKind.void_: return visitVoid (cast( VoidInitializer)init);
+ case InitKind.error: return visitError (cast( ErrorInitializer)init);
+ case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
+ case InitKind.array: return visitArray (cast( ArrayInitializer)init);
+ case InitKind.exp: return visitExp (cast( ExpInitializer)init);
+ case InitKind.C_: return visitC (cast( CInitializer)init);
+ }
+}
+
+/***********************
+ * Translate init to an `Expression` in order to infer the type.
+ * Params:
+ * init = `Initializer` AST node
+ * sc = context
+ * Returns:
+ * an equivalent `ExpInitializer` if successful, or `ErrorInitializer` if it cannot be translated
+ */
+Initializer inferType(Initializer init, Scope* sc)
+{
+ Initializer visitVoid(VoidInitializer i)
+ {
+ error(i.loc, "cannot infer type from void initializer");
+ return new ErrorInitializer();
+ }
+
+ Initializer visitError(ErrorInitializer i)
+ {
+ return i;
+ }
+
+ Initializer visitStruct(StructInitializer i)
+ {
+ error(i.loc, "cannot infer type from struct initializer");
+ return new ErrorInitializer();
+ }
+
+ Initializer visitArray(ArrayInitializer init)
+ {
+ //printf("ArrayInitializer::inferType() %s\n", toChars());
+ Expressions* keys = null;
+ Expressions* values;
+ if (init.isAssociativeArray())
+ {
+ keys = new Expressions(init.value.dim);
+ values = new Expressions(init.value.dim);
+ for (size_t i = 0; i < init.value.dim; i++)
+ {
+ Expression e = init.index[i];
+ if (!e)
+ goto Lno;
+ (*keys)[i] = e;
+ Initializer iz = init.value[i];
+ if (!iz)
+ goto Lno;
+ iz = iz.inferType(sc);
+ if (iz.isErrorInitializer())
+ {
+ return iz;
+ }
+ assert(iz.isExpInitializer());
+ (*values)[i] = (cast(ExpInitializer)iz).exp;
+ assert((*values)[i].op != TOK.error);
+ }
+ Expression e = new AssocArrayLiteralExp(init.loc, keys, values);
+ auto ei = new ExpInitializer(init.loc, e);
+ return ei.inferType(sc);
+ }
+ else
+ {
+ auto elements = new Expressions(init.value.dim);
+ elements.zero();
+ for (size_t i = 0; i < init.value.dim; i++)
+ {
+ assert(!init.index[i]); // already asserted by isAssociativeArray()
+ Initializer iz = init.value[i];
+ if (!iz)
+ goto Lno;
+ iz = iz.inferType(sc);
+ if (iz.isErrorInitializer())
+ {
+ return iz;
+ }
+ assert(iz.isExpInitializer());
+ (*elements)[i] = (cast(ExpInitializer)iz).exp;
+ assert((*elements)[i].op != TOK.error);
+ }
+ Expression e = new ArrayLiteralExp(init.loc, null, elements);
+ auto ei = new ExpInitializer(init.loc, e);
+ return ei.inferType(sc);
+ }
+ Lno:
+ if (keys)
+ {
+ error(init.loc, "not an associative array initializer");
+ }
+ else
+ {
+ error(init.loc, "cannot infer type from array initializer");
+ }
+ return new ErrorInitializer();
+ }
+
+ Initializer visitExp(ExpInitializer init)
+ {
+ //printf("ExpInitializer::inferType() %s\n", init.toChars());
+ init.exp = init.exp.expressionSemantic(sc);
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (init.exp.op == TOK.type)
+ init.exp = resolveAliasThis(sc, init.exp);
+
+ init.exp = resolveProperties(sc, init.exp);
+ if (init.exp.op == TOK.scope_)
+ {
+ ScopeExp se = cast(ScopeExp)init.exp;
+ TemplateInstance ti = se.sds.isTemplateInstance();
+ if (ti && ti.semanticRun == PASS.semantic && !ti.aliasdecl)
+ se.error("cannot infer type from %s `%s`, possible circular dependency", se.sds.kind(), se.toChars());
+ else
+ se.error("cannot infer type from %s `%s`", se.sds.kind(), se.toChars());
+ return new ErrorInitializer();
+ }
+
+ // Give error for overloaded function addresses
+ bool hasOverloads;
+ if (auto f = isFuncAddress(init.exp, &hasOverloads))
+ {
+ if (f.checkForwardRef(init.loc))
+ {
+ return new ErrorInitializer();
+ }
+ if (hasOverloads && !f.isUnique())
+ {
+ init.exp.error("cannot infer type from overloaded function symbol `%s`", init.exp.toChars());
+ return new ErrorInitializer();
+ }
+ }
+ if (init.exp.op == TOK.address)
+ {
+ AddrExp ae = cast(AddrExp)init.exp;
+ if (ae.e1.op == TOK.overloadSet)
+ {
+ init.exp.error("cannot infer type from overloaded function symbol `%s`", init.exp.toChars());
+ return new ErrorInitializer();
+ }
+ }
+ if (init.exp.op == TOK.error)
+ {
+ return new ErrorInitializer();
+ }
+ if (!init.exp.type)
+ {
+ return new ErrorInitializer();
+ }
+ return init;
+ }
+
+ Initializer visitC(CInitializer i)
+ {
+ //printf(CInitializer::inferType()\n");
+ error(i.loc, "TODO C inferType initializers not supported yet");
+ return new ErrorInitializer();
+ }
+
+ final switch (init.kind)
+ {
+ case InitKind.void_: return visitVoid (cast( VoidInitializer)init);
+ case InitKind.error: return visitError (cast( ErrorInitializer)init);
+ case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
+ case InitKind.array: return visitArray (cast( ArrayInitializer)init);
+ case InitKind.exp: return visitExp (cast( ExpInitializer)init);
+ case InitKind.C_: return visitC (cast( CInitializer)init);
+ }
+}
+
+/***********************
+ * Translate init to an `Expression`.
+ * Params:
+ * init = `Initializer` AST node
+ * itype = if not `null`, type to coerce expression to
+ * Returns:
+ * `Expression` created, `null` if cannot, `ErrorExp` for other errors
+ */
+extern (C++) Expression initializerToExpression(Initializer init, Type itype = null)
+{
+ Expression visitVoid(VoidInitializer)
+ {
+ return null;
+ }
+
+ Expression visitError(ErrorInitializer)
+ {
+ return ErrorExp.get();
+ }
+
+ /***************************************
+ * This works by transforming a struct initializer into
+ * a struct literal. In the future, the two should be the
+ * same thing.
+ */
+ Expression visitStruct(StructInitializer)
+ {
+ // cannot convert to an expression without target 'ad'
+ return null;
+ }
+
+ /********************************
+ * If possible, convert array initializer to array literal.
+ * Otherwise return NULL.
+ */
+ Expression visitArray(ArrayInitializer init)
+ {
+ //printf("ArrayInitializer::toExpression(), dim = %d\n", dim);
+ //static int i; if (++i == 2) assert(0);
+ uint edim; // the length of the resulting array literal
+ const(uint) amax = 0x80000000;
+ Type t = null; // type of the array literal being initialized
+ if (init.type)
+ {
+ if (init.type == Type.terror)
+ {
+ return ErrorExp.get();
+ }
+ t = init.type.toBasetype();
+ switch (t.ty)
+ {
+ case Tvector:
+ t = t.isTypeVector().basetype;
+ goto case Tsarray;
+
+ case Tsarray:
+ uinteger_t adim = t.isTypeSArray().dim.toInteger();
+ if (adim >= amax)
+ return null;
+ edim = cast(uint)adim;
+ break;
+
+ case Tpointer:
+ case Tarray:
+ edim = init.dim;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ else
+ {
+ /* Calculate the length of the array literal
+ */
+ edim = cast(uint)init.value.dim;
+ size_t j = 0;
+ foreach (i; 0 .. init.value.dim)
+ {
+ if (auto e = init.index[i])
+ {
+ if (e.op == TOK.int64)
+ {
+ const uinteger_t idxval = e.toInteger();
+ if (idxval >= amax)
+ return null;
+ j = cast(size_t)idxval;
+ }
+ else
+ return null;
+ }
+ ++j;
+ if (j > edim)
+ edim = cast(uint)j;
+ }
+ }
+
+ auto elements = new Expressions(edim);
+ elements.zero();
+ size_t j = 0;
+ foreach (i; 0 .. init.value.dim)
+ {
+ if (auto e = init.index[i])
+ j = cast(size_t)e.toInteger();
+ assert(j < edim);
+ if (Initializer iz = init.value[i])
+ {
+ if (Expression ex = iz.initializerToExpression())
+ {
+ (*elements)[j] = ex;
+ ++j;
+ }
+ else
+ return null;
+ }
+ else
+ return null;
+ }
+
+ /* Fill in any missing elements with the default initializer
+ */
+ Expression defaultInit = null; // lazily create it
+ foreach (ref element; (*elements)[0 .. edim])
+ {
+ if (!element)
+ {
+ if (!init.type) // don't know what type to use
+ return null;
+ if (!defaultInit)
+ defaultInit = (cast(TypeNext)t).next.defaultInit(Loc.initial);
+ element = defaultInit;
+ }
+ }
+
+ /* Expand any static array initializers that are a single expression
+ * into an array of them
+ * e => [e, e, ..., e, e]
+ */
+ if (t)
+ {
+ Type tn = t.nextOf().toBasetype();
+ if (tn.ty == Tsarray)
+ {
+ const dim = cast(size_t)(cast(TypeSArray)tn).dim.toInteger();
+ Type te = tn.nextOf().toBasetype();
+ foreach (ref e; *elements)
+ {
+ if (te.equals(e.type))
+ {
+ auto elements2 = new Expressions(dim);
+ foreach (ref e2; *elements2)
+ e2 = e;
+ e = new ArrayLiteralExp(e.loc, tn, elements2);
+ }
+ }
+ }
+ }
+
+ /* If any elements are errors, then the whole thing is an error
+ */
+ foreach (e; (*elements)[0 .. edim])
+ {
+ if (e.op == TOK.error)
+ {
+ return e;
+ }
+ }
+
+ Expression e = new ArrayLiteralExp(init.loc, init.type, elements);
+ return e;
+ }
+
+ Expression visitExp(ExpInitializer i)
+ {
+ if (itype)
+ {
+ //printf("ExpInitializer::toExpression(t = %s) exp = %s\n", itype.toChars(), i.exp.toChars());
+ Type tb = itype.toBasetype();
+ Expression e = (i.exp.op == TOK.construct || i.exp.op == TOK.blit) ? (cast(AssignExp)i.exp).e2 : i.exp;
+ if (tb.ty == Tsarray && e.implicitConvTo(tb.nextOf()))
+ {
+ TypeSArray tsa = cast(TypeSArray)tb;
+ size_t d = cast(size_t)tsa.dim.toInteger();
+ auto elements = new Expressions(d);
+ for (size_t j = 0; j < d; j++)
+ (*elements)[j] = e;
+ auto ae = new ArrayLiteralExp(e.loc, itype, elements);
+ return ae;
+ }
+ }
+ return i.exp;
+ }
+
+ Expression visitC(CInitializer i)
+ {
+ //printf("CInitializer.initializerToExpression()\n");
+ return null;
+ }
+
+ final switch (init.kind)
+ {
+ case InitKind.void_: return visitVoid (cast( VoidInitializer)init);
+ case InitKind.error: return visitError (cast( ErrorInitializer)init);
+ case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
+ case InitKind.array: return visitArray (cast( ArrayInitializer)init);
+ case InitKind.exp: return visitExp (cast( ExpInitializer)init);
+ case InitKind.C_: return visitC (cast( CInitializer)init);
+ }
+}
+
+
+/**************************************
+ * Determine if expression has non-constant pointers, or more precisely,
+ * a pointer that CTFE cannot handle.
+ * Params:
+ * e = expression to check
+ * Returns:
+ * true if it has non-constant pointers
+ */
+private bool hasNonConstPointers(Expression e)
+{
+ static bool checkArray(Expressions* elems)
+ {
+ foreach (e; *elems)
+ {
+ if (e && hasNonConstPointers(e))
+ return true;
+ }
+ return false;
+ }
+
+ if (e.type.ty == Terror)
+ return false;
+ if (e.op == TOK.null_)
+ return false;
+ if (auto se = e.isStructLiteralExp())
+ {
+ return checkArray(se.elements);
+ }
+ if (auto ae = e.isArrayLiteralExp())
+ {
+ if (!ae.type.nextOf().hasPointers())
+ return false;
+ return checkArray(ae.elements);
+ }
+ if (auto ae = e.isAssocArrayLiteralExp())
+ {
+ if (ae.type.nextOf().hasPointers() && checkArray(ae.values))
+ return true;
+ if ((cast(TypeAArray)ae.type).index.hasPointers())
+ return checkArray(ae.keys);
+ return false;
+ }
+ if (auto ae = e.isAddrExp())
+ {
+ if (auto se = ae.e1.isStructLiteralExp())
+ {
+ if (!(se.stageflags & stageSearchPointers))
+ {
+ const old = se.stageflags;
+ se.stageflags |= stageSearchPointers;
+ bool ret = checkArray(se.elements);
+ se.stageflags = old;
+ return ret;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (e.type.ty == Tpointer && !e.type.isPtrToFunction())
+ {
+ if (e.op == TOK.symbolOffset) // address of a global is OK
+ return false;
+ if (e.op == TOK.int64) // cast(void *)int is OK
+ return false;
+ if (e.op == TOK.string_) // "abc".ptr is OK
+ return false;
+ return true;
+ }
+ return false;
+}
+
+
+