| /** |
| * Semantic analysis of initializers. |
| * |
| * 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/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.importc; |
| 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; |
| } |
| } |
| if (j >= nfields) |
| { |
| error(i.value[j].loc, "too many initializers for `%s`", sd.toChars()); |
| return err(); |
| } |
| |
| VarDeclaration vd = sd.fields[fieldi]; |
| if (elems[fieldi]) |
| { |
| error(i.value[j].loc, "duplicate initializer for field `%s`", vd.toChars()); |
| errors = true; |
| elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors |
| ++fieldi; |
| continue; |
| } |
| |
| // Check for @safe violations |
| if (vd.type.hasPointers) |
| { |
| if ((!t.alignment.isDefault() && t.alignment.get() < target.ptrsize || |
| (vd.offset & (target.ptrsize - 1))) && |
| sc.func && sc.func.setUnsafe()) |
| { |
| error(i.value[j].loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code", |
| sd.toChars(), vd.toChars()); |
| errors = true; |
| elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors |
| ++fieldi; |
| continue; |
| } |
| } |
| |
| // Check for overlapping initializations (can happen with unions) |
| foreach (k, v2; sd.fields[0 .. nfields]) |
| { |
| if (vd.isOverlappedWith(v2) && elems[k]) |
| { |
| error(elems[k].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 == EXP.error) |
| { |
| errors = true; |
| elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors |
| ++fieldi; |
| 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 = t.isTypeVector().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 == EXP.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 == EXP.tuple) |
| { |
| TupleExp te = ei.exp.isTupleExp(); |
| 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 (auto tsa = t.isTypeSArray()) |
| { |
| uinteger_t edim = tsa.dim.toInteger(); |
| if (i.dim > edim && !(tsa.isIncomplete() && (sc.flags & SCOPE.Cfile))) |
| { |
| 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(); |
| if (sz == SIZE_INVALID) |
| return err(); |
| 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 == EXP.error) |
| return err(); |
| uint olderrors = global.errors; |
| |
| /* ImportC: convert arrays to pointers, functions to pointers to functions |
| */ |
| Type tb = t.toBasetype(); |
| if (tb.isTypePointer()) |
| i.exp = i.exp.arrayFuncConv(sc); |
| |
| /* 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) && !(sc.flags & SCOPE.Cfile)) |
| { |
| i.exp = i.exp.implicitCastTo(sc, t); |
| } |
| if (!global.gag && olderrors != global.errors) |
| { |
| return i; |
| } |
| if (sc.flags & SCOPE.Cfile) |
| { |
| /* the interpreter turns (char*)"string" into &"string"[0] which then |
| * it cannot interpret. Resolve that case by doing optimize() first |
| */ |
| i.exp = i.exp.optimize(WANTvalue); |
| if (i.exp.isSymOffExp()) |
| { |
| /* `static variable cannot be read at compile time` |
| * https://issues.dlang.org/show_bug.cgi?id=22513 |
| * Maybe this would be better addressed in ctfeInterpret()? |
| */ |
| needInterpret = NeedInterpret.INITnointerpret; |
| } |
| } |
| if (needInterpret) |
| i.exp = i.exp.ctfeInterpret(); |
| if (i.exp.op == EXP.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.isTypeTuple() && i.exp.type.isTypeTuple().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 == EXP.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 ti = i.exp.type.toBasetype(); |
| if (i.exp.op == EXP.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 == EXP.string_ && tb.ty == Tsarray) |
| { |
| StringExp se = i.exp.isStringExp(); |
| Type typeb = se.type.toBasetype(); |
| TY tynto = tb.nextOf().ty; |
| if (!se.committed && |
| (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar && |
| se.numberOfCodeUnits(tynto) < tb.isTypeSArray().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. |
| * Change to static array of known size |
| */ |
| if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() && |
| tb.isTypeSArray() && tb.isTypeSArray().isIncomplete()) |
| { |
| 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 = tb.isTypeStruct().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 = tb.isTypeSArray().dim.toInteger(); |
| uinteger_t dim2 = dim1; |
| if (auto ale = i.exp.isArrayLiteralExp()) |
| { |
| dim2 = ale.elements ? ale.elements.dim : 0; |
| } |
| else if (auto se = i.exp.isSliceExp()) |
| { |
| if (Type tx = toStaticArrayType(se)) |
| dim2 = tx.isTypeSArray().dim.toInteger(); |
| } |
| if (dim1 != dim2) |
| { |
| i.exp.error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2); |
| i.exp = ErrorExp.get(); |
| } |
| } |
| Type et = i.exp.type; |
| const errors = global.startGagging(); |
| i.exp = i.exp.implicitCastTo(sc, t); |
| if (global.endGagging(errors)) |
| currExp.error("cannot implicitly convert expression `%s` of type `%s` to `%s`", currExp.toChars(), et.toChars(), t.toChars()); |
| } |
| L1: |
| if (i.exp.op == EXP.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 == EXP.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(); |
| if (!tsa) |
| { |
| /* 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 |
| * simple = true if array of simple elements |
| * Returns: |
| * # of elements in array |
| */ |
| size_t array(Type t, size_t dim, ref bool simple) |
| { |
| //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(); |
| auto tnsa = tn.isTypeSArray(); |
| if (tnsa && tnsa.isIncomplete()) |
| { |
| // 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", tnsa.toChars()); |
| errors = true; |
| return 1; |
| } |
| if (i == dil.length) |
| return 0; |
| size_t n; |
| const nelems = tnsa ? cast(size_t)tnsa.dim.toInteger() : 0; |
| |
| /* Run initializerSemantic on a single element. |
| */ |
| Initializer elem(Initializer ie) |
| { |
| ++i; |
| auto tnx = tn; // in case initializerSemantic tries to change it |
| ie = ie.initializerSemantic(sc, tnx, needInterpret); |
| if (ie.isErrorInitializer()) |
| errors = true; |
| assert(tnx == tn); // sub-types should not be modified |
| return ie; |
| } |
| |
| 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, simple); |
| } |
| else if (auto tns = tn.isTypeStruct()) |
| { |
| if (auto ei = di.initializer.isExpInitializer()) |
| { |
| // no braces enclosing struct initializer |
| |
| /* Disambiguate between an exp representing the entire |
| * struct, and an exp representing the first field of the struct |
| */ |
| if (needInterpret) |
| sc = sc.startCTFE(); |
| ei.exp = ei.exp.expressionSemantic(sc); |
| ei.exp = resolveProperties(sc, ei.exp); |
| if (needInterpret) |
| sc = sc.endCTFE(); |
| if (ei.exp.implicitConvTo(tn)) |
| di.initializer = elem(di.initializer); // the whole struct |
| else |
| { |
| simple = false; |
| dil[n].initializer = structs(tns); // the first field |
| } |
| } |
| else |
| dil[n].initializer = elem(di.initializer); |
| } |
| else |
| { |
| di.initializer = elem(di.initializer); |
| } |
| ++n; |
| if (i == dil.length) |
| break; |
| } |
| //printf(" n: %d i: %d\n", cast(int)n, cast(int)i); |
| return n; |
| } |
| |
| size_t dim = tsa.isIncomplete() ? dil.length : cast(size_t)tsa.dim.toInteger(); |
| bool simple = true; |
| auto newdim = array(t, dim, simple); |
| |
| if (errors) |
| return err(); |
| |
| if (tsa.isIncomplete()) // array of unknown length |
| { |
| // Change to array of known length |
| tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, newdim, 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 |
| if (sz == SIZE_INVALID) |
| return err(); |
| 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(); |
| } |
| |
| /* If an array of simple elements, replace with an ArrayInitializer |
| */ |
| auto tnb = tn.toBasetype(); |
| if (!tnb.isTypeSArray() && (!tnb.isTypeStruct() || simple)) |
| { |
| auto ai = new ArrayInitializer(ci.loc); |
| ai.dim = cast(uint) dil.length; |
| ai.index.setDim(dil.length); |
| ai.value.setDim(dil.length); |
| foreach (const j; 0 .. dil.length) |
| { |
| ai.index[j] = null; |
| ai.value[j] = dil[j].initializer; |
| } |
| auto ty = tx; |
| return ai.initializerSemantic(sc, ty, needInterpret); |
| } |
| |
| if (newdim < ci.initializerList.length && tnb.isTypeStruct()) |
| { |
| // https://issues.dlang.org/show_bug.cgi?id=22375 |
| // initializerList can be bigger than the number of actual elements |
| // to initialize for array of structs because it is not required |
| // for values to have proper bracing. |
| // i.e: These are all valid initializers for `struct{int a,b;}[3]`: |
| // {1,2,3,4}, {{1,2},3,4}, {1,2,{3,4}}, {{1,2},{3,4}} |
| // In all examples above, the new length of the initializer list |
| // has been shortened from four elements to two. This is important, |
| // because `dil` is written back to directly, making the lowered |
| // initializer `{{1,2},{3,4}}` and not `{{1,2},{3,4},3,4}`. |
| ci.initializerList.length = newdim; |
| } |
| |
| return ci; |
| } |
| |
| final switch (init.kind) |
| { |
| case InitKind.void_: return visitVoid (init.isVoidInitializer()); |
| case InitKind.error: return visitError (init.isErrorInitializer()); |
| case InitKind.struct_: return visitStruct(init.isStructInitializer()); |
| case InitKind.array: return visitArray (init.isArrayInitializer()); |
| case InitKind.exp: return visitExp (init.isExpInitializer()); |
| case InitKind.C_: return visitC (init.isCInitializer()); |
| } |
| } |
| |
| /*********************** |
| * 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; |
| } |
| (*values)[i] = iz.isExpInitializer().exp; |
| assert(!(*values)[i].isErrorExp()); |
| } |
| 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; |
| } |
| (*elements)[i] = iz.isExpInitializer().exp; |
| assert(!(*elements)[i].isErrorExp()); |
| } |
| 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 == EXP.type) |
| init.exp = resolveAliasThis(sc, init.exp); |
| |
| init.exp = resolveProperties(sc, init.exp); |
| if (auto se = init.exp.isScopeExp()) |
| { |
| 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 (auto ae = init.exp.isAddrExp()) |
| { |
| if (ae.e1.op == EXP.overloadSet) |
| { |
| init.exp.error("cannot infer type from overloaded function symbol `%s`", init.exp.toChars()); |
| return new ErrorInitializer(); |
| } |
| } |
| if (init.exp.isErrorExp()) |
| { |
| 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 (init.isVoidInitializer()); |
| case InitKind.error: return visitError (init.isErrorInitializer()); |
| case InitKind.struct_: return visitStruct(init.isStructInitializer()); |
| case InitKind.array: return visitArray (init.isArrayInitializer()); |
| case InitKind.exp: return visitExp (init.isExpInitializer()); |
| case InitKind.C_: return visitC (init.isCInitializer()); |
| } |
| } |
| |
| /*********************** |
| * Translate init to an `Expression`. |
| * Params: |
| * init = `Initializer` AST node |
| * itype = if not `null`, type to coerce expression to |
| * isCfile = default initializers are different with C |
| * Returns: |
| * `Expression` created, `null` if cannot, `ErrorExp` for other errors |
| */ |
| extern (C++) Expression initializerToExpression(Initializer init, Type itype = null, const bool isCfile = false) |
| { |
| 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 == EXP.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, isCfile); |
| 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 == EXP.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 == EXP.construct || i.exp.op == EXP.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 (init.isVoidInitializer()); |
| case InitKind.error: return visitError (init.isErrorInitializer()); |
| case InitKind.struct_: return visitStruct(init.isStructInitializer()); |
| case InitKind.array: return visitArray (init.isArrayInitializer()); |
| case InitKind.exp: return visitExp (init.isExpInitializer()); |
| case InitKind.C_: return visitC (init.isCInitializer()); |
| } |
| } |
| |
| |
| /************************************** |
| * 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 == EXP.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 (ae.type.isTypeAArray().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 == EXP.symbolOffset) // address of a global is OK |
| return false; |
| if (e.op == EXP.int64) // cast(void *)int is OK |
| return false; |
| if (e.op == EXP.string_) // "abc".ptr is OK |
| return false; |
| return true; |
| } |
| return false; |
| } |