summaryrefslogtreecommitdiff
path: root/gcc/d/dmd/expressionsem.d
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/expressionsem.d')
-rw-r--r--gcc/d/dmd/expressionsem.d13058
1 files changed, 13058 insertions, 0 deletions
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
new file mode 100644
index 00000000000..3b604af53b9
--- /dev/null
+++ b/gcc/d/dmd/expressionsem.d
@@ -0,0 +1,13058 @@
+/**
+ * Semantic analysis of expressions.
+ *
+ * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
+ *
+ * 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/expressionsem.d, _expressionsem.d)
+ * Documentation: https://dlang.org/phobos/dmd_expressionsem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expressionsem.d
+ */
+
+module dmd.expressionsem;
+
+import core.stdc.stdio;
+
+import dmd.access;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arrayop;
+import dmd.arraytypes;
+import dmd.attrib;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.canthrow;
+import dmd.chkformat;
+import dmd.ctorflow;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.declaration;
+import dmd.dclass;
+import dmd.dcast;
+import dmd.delegatize;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dinterpret;
+import dmd.dmangle;
+import dmd.dmodule;
+import dmd.dstruct;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.imphint;
+import dmd.init;
+import dmd.initsem;
+import dmd.inline;
+import dmd.intrange;
+import dmd.mtype;
+import dmd.nspace;
+import dmd.opover;
+import dmd.optimize;
+import dmd.parse;
+import dmd.printast;
+import dmd.root.ctfloat;
+import dmd.root.file;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.semantic2;
+import dmd.semantic3;
+import dmd.sideeffect;
+import dmd.safe;
+import dmd.target;
+import dmd.tokens;
+import dmd.traits;
+import dmd.typesem;
+import dmd.typinf;
+import dmd.utf;
+import dmd.utils;
+import dmd.visitor;
+
+enum LOGSEMANTIC = false;
+
+/********************************************************
+ * Perform semantic analysis and CTFE on expressions to produce
+ * a string.
+ * Params:
+ * buf = append generated string to buffer
+ * sc = context
+ * exps = array of Expressions
+ * Returns:
+ * true on error
+ */
+bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps)
+{
+ if (!exps)
+ return false;
+
+ foreach (ex; *exps)
+ {
+ if (!ex)
+ continue;
+ auto sc2 = sc.startCTFE();
+ auto e2 = ex.expressionSemantic(sc2);
+ auto e3 = resolveProperties(sc2, e2);
+ sc2.endCTFE();
+
+ // allowed to contain types as well as expressions
+ auto e4 = ctfeInterpretForPragmaMsg(e3);
+ if (!e4 || e4.op == TOK.error)
+ return true;
+
+ // expand tuple
+ if (auto te = e4.isTupleExp())
+ {
+ if (expressionsToString(buf, sc, te.exps))
+ return true;
+ continue;
+ }
+ // char literals exp `.toStringExp` return `null` but we cant override it
+ // because in most contexts we don't want the conversion to succeed.
+ IntegerExp ie = e4.isIntegerExp();
+ const ty = (ie && ie.type) ? ie.type.ty : Terror;
+ if (ty.isSomeChar)
+ {
+ auto tsa = new TypeSArray(ie.type, IntegerExp.literal!1);
+ e4 = new ArrayLiteralExp(ex.loc, tsa, ie);
+ }
+
+ if (StringExp se = e4.toStringExp())
+ buf.writestring(se.toUTF8(sc).peekString());
+ else
+ buf.writestring(e4.toString());
+ }
+ return false;
+}
+
+
+/***********************************************************
+ * Resolve `exp` as a compile-time known string.
+ * Params:
+ * sc = scope
+ * exp = Expression which expected as a string
+ * s = What the string is expected for, will be used in error diagnostic.
+ * Returns:
+ * String literal, or `null` if error happens.
+ */
+StringExp semanticString(Scope *sc, Expression exp, const char* s)
+{
+ sc = sc.startCTFE();
+ exp = exp.expressionSemantic(sc);
+ exp = resolveProperties(sc, exp);
+ sc = sc.endCTFE();
+
+ if (exp.op == TOK.error)
+ return null;
+
+ auto e = exp;
+ if (exp.type.isString())
+ {
+ e = e.ctfeInterpret();
+ if (e.op == TOK.error)
+ return null;
+ }
+
+ auto se = e.toStringExp();
+ if (!se)
+ {
+ exp.error("`string` expected for %s, not `(%s)` of type `%s`",
+ s, exp.toChars(), exp.type.toChars());
+ return null;
+ }
+ return se;
+}
+
+private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue)
+{
+ Expression e0;
+ Expression e1 = Expression.extractLast(ue.e1, e0);
+ // https://issues.dlang.org/show_bug.cgi?id=12585
+ // Extract the side effect part if ue.e1 is comma.
+
+ if ((sc.flags & SCOPE.ctfe) ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect()
+ {
+ /* Even if opDollar is needed, 'e1' should be evaluate only once. So
+ * Rewrite:
+ * e1.opIndex( ... use of $ ... )
+ * e1.opSlice( ... use of $ ... )
+ * as:
+ * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
+ * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
+ */
+ e1 = extractSideEffect(sc, "__dop", e0, e1, false);
+ assert(e1.isVarExp());
+ e1.isVarExp().var.storage_class |= STC.exptemp; // lifetime limited to expression
+ }
+ ue.e1 = e1;
+ return e0;
+}
+
+/**************************************
+ * Runs semantic on ae.arguments. Declares temporary variables
+ * if '$' was used.
+ */
+Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
+{
+ assert(!ae.lengthVar);
+ *pe0 = null;
+ AggregateDeclaration ad = isAggregate(ae.e1.type);
+ Dsymbol slice = search_function(ad, Id.slice);
+ //printf("slice = %s %s\n", slice.kind(), slice.toChars());
+ foreach (i, e; *ae.arguments)
+ {
+ if (i == 0)
+ *pe0 = extractOpDollarSideEffect(sc, ae);
+
+ if (e.op == TOK.interval && !(slice && slice.isTemplateDeclaration()))
+ {
+ Lfallback:
+ if (ae.arguments.dim == 1)
+ return null;
+ ae.error("multi-dimensional slicing requires template `opSlice`");
+ return ErrorExp.get();
+ }
+ //printf("[%d] e = %s\n", i, e.toChars());
+
+ // Create scope for '$' variable for this dimension
+ auto sym = new ArrayScopeSymbol(sc, ae);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+ ae.lengthVar = null; // Create it only if required
+ ae.currentDimension = i; // Dimension for $, if required
+
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+
+ if (ae.lengthVar && sc.func)
+ {
+ // If $ was used, declare it now
+ Expression de = new DeclarationExp(ae.loc, ae.lengthVar);
+ de = de.expressionSemantic(sc);
+ *pe0 = Expression.combine(*pe0, de);
+ }
+ sc = sc.pop();
+
+ if (auto ie = e.isIntervalExp())
+ {
+ auto tiargs = new Objects();
+ Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t);
+ edim = edim.expressionSemantic(sc);
+ tiargs.push(edim);
+
+ auto fargs = new Expressions(2);
+ (*fargs)[0] = ie.lwr;
+ (*fargs)[1] = ie.upr;
+
+ uint xerrors = global.startGagging();
+ sc = sc.push();
+ FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, fargs, FuncResolveFlag.quiet);
+ sc = sc.pop();
+ global.endGagging(xerrors);
+ if (!fslice)
+ goto Lfallback;
+
+ e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs);
+ e = new CallExp(ae.loc, e, fargs);
+ e = e.expressionSemantic(sc);
+ }
+
+ if (!e.type)
+ {
+ ae.error("`%s` has no value", e.toChars());
+ e = ErrorExp.get();
+ }
+ if (e.op == TOK.error)
+ return e;
+
+ (*ae.arguments)[i] = e;
+ }
+ return ae;
+}
+
+/**************************************
+ * Runs semantic on se.lwr and se.upr. Declares a temporary variable
+ * if '$' was used.
+ * Returns:
+ * ae, or ErrorExp if errors occurred
+ */
+Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* pe0)
+{
+ //assert(!ae.lengthVar);
+ if (!ie)
+ return ae;
+
+ VarDeclaration lengthVar = ae.lengthVar;
+ bool errors = false;
+
+ // create scope for '$'
+ auto sym = new ArrayScopeSymbol(sc, ae);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+
+ Expression sem(Expression e)
+ {
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ if (!e.type)
+ {
+ ae.error("`%s` has no value", e.toChars());
+ errors = true;
+ }
+ return e;
+ }
+
+ ie.lwr = sem(ie.lwr);
+ ie.upr = sem(ie.upr);
+
+ if (lengthVar != ae.lengthVar && sc.func)
+ {
+ // If $ was used, declare it now
+ Expression de = new DeclarationExp(ae.loc, ae.lengthVar);
+ de = de.expressionSemantic(sc);
+ *pe0 = Expression.combine(*pe0, de);
+ }
+
+ sc = sc.pop();
+
+ return errors ? ErrorExp.get() : ae;
+}
+
+/******************************
+ * Perform semantic() on an array of Expressions.
+ */
+bool arrayExpressionSemantic(Expressions* exps, Scope* sc, bool preserveErrors = false)
+{
+ bool err = false;
+ if (exps)
+ {
+ foreach (ref e; *exps)
+ {
+ if (e)
+ {
+ auto e2 = e.expressionSemantic(sc);
+ if (e2.op == TOK.error)
+ err = true;
+ if (preserveErrors || e2.op != TOK.error)
+ e = e2;
+ }
+ }
+ }
+ return err;
+}
+
+/******************************
+ * Check the tail CallExp is really property function call.
+ * Bugs:
+ * This doesn't appear to do anything.
+ */
+private bool checkPropertyCall(Expression e)
+{
+ e = lastComma(e);
+
+ if (auto ce = e.isCallExp())
+ {
+ if (ce.f)
+ {
+ auto tf = ce.f.type.isTypeFunction();
+ /* If a forward reference to ce.f, try to resolve it
+ */
+ if (!tf.deco && ce.f.semanticRun < PASS.semanticdone)
+ {
+ ce.f.dsymbolSemantic(null);
+ tf = ce.f.type.isTypeFunction();
+ }
+ }
+ else if (!ce.e1.type.isFunction_Delegate_PtrToFunction())
+ assert(0);
+ }
+ return false;
+}
+
+/******************************
+ * Find symbol in accordance with the UFCS name look up rule
+ */
+private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident)
+{
+ //printf("searchUFCS(ident = %s)\n", ident.toChars());
+ Loc loc = ue.loc;
+
+ // TODO: merge with Scope.search.searchScopes()
+ Dsymbol searchScopes(int flags)
+ {
+ Dsymbol s = null;
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (!scx.scopesym)
+ continue;
+ if (scx.scopesym.isModule())
+ flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
+ s = scx.scopesym.search(loc, ident, flags);
+ if (s)
+ {
+ // overload set contains only module scope symbols.
+ if (s.isOverloadSet())
+ break;
+ // selective/renamed imports also be picked up
+ if (AliasDeclaration ad = s.isAliasDeclaration())
+ {
+ if (ad._import)
+ break;
+ }
+ // See only module scope symbols for UFCS target.
+ Dsymbol p = s.toParent2();
+ if (p && p.isModule())
+ break;
+ }
+ s = null;
+
+ // Stop when we hit a module, but keep going if that is not just under the global scope
+ if (scx.scopesym.isModule() && !(scx.enclosing && !scx.enclosing.enclosing))
+ break;
+ }
+ return s;
+ }
+
+ int flags = 0;
+ Dsymbol s;
+
+ if (sc.flags & SCOPE.ignoresymbolvisibility)
+ flags |= IgnoreSymbolVisibility;
+
+ // First look in local scopes
+ s = searchScopes(flags | SearchLocalsOnly);
+ if (!s)
+ {
+ // Second look in imported modules
+ s = searchScopes(flags | SearchImportsOnly);
+ }
+
+ if (!s)
+ return ue.e1.type.Type.getProperty(sc, loc, ident, 0);
+
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ TemplateDeclaration td = getFuncTemplateDecl(f);
+ if (td)
+ {
+ if (td.overroot)
+ td = td.overroot;
+ s = td;
+ }
+ }
+
+ if (auto dti = ue.isDotTemplateInstanceExp())
+ {
+ auto ti = new TemplateInstance(loc, s.ident, dti.ti.tiargs);
+ if (!ti.updateTempDecl(sc, s))
+ return ErrorExp.get();
+ return new ScopeExp(loc, ti);
+ }
+ else
+ {
+ //printf("-searchUFCS() %s\n", s.toChars());
+ return new DsymbolExp(loc, s);
+ }
+}
+
+/******************************
+ * Pull out callable entity with UFCS.
+ */
+private Expression resolveUFCS(Scope* sc, CallExp ce)
+{
+ Loc loc = ce.loc;
+ Expression eleft;
+ Expression e;
+
+ if (auto die = ce.e1.isDotIdExp())
+ {
+ Identifier ident = die.ident;
+
+ Expression ex = die.semanticX(sc);
+ if (ex != die)
+ {
+ ce.e1 = ex;
+ return null;
+ }
+ eleft = die.e1;
+
+ Type t = eleft.type.toBasetype();
+ if (t.ty == Tarray || t.ty == Tsarray || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid))
+ {
+ /* Built-in types and arrays have no callable properties, so do shortcut.
+ * It is necessary in: e.init()
+ */
+ }
+ else if (t.ty == Taarray)
+ {
+ if (ident == Id.remove)
+ {
+ /* Transform:
+ * aa.remove(arg) into delete aa[arg]
+ */
+ if (!ce.arguments || ce.arguments.dim != 1)
+ {
+ ce.error("expected key as argument to `aa.remove()`");
+ return ErrorExp.get();
+ }
+ if (!eleft.type.isMutable())
+ {
+ ce.error("cannot remove key from `%s` associative array `%s`", MODtoChars(t.mod), eleft.toChars());
+ return ErrorExp.get();
+ }
+ Expression key = (*ce.arguments)[0];
+ key = key.expressionSemantic(sc);
+ key = resolveProperties(sc, key);
+
+ TypeAArray taa = t.isTypeAArray();
+ key = key.implicitCastTo(sc, taa.index);
+
+ if (key.checkValue() || key.checkSharedAccess(sc))
+ return ErrorExp.get();
+
+ semanticTypeInfo(sc, taa.index);
+
+ return new RemoveExp(loc, eleft, key);
+ }
+ }
+ else
+ {
+ if (Expression ey = die.semanticY(sc, 1))
+ {
+ if (ey.op == TOK.error)
+ return ey;
+ ce.e1 = ey;
+ if (isDotOpDispatch(ey))
+ {
+ uint errors = global.startGagging();
+ e = ce.syntaxCopy().expressionSemantic(sc);
+ if (!global.endGagging(errors))
+ return e;
+
+ // even opDispatch and UFCS must have valid arguments,
+ // so now that we've seen indication of a problem,
+ // check them for issues.
+ Expressions* originalArguments = Expression.arraySyntaxCopy(ce.arguments);
+
+ if (arrayExpressionSemantic(originalArguments, sc))
+ return ErrorExp.get();
+
+ /* fall down to UFCS */
+ }
+ else
+ return null;
+ }
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=13953
+ *
+ * If a struct has an alias this to an associative array
+ * and remove is used on a struct instance, we have to
+ * check first if there is a remove function that can be called
+ * on the struct. If not we must check the alias this.
+ *
+ * struct A
+ * {
+ * string[string] a;
+ * alias a this;
+ * }
+ *
+ * void fun()
+ * {
+ * A s;
+ * s.remove("foo");
+ * }
+ */
+ const errors = global.startGagging();
+ e = searchUFCS(sc, die, ident);
+ // if there were any errors and the identifier was remove
+ if (global.endGagging(errors))
+ {
+ if (ident == Id.remove)
+ {
+ // check alias this
+ Expression alias_e = resolveAliasThis(sc, die.e1, 1);
+ if (alias_e && alias_e != die.e1)
+ {
+ die.e1 = alias_e;
+ CallExp ce2 = ce.syntaxCopy();
+ ce2.e1 = die;
+ e = ce2.isCallExp().trySemantic(sc);
+ if (e)
+ return e;
+ }
+ }
+ // if alias this did not work out, print the initial errors
+ searchUFCS(sc, die, ident);
+ }
+ }
+ else if (auto dti = ce.e1.isDotTemplateInstanceExp())
+ {
+ if (Expression ey = dti.semanticY(sc, 1))
+ {
+ ce.e1 = ey;
+ return null;
+ }
+ eleft = dti.e1;
+ e = searchUFCS(sc, dti, dti.ti.name);
+ }
+ else
+ return null;
+
+ // Rewrite
+ ce.e1 = e;
+ if (!ce.arguments)
+ ce.arguments = new Expressions();
+ ce.arguments.shift(eleft);
+
+ return null;
+}
+
+/******************************
+ * Pull out property with UFCS.
+ */
+private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 = null)
+{
+ Loc loc = e1.loc;
+ Expression eleft;
+ Expression e;
+
+ if (auto die = e1.isDotIdExp())
+ {
+ eleft = die.e1;
+ e = searchUFCS(sc, die, die.ident);
+ }
+ else if (auto dti = e1.isDotTemplateInstanceExp())
+ {
+ eleft = dti.e1;
+ e = searchUFCS(sc, dti, dti.ti.name);
+ }
+ else
+ return null;
+
+ if (e is null)
+ return null;
+
+ // Rewrite
+ if (e2)
+ {
+ // run semantic without gagging
+ e2 = e2.expressionSemantic(sc);
+
+ /* f(e1) = e2
+ */
+ Expression ex = e.copy();
+ auto a1 = new Expressions(1);
+ (*a1)[0] = eleft;
+ ex = new CallExp(loc, ex, a1);
+ auto e1PassSemantic = ex.trySemantic(sc);
+
+ /* f(e1, e2)
+ */
+ auto a2 = new Expressions(2);
+ (*a2)[0] = eleft;
+ (*a2)[1] = e2;
+ e = new CallExp(loc, e, a2);
+ e = e.trySemantic(sc);
+ if (!e1PassSemantic && !e)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=20448
+ *
+ * If both versions have failed to pass semantic,
+ * f(e1) = e2 gets priority in error printing
+ * because f might be a templated function that
+ * failed to instantiate and we have to print
+ * the instantiation errors.
+ */
+ return e1.expressionSemantic(sc);
+ }
+ else if (ex && !e)
+ {
+ checkPropertyCall(ex);
+ ex = new AssignExp(loc, ex, e2);
+ return ex.expressionSemantic(sc);
+ }
+ else
+ {
+ // strict setter prints errors if fails
+ e = e.expressionSemantic(sc);
+ }
+ checkPropertyCall(e);
+ return e;
+ }
+ else
+ {
+ /* f(e1)
+ */
+ auto arguments = new Expressions(1);
+ (*arguments)[0] = eleft;
+ e = new CallExp(loc, e, arguments);
+ e = e.expressionSemantic(sc);
+ checkPropertyCall(e);
+ return e.expressionSemantic(sc);
+ }
+}
+
+/******************************
+ * If e1 is a property function (template), resolve it.
+ */
+Expression resolvePropertiesOnly(Scope* sc, Expression e1)
+{
+ //printf("e1 = %s %s\n", Token::toChars(e1.op), e1.toChars());
+
+ Expression handleOverloadSet(OverloadSet os)
+ {
+ assert(os);
+ foreach (s; os.a)
+ {
+ auto fd = s.isFuncDeclaration();
+ auto td = s.isTemplateDeclaration();
+ if (fd)
+ {
+ if (fd.type.isTypeFunction().isproperty)
+ return resolveProperties(sc, e1);
+ }
+ else if (td && td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null)
+ {
+ if (fd.type.isTypeFunction().isproperty ||
+ (fd.storage_class2 & STC.property) ||
+ (td._scope.stc & STC.property))
+ return resolveProperties(sc, e1);
+ }
+ }
+ return e1;
+ }
+
+ Expression handleTemplateDecl(TemplateDeclaration td)
+ {
+ assert(td);
+ if (td.onemember)
+ {
+ if (auto fd = td.onemember.isFuncDeclaration())
+ {
+ if (fd.type.isTypeFunction().isproperty ||
+ (fd.storage_class2 & STC.property) ||
+ (td._scope.stc & STC.property))
+ return resolveProperties(sc, e1);
+ }
+ }
+ return e1;
+ }
+
+ Expression handleFuncDecl(FuncDeclaration fd)
+ {
+ assert(fd);
+ if (fd.type.isTypeFunction().isproperty)
+ return resolveProperties(sc, e1);
+ return e1;
+ }
+
+ if (auto de = e1.isDotExp())
+ {
+ if (auto os = de.e2.isOverExp())
+ return handleOverloadSet(os.vars);
+ }
+ else if (auto oe = e1.isOverExp())
+ return handleOverloadSet(oe.vars);
+ else if (auto dti = e1.isDotTemplateInstanceExp())
+ {
+ if (dti.ti.tempdecl)
+ if (auto td = dti.ti.tempdecl.isTemplateDeclaration())
+ return handleTemplateDecl(td);
+ }
+ else if (auto dte = e1.isDotTemplateExp())
+ return handleTemplateDecl(dte.td);
+ else if (auto se = e1.isScopeExp())
+ {
+ Dsymbol s = se.sds;
+ TemplateInstance ti = s.isTemplateInstance();
+ if (ti && !ti.semanticRun && ti.tempdecl)
+ if (auto td = ti.tempdecl.isTemplateDeclaration())
+ return handleTemplateDecl(td);
+ }
+ else if (auto et = e1.isTemplateExp())
+ return handleTemplateDecl(et.td);
+ else if (e1.isDotVarExp() && e1.type.isTypeFunction())
+ {
+ DotVarExp dve = e1.isDotVarExp();
+ return handleFuncDecl(dve.var.isFuncDeclaration());
+ }
+ else if (e1.isVarExp() && e1.type && e1.type.isTypeFunction() && (sc.intypeof || !e1.isVarExp().var.needThis()))
+ return handleFuncDecl(e1.isVarExp().var.isFuncDeclaration());
+ return e1;
+}
+
+/****************************************
+ * Turn symbol `s` into the expression it represents.
+ *
+ * Params:
+ * s = symbol to resolve
+ * loc = location of use of `s`
+ * sc = context
+ * hasOverloads = applies if `s` represents a function.
+ * true means it's overloaded and will be resolved later,
+ * false means it's the exact function symbol.
+ * Returns:
+ * `s` turned into an expression, `ErrorExp` if an error occurred
+ */
+Expression symbolToExp(Dsymbol s, const ref Loc loc, Scope *sc, bool hasOverloads)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("DsymbolExp::resolve(%s %s)\n", s.kind(), s.toChars());
+ }
+
+Lagain:
+ Expression e;
+
+ //printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars());
+ //printf("s = '%s', s.kind = '%s'\n", s.toChars(), s.kind());
+ Dsymbol olds = s;
+ Declaration d = s.isDeclaration();
+ if (d && (d.storage_class & STC.templateparameter))
+ {
+ s = s.toAlias();
+ }
+ else
+ {
+ // functions are checked after overloading
+ // templates are checked after matching constraints
+ if (!s.isFuncDeclaration() && !s.isTemplateDeclaration())
+ {
+ s.checkDeprecated(loc, sc);
+ if (d)
+ d.checkDisabled(loc, sc);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=12023
+ // if 's' is a tuple variable, the tuple is returned.
+ s = s.toAlias();
+
+ //printf("s = '%s', s.kind = '%s', s.needThis() = %p\n", s.toChars(), s.kind(), s.needThis());
+ if (s != olds && !s.isFuncDeclaration() && !s.isTemplateDeclaration())
+ {
+ s.checkDeprecated(loc, sc);
+ if (d)
+ d.checkDisabled(loc, sc);
+ }
+ }
+
+ if (auto em = s.isEnumMember())
+ {
+ return em.getVarExp(loc, sc);
+ }
+ if (auto v = s.isVarDeclaration())
+ {
+ //printf("Identifier '%s' is a variable, type '%s'\n", s.toChars(), v.type.toChars());
+ if (sc.intypeof == 1 && !v.inuse)
+ v.dsymbolSemantic(sc);
+ if (!v.type || // during variable type inference
+ !v.type.deco && v.inuse) // during variable type semantic
+ {
+ if (v.inuse) // variable type depends on the variable itself
+ error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars());
+ else // variable type cannot be determined
+ error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ if (v.type.ty == Terror)
+ return ErrorExp.get();
+
+ if ((v.storage_class & STC.manifest) && v._init)
+ {
+ if (v.inuse)
+ {
+ error(loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ e = v.expandInitializer(loc);
+ v.inuse++;
+ e = e.expressionSemantic(sc);
+ v.inuse--;
+ return e;
+ }
+
+ // We need to run semantics to correctly set 'STC.field' if it is a member variable
+ // that could be forward referenced. This is needed for 'v.needThis()' to work
+ if (v.isThis())
+ v.dsymbolSemantic(sc);
+
+ // Change the ancestor lambdas to delegate before hasThis(sc) call.
+ if (v.checkNestedReference(sc, loc))
+ return ErrorExp.get();
+
+ if (v.needThis() && hasThis(sc))
+ e = new DotVarExp(loc, new ThisExp(loc), v);
+ else
+ e = new VarExp(loc, v);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (auto fld = s.isFuncLiteralDeclaration())
+ {
+ //printf("'%s' is a function literal\n", fld.toChars());
+ e = new FuncExp(loc, fld);
+ return e.expressionSemantic(sc);
+ }
+ if (auto f = s.isFuncDeclaration())
+ {
+ f = f.toAliasFunc();
+ if (!f.functionSemantic())
+ return ErrorExp.get();
+
+ if (!hasOverloads && f.checkForwardRef(loc))
+ return ErrorExp.get();
+
+ auto fd = s.isFuncDeclaration();
+ fd.type = f.type;
+ return new VarExp(loc, fd, hasOverloads);
+ }
+ if (OverDeclaration od = s.isOverDeclaration())
+ {
+ e = new VarExp(loc, od, true);
+ e.type = Type.tvoid;
+ return e;
+ }
+ if (OverloadSet o = s.isOverloadSet())
+ {
+ //printf("'%s' is an overload set\n", o.toChars());
+ return new OverExp(loc, o);
+ }
+
+ if (Import imp = s.isImport())
+ {
+ if (!imp.pkg)
+ {
+ .error(loc, "forward reference of import `%s`", imp.toChars());
+ return ErrorExp.get();
+ }
+ auto ie = new ScopeExp(loc, imp.pkg);
+ return ie.expressionSemantic(sc);
+ }
+ if (Package pkg = s.isPackage())
+ {
+ auto ie = new ScopeExp(loc, pkg);
+ return ie.expressionSemantic(sc);
+ }
+ if (Module mod = s.isModule())
+ {
+ auto ie = new ScopeExp(loc, mod);
+ return ie.expressionSemantic(sc);
+ }
+ if (Nspace ns = s.isNspace())
+ {
+ auto ie = new ScopeExp(loc, ns);
+ return ie.expressionSemantic(sc);
+ }
+
+ if (Type t = s.getType())
+ {
+ return (new TypeExp(loc, t)).expressionSemantic(sc);
+ }
+
+ if (TupleDeclaration tup = s.isTupleDeclaration())
+ {
+ if (tup.needThis() && hasThis(sc))
+ e = new DotVarExp(loc, new ThisExp(loc), tup);
+ else
+ e = new TupleExp(loc, tup);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ if (TemplateInstance ti = s.isTemplateInstance())
+ {
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors)
+ return ErrorExp.get();
+ s = ti.toAlias();
+ if (!s.isTemplateInstance())
+ goto Lagain;
+ e = new ScopeExp(loc, ti);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (TemplateDeclaration td = s.isTemplateDeclaration())
+ {
+ Dsymbol p = td.toParentLocal();
+ FuncDeclaration fdthis = hasThis(sc);
+ AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null;
+ if (fdthis && ad && fdthis.isMemberLocal() == ad && (td._scope.stc & STC.static_) == 0)
+ {
+ e = new DotTemplateExp(loc, new ThisExp(loc), td);
+ }
+ else
+ e = new TemplateExp(loc, td);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ .error(loc, "%s `%s` is not a variable", s.kind(), s.toChars());
+ return ErrorExp.get();
+}
+
+/*************************************************************
+ * Given var, get the
+ * right `this` pointer if var is in an outer class, but our
+ * existing `this` pointer is in an inner class.
+ * Params:
+ * loc = location to use for error messages
+ * sc = context
+ * ad = struct or class we need the correct `this` for
+ * e1 = existing `this`
+ * var = the specific member of ad we're accessing
+ * flag = if true, return `null` instead of throwing an error
+ * Returns:
+ * Expression representing the `this` for the var
+ */
+private Expression getRightThis(const ref Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Dsymbol var, int flag = 0)
+{
+ //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1.toChars(), ad.toChars(), var.toChars());
+L1:
+ Type t = e1.type.toBasetype();
+ //printf("e1.type = %s, var.type = %s\n", e1.type.toChars(), var.type.toChars());
+
+ if (e1.op == TOK.objcClassReference)
+ {
+ // We already have an Objective-C class reference, just use that as 'this'.
+ return e1;
+ }
+ else if (ad && ad.isClassDeclaration && ad.isClassDeclaration.classKind == ClassKind.objc &&
+ var.isFuncDeclaration && var.isFuncDeclaration.isStatic &&
+ var.isFuncDeclaration.objc.selector)
+ {
+ return new ObjcClassReferenceExp(e1.loc, ad.isClassDeclaration());
+ }
+
+ /* Access of a member which is a template parameter in dual-scope scenario
+ * class A { inc(alias m)() { ++m; } } // `m` needs `this` of `B`
+ * class B {int m; inc() { new A().inc!m(); } }
+ */
+ if (e1.op == TOK.this_)
+ {
+ FuncDeclaration f = hasThis(sc);
+ if (f && f.isThis2)
+ {
+ if (f.followInstantiationContext(ad))
+ {
+ e1 = new VarExp(loc, f.vthis);
+ e1 = new PtrExp(loc, e1);
+ e1 = new IndexExp(loc, e1, IntegerExp.literal!1);
+ e1 = getThisSkipNestedFuncs(loc, sc, f.toParent2(), ad, e1, t, var);
+ if (e1.op == TOK.error)
+ return e1;
+ goto L1;
+ }
+ }
+ }
+
+ /* If e1 is not the 'this' pointer for ad
+ */
+ if (ad &&
+ !(t.isTypePointer() && t.nextOf().isTypeStruct() && t.nextOf().isTypeStruct().sym == ad) &&
+ !(t.isTypeStruct() && t.isTypeStruct().sym == ad))
+ {
+ ClassDeclaration cd = ad.isClassDeclaration();
+ ClassDeclaration tcd = t.isClassHandle();
+
+ /* e1 is the right this if ad is a base class of e1
+ */
+ if (!cd || !tcd || !(tcd == cd || cd.isBaseOf(tcd, null)))
+ {
+ /* Only classes can be inner classes with an 'outer'
+ * member pointing to the enclosing class instance
+ */
+ if (tcd && tcd.isNested())
+ {
+ /* e1 is the 'this' pointer for an inner class: tcd.
+ * Rewrite it as the 'this' pointer for the outer class.
+ */
+ auto vthis = tcd.followInstantiationContext(ad) ? tcd.vthis2 : tcd.vthis;
+ e1 = new DotVarExp(loc, e1, vthis);
+ e1.type = vthis.type;
+ e1.type = e1.type.addMod(t.mod);
+ // Do not call ensureStaticLinkTo()
+ //e1 = e1.semantic(sc);
+
+ // Skip up over nested functions, and get the enclosing
+ // class type.
+ e1 = getThisSkipNestedFuncs(loc, sc, tcd.toParentP(ad), ad, e1, t, var);
+ if (e1.op == TOK.error)
+ return e1;
+ goto L1;
+ }
+
+ /* Can't find a path from e1 to ad
+ */
+ if (flag)
+ return null;
+ e1.error("`this` for `%s` needs to be type `%s` not type `%s`", var.toChars(), ad.toChars(), t.toChars());
+ return ErrorExp.get();
+ }
+ }
+ return e1;
+}
+
+/***************************************
+ * Pull out any properties.
+ */
+private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null)
+{
+ //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token.toChars(e1.op), e1.toChars(), e2 ? e2.toChars() : null);
+ Loc loc = e1.loc;
+
+ OverloadSet os;
+ Dsymbol s;
+ Objects* tiargs;
+ Type tthis;
+ if (auto de = e1.isDotExp())
+ {
+ if (auto oe = de.e2.isOverExp())
+ {
+ tiargs = null;
+ tthis = de.e1.type;
+ os = oe.vars;
+ goto Los;
+ }
+ }
+ else if (e1.isOverExp())
+ {
+ tiargs = null;
+ tthis = null;
+ os = e1.isOverExp().vars;
+ Los:
+ assert(os);
+ FuncDeclaration fd = null;
+ if (e2)
+ {
+ e2 = e2.expressionSemantic(sc);
+ if (e2.op == TOK.error)
+ return ErrorExp.get();
+ e2 = resolveProperties(sc, e2);
+
+ Expressions a;
+ a.push(e2);
+
+ for (size_t i = 0; i < os.a.dim; i++)
+ {
+ if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, &a, FuncResolveFlag.quiet))
+ {
+ if (f.errors)
+ return ErrorExp.get();
+ fd = f;
+ assert(fd.type.ty == Tfunction);
+ }
+ }
+ if (fd)
+ {
+ Expression e = new CallExp(loc, e1, e2);
+ return e.expressionSemantic(sc);
+ }
+ }
+ {
+ for (size_t i = 0; i < os.a.dim; i++)
+ {
+ if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, null, FuncResolveFlag.quiet))
+ {
+ if (f.errors)
+ return ErrorExp.get();
+ fd = f;
+ assert(fd.type.ty == Tfunction);
+ auto tf = fd.type.isTypeFunction();
+ if (!tf.isref && e2)
+ {
+ error(loc, "%s is not an lvalue", e1.toChars());
+ return ErrorExp.get();
+ }
+ }
+ }
+ if (fd)
+ {
+ Expression e = new CallExp(loc, e1);
+ if (e2)
+ e = new AssignExp(loc, e, e2);
+ return e.expressionSemantic(sc);
+ }
+ }
+ if (e2)
+ goto Leprop;
+ }
+ else if (auto dti = e1.isDotTemplateInstanceExp())
+ {
+ if (!dti.findTempDecl(sc))
+ goto Leprop;
+ if (!dti.ti.semanticTiargs(sc))
+ goto Leprop;
+ tiargs = dti.ti.tiargs;
+ tthis = dti.e1.type;
+ if ((os = dti.ti.tempdecl.isOverloadSet()) !is null)
+ goto Los;
+ if ((s = dti.ti.tempdecl) !is null)
+ goto Lfd;
+ }
+ else if (auto dte = e1.isDotTemplateExp())
+ {
+ s = dte.td;
+ tiargs = null;
+ tthis = dte.e1.type;
+ goto Lfd;
+ }
+ else if (auto se = e1.isScopeExp())
+ {
+ s = se.sds;
+ TemplateInstance ti = s.isTemplateInstance();
+ if (ti && !ti.semanticRun && ti.tempdecl)
+ {
+ //assert(ti.needsTypeInference(sc));
+ if (!ti.semanticTiargs(sc))
+ goto Leprop;
+ tiargs = ti.tiargs;
+ tthis = null;
+ if ((os = ti.tempdecl.isOverloadSet()) !is null)
+ goto Los;
+ if ((s = ti.tempdecl) !is null)
+ goto Lfd;
+ }
+ }
+ else if (auto te = e1.isTemplateExp())
+ {
+ s = te.td;
+ tiargs = null;
+ tthis = null;
+ goto Lfd;
+ }
+ else if (e1.op == TOK.dotVariable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(DotVarExp)e1).var.isOverDeclaration()))
+ {
+ DotVarExp dve = cast(DotVarExp)e1;
+ s = dve.var;
+ tiargs = null;
+ tthis = dve.e1.type;
+ goto Lfd;
+ }
+ else if (e1.op == TOK.variable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(VarExp)e1).var.isOverDeclaration()))
+ {
+ s = (cast(VarExp)e1).var;
+ tiargs = null;
+ tthis = null;
+ Lfd:
+ assert(s);
+ if (e2)
+ {
+ e2 = e2.expressionSemantic(sc);
+ if (e2.op == TOK.error)
+ return ErrorExp.get();
+ e2 = resolveProperties(sc, e2);
+
+ Expressions a;
+ a.push(e2);
+
+ FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, FuncResolveFlag.quiet);
+ if (fd && fd.type)
+ {
+ if (fd.errors)
+ return ErrorExp.get();
+ if (!checkSymbolAccess(sc, fd))
+ {
+ // @@@DEPRECATED_2020-10@@@
+ // When turning into error, uncomment the return statement
+ TypeFunction tf = cast(TypeFunction)fd.type;
+ deprecation(loc, "Function `%s` of type `%s` is not accessible from module `%s`",
+ fd.toPrettyChars(), tf.toChars, sc._module.toChars);
+ //return ErrorExp.get();
+ }
+ assert(fd.type.ty == Tfunction);
+ Expression e = new CallExp(loc, e1, e2);
+ return e.expressionSemantic(sc);
+ }
+ }
+ {
+ FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, null, FuncResolveFlag.quiet);
+ if (fd && fd.type)
+ {
+ if (fd.errors)
+ return ErrorExp.get();
+ assert(fd.type.ty == Tfunction);
+ TypeFunction tf = cast(TypeFunction)fd.type;
+ if (!e2 || tf.isref)
+ {
+ if (!checkSymbolAccess(sc, fd))
+ {
+ // @@@DEPRECATED_2020-10@@@
+ // When turning into error, uncomment the return statement
+ deprecation(loc, "Function `%s` of type `%s` is not accessible from module `%s`",
+ fd.toPrettyChars(), tf.toChars, sc._module.toChars);
+ //return ErrorExp.get();
+ }
+ Expression e = new CallExp(loc, e1);
+ if (e2)
+ e = new AssignExp(loc, e, e2);
+ return e.expressionSemantic(sc);
+ }
+ }
+ }
+ if (FuncDeclaration fd = s.isFuncDeclaration())
+ {
+ // Keep better diagnostic message for invalid property usage of functions
+ assert(fd.type.ty == Tfunction);
+ Expression e = new CallExp(loc, e1, e2);
+ return e.expressionSemantic(sc);
+ }
+ if (e2)
+ goto Leprop;
+ }
+ if (e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)e1;
+ VarDeclaration v = ve.var.isVarDeclaration();
+ if (v && ve.checkPurity(sc, v))
+ return ErrorExp.get();
+ }
+ if (e2)
+ return null;
+
+ if (e1.type && e1.op != TOK.type) // function type is not a property
+ {
+ /* Look for e1 being a lazy parameter; rewrite as delegate call
+ * only if the symbol wasn't already treated as a delegate
+ */
+ auto ve = e1.isVarExp();
+ if (ve && ve.var.storage_class & STC.lazy_ && !ve.delegateWasExtracted)
+ {
+ Expression e = new CallExp(loc, e1);
+ return e.expressionSemantic(sc);
+ }
+ else if (e1.op == TOK.dotVariable)
+ {
+ // Check for reading overlapped pointer field in @safe code.
+ if (checkUnsafeAccess(sc, e1, true, true))
+ return ErrorExp.get();
+ }
+ else if (e1.op == TOK.call)
+ {
+ CallExp ce = cast(CallExp)e1;
+ // Check for reading overlapped pointer field in @safe code.
+ if (checkUnsafeAccess(sc, ce.e1, true, true))
+ return ErrorExp.get();
+ }
+ }
+
+ if (!e1.type)
+ {
+ error(loc, "cannot resolve type for %s", e1.toChars());
+ e1 = ErrorExp.get();
+ }
+ return e1;
+
+Leprop:
+ error(loc, "not a property %s", e1.toChars());
+ return ErrorExp.get();
+}
+
+extern (C++) Expression resolveProperties(Scope* sc, Expression e)
+{
+ //printf("resolveProperties(%s)\n", e.toChars());
+ e = resolvePropertiesX(sc, e);
+ if (e.checkRightThis(sc))
+ return ErrorExp.get();
+ return e;
+}
+
+/****************************************
+ * The common type is determined by applying ?: to each pair.
+ * Output:
+ * exps[] properties resolved, implicitly cast to common type, rewritten in place
+ * Returns:
+ * The common type, or `null` if an error has occured
+ */
+private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
+{
+ /* Still have a problem with:
+ * ubyte[][] = [ cast(ubyte[])"hello", [1]];
+ * which works if the array literal is initialized top down with the ubyte[][]
+ * type, but fails with this function doing bottom up typing.
+ */
+
+ //printf("arrayExpressionToCommonType()\n");
+ scope IntegerExp integerexp = IntegerExp.literal!0;
+ scope CondExp condexp = new CondExp(Loc.initial, integerexp, null, null);
+
+ Type t0 = null;
+ Expression e0 = null;
+ size_t j0 = ~0;
+ bool foundType;
+
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression e = exps[i];
+ if (!e)
+ continue;
+
+ e = resolveProperties(sc, e);
+ if (!e.type)
+ {
+ e.error("`%s` has no value", e.toChars());
+ t0 = Type.terror;
+ continue;
+ }
+ if (e.op == TOK.type)
+ {
+ foundType = true; // do not break immediately, there might be more errors
+ e.checkValue(); // report an error "type T has no value"
+ t0 = Type.terror;
+ continue;
+ }
+ if (e.type.ty == Tvoid)
+ {
+ // void expressions do not concur to the determination of the common
+ // type.
+ continue;
+ }
+ if (checkNonAssignmentArrayOp(e))
+ {
+ t0 = Type.terror;
+ continue;
+ }
+
+ e = doCopyOrMove(sc, e);
+
+ if (!foundType && t0 && !t0.equals(e.type))
+ {
+ /* This applies ?: to merge the types. It's backwards;
+ * ?: should call this function to merge types.
+ */
+ condexp.type = null;
+ condexp.e1 = e0;
+ condexp.e2 = e;
+ condexp.loc = e.loc;
+ Expression ex = condexp.expressionSemantic(sc);
+ if (ex.op == TOK.error)
+ e = ex;
+ else if (e.op == TOK.function_ || e.op == TOK.delegate_)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=21285
+ // Functions and delegates don't convert correctly with castTo below
+ exps[j0] = condexp.e1;
+ e = condexp.e2;
+ }
+ else
+ {
+ // Convert to common type
+ exps[j0] = condexp.e1.castTo(sc, condexp.type);
+ e = condexp.e2.castTo(sc, condexp.type);
+ }
+ }
+ j0 = i;
+ e0 = e;
+ t0 = e.type;
+ if (e.op != TOK.error)
+ exps[i] = e;
+ }
+
+ // [] is typed as void[]
+ if (!t0)
+ return Type.tvoid;
+
+ // It's an error, don't do the cast
+ if (t0.ty == Terror)
+ return null;
+
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression e = exps[i];
+ if (!e)
+ continue;
+
+ e = e.implicitCastTo(sc, t0);
+ if (e.op == TOK.error)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=13024
+ * a workaround for the bug in typeMerge -
+ * it should paint e1 and e2 by deduced common type,
+ * but doesn't in this particular case.
+ */
+ return null;
+ }
+ exps[i] = e;
+ }
+ return t0;
+}
+
+private Expression opAssignToOp(const ref Loc loc, TOK op, Expression e1, Expression e2)
+{
+ Expression e;
+ switch (op)
+ {
+ case TOK.addAssign:
+ e = new AddExp(loc, e1, e2);
+ break;
+
+ case TOK.minAssign:
+ e = new MinExp(loc, e1, e2);
+ break;
+
+ case TOK.mulAssign:
+ e = new MulExp(loc, e1, e2);
+ break;
+
+ case TOK.divAssign:
+ e = new DivExp(loc, e1, e2);
+ break;
+
+ case TOK.modAssign:
+ e = new ModExp(loc, e1, e2);
+ break;
+
+ case TOK.andAssign:
+ e = new AndExp(loc, e1, e2);
+ break;
+
+ case TOK.orAssign:
+ e = new OrExp(loc, e1, e2);
+ break;
+
+ case TOK.xorAssign:
+ e = new XorExp(loc, e1, e2);
+ break;
+
+ case TOK.leftShiftAssign:
+ e = new ShlExp(loc, e1, e2);
+ break;
+
+ case TOK.rightShiftAssign:
+ e = new ShrExp(loc, e1, e2);
+ break;
+
+ case TOK.unsignedRightShiftAssign:
+ e = new UshrExp(loc, e1, e2);
+ break;
+
+ default:
+ assert(0);
+ }
+ return e;
+}
+
+/*********************
+ * Rewrite:
+ * array.length op= e2
+ * as:
+ * array.length = array.length op e2
+ * or:
+ * auto tmp = &array;
+ * (*tmp).length = (*tmp).length op e2
+ */
+private Expression rewriteOpAssign(BinExp exp)
+{
+ Expression e;
+
+ assert(exp.e1.op == TOK.arrayLength);
+ ArrayLengthExp ale = cast(ArrayLengthExp)exp.e1;
+ if (ale.e1.op == TOK.variable)
+ {
+ e = opAssignToOp(exp.loc, exp.op, ale, exp.e2);
+ e = new AssignExp(exp.loc, ale.syntaxCopy(), e);
+ }
+ else
+ {
+ /* auto tmp = &array;
+ * (*tmp).length = (*tmp).length op e2
+ */
+ auto tmp = copyToTemp(0, "__arraylength", new AddrExp(ale.loc, ale.e1));
+
+ Expression e1 = new ArrayLengthExp(ale.loc, new PtrExp(ale.loc, new VarExp(ale.loc, tmp)));
+ Expression elvalue = e1.syntaxCopy();
+ e = opAssignToOp(exp.loc, exp.op, e1, exp.e2);
+ e = new AssignExp(exp.loc, elvalue, e);
+ e = new CommaExp(exp.loc, new DeclarationExp(ale.loc, tmp), e);
+ }
+ return e;
+}
+
+/****************************************
+ * Preprocess arguments to function.
+ * Input:
+ * reportErrors whether or not to report errors here. Some callers are not
+ * checking actual function params, so they'll do their own error reporting
+ * Output:
+ * exps[] tuples expanded, properties resolved, rewritten in place
+ * Returns:
+ * true a semantic error occurred
+ */
+private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool reportErrors = true)
+{
+ bool err = false;
+ if (exps)
+ {
+ expandTuples(exps);
+
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression arg = (*exps)[i];
+ arg = resolveProperties(sc, arg);
+ if (arg.op == TOK.type)
+ {
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ arg = resolveAliasThis(sc, arg);
+
+ if (arg.op == TOK.type)
+ {
+ if (reportErrors)
+ {
+ arg.error("cannot pass type `%s` as a function argument", arg.toChars());
+ arg = ErrorExp.get();
+ }
+ err = true;
+ }
+ }
+ else if (arg.type.toBasetype().ty == Tfunction)
+ {
+ if (reportErrors)
+ {
+ arg.error("cannot pass function `%s` as a function argument", arg.toChars());
+ arg = ErrorExp.get();
+ }
+ err = true;
+ }
+ else if (checkNonAssignmentArrayOp(arg))
+ {
+ arg = ErrorExp.get();
+ err = true;
+ }
+ (*exps)[i] = arg;
+ }
+ }
+ return err;
+}
+
+/********************************************
+ * Issue an error if default construction is disabled for type t.
+ * Default construction is required for arrays and 'out' parameters.
+ * Returns:
+ * true an error was issued
+ */
+private bool checkDefCtor(Loc loc, Type t)
+{
+ t = t.baseElemOf();
+ if (t.ty == Tstruct)
+ {
+ StructDeclaration sd = (cast(TypeStruct)t).sym;
+ if (sd.noDefaultCtor)
+ {
+ sd.error(loc, "default construction is disabled");
+ return true;
+ }
+ }
+ return false;
+}
+
+/****************************************
+ * Now that we know the exact type of the function we're calling,
+ * the arguments[] need to be adjusted:
+ * 1. implicitly convert argument to the corresponding parameter type
+ * 2. add default arguments for any missing arguments
+ * 3. do default promotions on arguments corresponding to ...
+ * 4. add hidden _arguments[] argument
+ * 5. call copy constructor for struct value arguments
+ * Params:
+ * loc = location of function call
+ * sc = context
+ * tf = type of the function
+ * ethis = `this` argument, `null` if none or not known
+ * tthis = type of `this` argument, `null` if no `this` argument
+ * arguments = array of actual arguments to function call
+ * fd = the function being called, `null` if called indirectly
+ * prettype = set to return type of function
+ * peprefix = set to expression to execute before `arguments[]` are evaluated, `null` if none
+ * Returns:
+ * true errors happened
+ */
+private bool functionParameters(const ref Loc loc, Scope* sc,
+ TypeFunction tf, Expression ethis, Type tthis, Expressions* arguments, FuncDeclaration fd,
+ Type* prettype, Expression* peprefix)
+{
+ //printf("functionParameters() %s\n", fd ? fd.toChars() : "");
+ assert(arguments);
+ assert(fd || tf.next);
+ size_t nargs = arguments ? arguments.dim : 0;
+ const size_t nparams = tf.parameterList.length;
+ const olderrors = global.errors;
+ bool err = false;
+ *prettype = Type.terror;
+ Expression eprefix = null;
+ *peprefix = null;
+
+ if (nargs > nparams && tf.parameterList.varargs == VarArg.none)
+ {
+ error(loc, "expected %llu arguments, not %llu for non-variadic function type `%s`", cast(ulong)nparams, cast(ulong)nargs, tf.toChars());
+ return true;
+ }
+
+ // If inferring return type, and semantic3() needs to be run if not already run
+ if (!tf.next && fd.inferRetType)
+ {
+ fd.functionSemantic();
+ }
+ else if (fd && fd.parent)
+ {
+ TemplateInstance ti = fd.parent.isTemplateInstance();
+ if (ti && ti.tempdecl)
+ {
+ fd.functionSemantic3();
+ }
+ }
+
+ /* If calling a pragma(inline, true) function,
+ * set flag to later scan for inlines.
+ */
+ if (fd && fd.inlining == PINLINE.always)
+ {
+ if (sc._module)
+ sc._module.hasAlwaysInlines = true;
+ if (sc.func)
+ sc.func.hasAlwaysInlines = true;
+ }
+
+ const isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration();
+
+ const size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)
+
+ /* If the function return type has wildcards in it, we'll need to figure out the actual type
+ * based on the actual argument types.
+ * Start with the `this` argument, later on merge into wildmatch the mod bits of the rest
+ * of the arguments.
+ */
+ MOD wildmatch = (tthis && !isCtorCall) ? tthis.Type.deduceWild(tf, false) : 0;
+
+ bool done = false;
+ foreach (const i; 0 .. n)
+ {
+ Expression arg = (i < nargs) ? (*arguments)[i] : null;
+
+ if (i < nparams)
+ {
+ bool errorArgs()
+ {
+ error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs);
+ return true;
+ }
+
+ Parameter p = tf.parameterList[i];
+
+ if (!arg)
+ {
+ if (!p.defaultArg)
+ {
+ if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams)
+ goto L2;
+ return errorArgs();
+ }
+ arg = p.defaultArg;
+ arg = inlineCopy(arg, sc);
+ // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__
+ arg = arg.resolveLoc(loc, sc);
+ arguments.push(arg);
+ nargs++;
+ }
+ else
+ {
+ if (isDefaultInitOp(arg.op))
+ {
+ arg = arg.resolveLoc(loc, sc);
+ (*arguments)[i] = arg;
+ }
+ }
+
+
+ if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic
+ {
+ //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars());
+ {
+ MATCH m;
+ if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch)
+ {
+ if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m)
+ goto L2;
+ else if (nargs != nparams)
+ return errorArgs();
+ goto L1;
+ }
+ }
+ L2:
+ Type tb = p.type.toBasetype();
+ switch (tb.ty)
+ {
+ case Tsarray:
+ case Tarray:
+ {
+ /* Create a static array variable v of type arg.type:
+ * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ];
+ *
+ * The array literal in the initializer of the hidden variable
+ * is now optimized.
+ * https://issues.dlang.org/show_bug.cgi?id=2356
+ */
+ Type tbn = (cast(TypeArray)tb).next; // array element type
+ Type tret = p.isLazyArray();
+
+ auto elements = new Expressions(nargs - i);
+ foreach (u; 0 .. elements.dim)
+ {
+ Expression a = (*arguments)[i + u];
+ if (tret && a.implicitConvTo(tret))
+ {
+ // p is a lazy array of delegates, tret is return type of the delegates
+ a = a.implicitCastTo(sc, tret)
+ .optimize(WANTvalue)
+ .toDelegate(tret, sc);
+ }
+ else
+ a = a.implicitCastTo(sc, tbn);
+ a = a.addDtorHook(sc);
+ (*elements)[u] = a;
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=14395
+ // Convert to a static array literal, or its slice.
+ arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements);
+ if (tb.ty == Tarray)
+ {
+ arg = new SliceExp(loc, arg, null, null);
+ arg.type = p.type;
+ }
+ break;
+ }
+ case Tclass:
+ {
+ /* Set arg to be:
+ * new Tclass(arg0, arg1, ..., argn)
+ */
+ auto args = new Expressions(nargs - i);
+ foreach (u; i .. nargs)
+ (*args)[u - i] = (*arguments)[u];
+ arg = new NewExp(loc, null, null, p.type, args);
+ break;
+ }
+ default:
+ if (!arg)
+ {
+ error(loc, "not enough arguments");
+ return true;
+ }
+ break;
+ }
+ arg = arg.expressionSemantic(sc);
+ //printf("\targ = '%s'\n", arg.toChars());
+ arguments.setDim(i + 1);
+ (*arguments)[i] = arg;
+ nargs = i + 1;
+ done = true;
+ }
+
+ L1:
+ if (!(p.storageClass & STC.lazy_ && p.type.ty == Tvoid))
+ {
+ if (ubyte wm = arg.type.deduceWild(p.type, p.isReference()))
+ {
+ wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm;
+ //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch);
+ }
+ }
+ }
+ if (done)
+ break;
+ }
+ if ((wildmatch == MODFlags.mutable || wildmatch == MODFlags.immutable_) &&
+ tf.next && tf.next.hasWild() &&
+ (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf())))
+ {
+ bool errorInout(MOD wildmatch)
+ {
+ const(char)* s = wildmatch == MODFlags.mutable ? "mutable" : MODtoChars(wildmatch);
+ error(loc, "modify `inout` to `%s` is not allowed inside `inout` function", s);
+ return true;
+ }
+
+ if (fd)
+ {
+ /* If the called function may return the reference to
+ * outer inout data, it should be rejected.
+ *
+ * void foo(ref inout(int) x) {
+ * ref inout(int) bar(inout(int)) { return x; }
+ * struct S {
+ * ref inout(int) bar() inout { return x; }
+ * ref inout(int) baz(alias a)() inout { return x; }
+ * }
+ * bar(int.init) = 1; // bad!
+ * S().bar() = 1; // bad!
+ * }
+ * void test() {
+ * int a;
+ * auto s = foo(a);
+ * s.baz!a() = 1; // bad!
+ * }
+ *
+ */
+ bool checkEnclosingWild(Dsymbol s)
+ {
+ bool checkWild(Dsymbol s)
+ {
+ if (!s)
+ return false;
+ if (auto ad = s.isAggregateDeclaration())
+ {
+ if (ad.isNested())
+ return checkEnclosingWild(s);
+ }
+ else if (auto ff = s.isFuncDeclaration())
+ {
+ if ((cast(TypeFunction)ff.type).iswild)
+ return errorInout(wildmatch);
+
+ if (ff.isNested() || ff.isThis())
+ return checkEnclosingWild(s);
+ }
+ return false;
+ }
+
+ Dsymbol ctx0 = s.toParent2();
+ Dsymbol ctx1 = s.toParentLocal();
+ if (checkWild(ctx0))
+ return true;
+ if (ctx0 != ctx1)
+ return checkWild(ctx1);
+ return false;
+ }
+ if ((fd.isThis() || fd.isNested()) && checkEnclosingWild(fd))
+ return true;
+ }
+ else if (tf.isWild())
+ return errorInout(wildmatch);
+ }
+
+ Expression firstArg = ((tf.next && tf.next.ty == Tvoid || isCtorCall) &&
+ tthis &&
+ tthis.isMutable() && tthis.toBasetype().ty == Tstruct &&
+ tthis.hasPointers())
+ ? ethis : null;
+
+ assert(nargs >= nparams);
+ foreach (const i, arg; (*arguments)[0 .. nargs])
+ {
+ assert(arg);
+ if (i < nparams)
+ {
+ Parameter p = tf.parameterList[i];
+ Type targ = arg.type; // keep original type for isCopyable() because alias this
+ // resolution may hide an uncopyable type
+
+ if (!(p.storageClass & STC.lazy_ && p.type.ty == Tvoid))
+ {
+ Type tprm = p.type.hasWild()
+ ? p.type.substWildTo(wildmatch)
+ : p.type;
+
+ const hasCopyCtor = (arg.type.ty == Tstruct) && (cast(TypeStruct)arg.type).sym.hasCopyCtor;
+ const typesMatch = arg.type.mutableOf().unSharedOf().equals(tprm.mutableOf().unSharedOf());
+ if (!((hasCopyCtor && typesMatch) || tprm.equals(arg.type)))
+ {
+ //printf("arg.type = %s, p.type = %s\n", arg.type.toChars(), p.type.toChars());
+ arg = arg.implicitCastTo(sc, tprm);
+ arg = arg.optimize(WANTvalue, p.isReference());
+ }
+ }
+
+ // Support passing rvalue to `in` parameters
+ if ((p.storageClass & (STC.in_ | STC.ref_)) == (STC.in_ | STC.ref_))
+ {
+ if (!arg.isLvalue())
+ {
+ auto v = copyToTemp(STC.exptemp, "__rvalue", arg);
+ Expression ev = new DeclarationExp(arg.loc, v);
+ ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
+ arg = ev.expressionSemantic(sc);
+ }
+ arg = arg.toLvalue(sc, arg);
+
+ // Look for mutable misaligned pointer, etc., in @safe mode
+ err |= checkUnsafeAccess(sc, arg, false, true);
+ }
+ else if (p.storageClass & STC.ref_)
+ {
+ if (global.params.rvalueRefParam &&
+ !arg.isLvalue() &&
+ targ.isCopyable())
+ { /* allow rvalues to be passed to ref parameters by copying
+ * them to a temp, then pass the temp as the argument
+ */
+ auto v = copyToTemp(0, "__rvalue", arg);
+ Expression ev = new DeclarationExp(arg.loc, v);
+ ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
+ arg = ev.expressionSemantic(sc);
+ }
+ arg = arg.toLvalue(sc, arg);
+
+ // Look for mutable misaligned pointer, etc., in @safe mode
+ err |= checkUnsafeAccess(sc, arg, false, true);
+ }
+ else if (p.storageClass & STC.out_)
+ {
+ Type t = arg.type;
+ if (!t.isMutable() || !t.isAssignable()) // check blit assignable
+ {
+ arg.error("cannot modify struct `%s` with immutable members", arg.toChars());
+ err = true;
+ }
+ else
+ {
+ // Look for misaligned pointer, etc., in @safe mode
+ err |= checkUnsafeAccess(sc, arg, false, true);
+ err |= checkDefCtor(arg.loc, t); // t must be default constructible
+ }
+ arg = arg.toLvalue(sc, arg);
+ }
+ else if (p.storageClass & STC.lazy_)
+ {
+ // Convert lazy argument to a delegate
+ auto t = (p.type.ty == Tvoid) ? p.type : arg.type;
+ arg = toDelegate(arg, t, sc);
+ }
+ //printf("arg: %s\n", arg.toChars());
+ //printf("type: %s\n", arg.type.toChars());
+ //printf("param: %s\n", p.toChars());
+
+ if (firstArg && p.storageClass & STC.return_)
+ {
+ /* Argument value can be assigned to firstArg.
+ * Check arg to see if it matters.
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ err |= checkParamArgumentReturn(sc, firstArg, arg, false);
+ }
+ else if (tf.parameterEscapes(tthis, p))
+ {
+ /* Argument value can escape from the called function.
+ * Check arg to see if it matters.
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ err |= checkParamArgumentEscape(sc, fd, p, arg, false, false);
+ }
+ else if (!(p.storageClass & STC.return_))
+ {
+ /* Argument value cannot escape from the called function.
+ */
+ Expression a = arg;
+ if (a.op == TOK.cast_)
+ a = (cast(CastExp)a).e1;
+
+ ArrayLiteralExp ale;
+ if (p.type.toBasetype().ty == Tarray &&
+ (ale = a.isArrayLiteralExp()) !is null)
+ {
+ // allocate the array literal as temporary static array on the stack
+ ale.type = ale.type.nextOf().sarrayOf(ale.elements ? ale.elements.length : 0);
+ auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale);
+ auto declareTmp = new DeclarationExp(ale.loc, tmp);
+ auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp), p.type);
+ arg = CommaExp.combine(declareTmp, castToSlice);
+ arg = arg.expressionSemantic(sc);
+ }
+ else if (a.op == TOK.function_)
+ {
+ /* Function literals can only appear once, so if this
+ * appearance was scoped, there cannot be any others.
+ */
+ FuncExp fe = cast(FuncExp)a;
+ fe.fd.tookAddressOf = 0;
+ }
+ else if (a.op == TOK.delegate_)
+ {
+ /* For passing a delegate to a scoped parameter,
+ * this doesn't count as taking the address of it.
+ * We only worry about 'escaping' references to the function.
+ */
+ DelegateExp de = cast(DelegateExp)a;
+ if (de.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)de.e1;
+ FuncDeclaration f = ve.var.isFuncDeclaration();
+ if (f)
+ {
+ if (f.tookAddressOf)
+ --f.tookAddressOf;
+ //printf("--tookAddressOf = %d\n", f.tookAddressOf);
+ }
+ }
+ }
+ }
+ if (!p.isReference())
+ err |= arg.checkSharedAccess(sc);
+
+ arg = arg.optimize(WANTvalue, p.isReference());
+
+ /* Determine if this parameter is the "first reference" parameter through which
+ * later "return" arguments can be stored.
+ */
+ if (i == 0 && !tthis && p.isReference() && p.type &&
+ (tf.next && tf.next.ty == Tvoid || isCtorCall))
+ {
+ Type tb = p.type.baseElemOf();
+ if (tb.isMutable() && tb.hasPointers())
+ {
+ firstArg = arg;
+ }
+ }
+ }
+ else
+ {
+ // These will be the trailing ... arguments
+ // If not D linkage, do promotions
+ if (tf.linkage != LINK.d)
+ {
+ // Promote bytes, words, etc., to ints
+ arg = integralPromotions(arg, sc);
+
+ // Promote floats to doubles
+ switch (arg.type.ty)
+ {
+ case Tfloat32:
+ arg = arg.castTo(sc, Type.tfloat64);
+ break;
+
+ case Timaginary32:
+ arg = arg.castTo(sc, Type.timaginary64);
+ break;
+
+ default:
+ break;
+ }
+ if (tf.parameterList.varargs == VarArg.variadic)
+ {
+ const(char)* p = tf.linkage == LINK.c ? "extern(C)" : "extern(C++)";
+ if (arg.type.ty == Tarray)
+ {
+ arg.error("cannot pass dynamic arrays to `%s` vararg functions", p);
+ err = true;
+ }
+ if (arg.type.ty == Tsarray)
+ {
+ arg.error("cannot pass static arrays to `%s` vararg functions", p);
+ err = true;
+ }
+ }
+ }
+
+ // Do not allow types that need destructors or copy constructors.
+ if (arg.type.needsDestruction())
+ {
+ arg.error("cannot pass types that need destruction as variadic arguments");
+ err = true;
+ }
+ if (arg.type.needsCopyOrPostblit())
+ {
+ arg.error("cannot pass types with postblits or copy constructors as variadic arguments");
+ err = true;
+ }
+
+ // Convert static arrays to dynamic arrays
+ // BUG: I don't think this is right for D2
+ Type tb = arg.type.toBasetype();
+ if (tb.ty == Tsarray)
+ {
+ TypeSArray ts = cast(TypeSArray)tb;
+ Type ta = ts.next.arrayOf();
+ if (ts.size(arg.loc) == 0)
+ arg = new NullExp(arg.loc, ta);
+ else
+ arg = arg.castTo(sc, ta);
+ }
+ if (tb.ty == Tstruct)
+ {
+ //arg = callCpCtor(sc, arg);
+ }
+ // Give error for overloaded function addresses
+ if (arg.op == TOK.symbolOffset)
+ {
+ SymOffExp se = cast(SymOffExp)arg;
+ if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique())
+ {
+ arg.error("function `%s` is overloaded", arg.toChars());
+ err = true;
+ }
+ }
+ err |= arg.checkValue();
+ err |= arg.checkSharedAccess(sc);
+ arg = arg.optimize(WANTvalue);
+ }
+ (*arguments)[i] = arg;
+ }
+
+ /* If calling C scanf(), printf(), or any variants, check the format string against the arguments
+ */
+ const isVa_list = tf.parameterList.varargs == VarArg.none;
+ if (fd && fd.flags & FUNCFLAG.printf)
+ {
+ if (auto se = (*arguments)[nparams - 1 - isVa_list].isStringExp())
+ {
+ checkPrintfFormat(se.loc, se.peekString(), (*arguments)[nparams .. nargs], isVa_list);
+ }
+ }
+ else if (fd && fd.flags & FUNCFLAG.scanf)
+ {
+ if (auto se = (*arguments)[nparams - 1 - isVa_list].isStringExp())
+ {
+ checkScanfFormat(se.loc, se.peekString(), (*arguments)[nparams .. nargs], isVa_list);
+ }
+ }
+ else
+ {
+ // TODO: not checking the "v" functions yet (for those, check format string only, not args)
+ }
+
+ /* Remaining problems:
+ * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is
+ * implemented by calling a function) we'll defer this for now.
+ * 2. value structs (or static arrays of them) that need to be copy constructed
+ * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the
+ * function gets called.
+ * 4. value structs need to be destructed after the function call for platforms where the caller destroys the arguments.
+ * 2, 3 and 4 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned
+ * up properly. Pushing arguments on the stack then cannot fail.
+ */
+ {
+ /* TODO: tackle problem 1)
+ */
+ const bool leftToRight = true; // TODO: Any cases that need rightToLeft?
+ if (!leftToRight)
+ assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity
+
+ /* Does Problem (4) apply?
+ */
+ const bool callerDestroysArgs = !target.isCalleeDestroyingArgs(tf);
+
+ const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1);
+ const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1);
+ const ptrdiff_t step = (leftToRight ? 1 : -1);
+
+ /* Compute indices of last throwing argument and first arg needing destruction.
+ * Used to not set up destructors unless an arg needs destruction on a throw
+ * in a later argument.
+ */
+ ptrdiff_t lastthrow = -1; // last argument that may throw
+ ptrdiff_t firstdtor = -1; // first argument that needs destruction
+ ptrdiff_t lastdtor = -1; // last argument that needs destruction
+ for (ptrdiff_t i = start; i != end; i += step)
+ {
+ Expression arg = (*arguments)[i];
+ if (canThrow(arg, sc.func, false))
+ lastthrow = i;
+ if (arg.type.needsDestruction())
+ {
+ Parameter p = (i >= nparams ? null : tf.parameterList[i]);
+ if (!(p && (p.storageClass & (STC.lazy_ | STC.ref_ | STC.out_))))
+ {
+ if (firstdtor == -1)
+ firstdtor = i;
+ lastdtor = i;
+ }
+ }
+ }
+
+ /* Do we need 'eprefix' for problems 3 or 4?
+ */
+ const bool needsPrefix = callerDestroysArgs
+ ? firstdtor >= 0 // true if any argument needs destruction
+ : firstdtor >= 0 && lastthrow >= 0 &&
+ (lastthrow - firstdtor) * step > 0; // last throw after first destruction
+ const ptrdiff_t lastPrefix = callerDestroysArgs
+ ? lastdtor // up to last argument requiring destruction
+ : lastthrow; // up to last potentially throwing argument
+
+ /* Problem 3: initialize 'eprefix' by declaring the gate
+ */
+ VarDeclaration gate;
+ if (needsPrefix && !callerDestroysArgs)
+ {
+ // eprefix => bool __gate [= false]
+ Identifier idtmp = Identifier.generateId("__gate");
+ gate = new VarDeclaration(loc, Type.tbool, idtmp, null);
+ gate.storage_class |= STC.temp | STC.ctfe | STC.volatile_;
+ gate.dsymbolSemantic(sc);
+
+ auto ae = new DeclarationExp(loc, gate);
+ eprefix = ae.expressionSemantic(sc);
+ }
+
+ for (ptrdiff_t i = start; i != end; i += step)
+ {
+ Expression arg = (*arguments)[i];
+ //printf("arg[%d]: %s\n", cast(int)i, arg.toChars());
+
+ Parameter parameter = (i >= nparams ? null : tf.parameterList[i]);
+ const bool isRef = parameter && parameter.isReference();
+ const bool isLazy = (parameter && (parameter.storageClass & STC.lazy_));
+
+ /* Skip lazy parameters
+ */
+ if (isLazy)
+ continue;
+
+ /* Do we have 'eprefix' and aren't past 'lastPrefix' yet?
+ * Then declare a temporary variable for this arg and append that declaration
+ * to 'eprefix', which will implicitly take care of potential problem 2) for
+ * this arg.
+ * 'eprefix' will therefore finally contain all args up to and including 'lastPrefix',
+ * excluding all lazy parameters.
+ */
+ if (needsPrefix && (lastPrefix - i) * step >= 0)
+ {
+ const bool needsDtor = !isRef && arg.type.needsDestruction() &&
+ // Problem 3: last throwing arg doesn't require dtor patching
+ (callerDestroysArgs || i != lastPrefix);
+
+ /* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor)
+ */
+ auto tmp = copyToTemp(
+ (parameter ? parameter.storageClass : tf.parameterList.stc) & (STC.scope_),
+ needsDtor ? "__pfx" : "__pfy",
+ !isRef ? arg : arg.addressOf());
+ tmp.dsymbolSemantic(sc);
+
+ if (callerDestroysArgs)
+ {
+ /* Problem 4: Normal temporary, destructed after the call
+ */
+ if (needsDtor)
+ tmp.isArgDtorVar = true; // mark it so that the backend passes it by ref to the function being called
+ }
+ else
+ {
+ /* Problem 3: Modify the destructor so it only runs if gate==false,
+ * i.e., only if there was a throw while constructing the args
+ */
+ if (!needsDtor)
+ {
+ if (tmp.edtor)
+ {
+ assert(i == lastPrefix);
+ tmp.edtor = null;
+ }
+ }
+ else
+ {
+ // edtor => (__gate || edtor)
+ assert(tmp.edtor);
+ Expression e = tmp.edtor;
+ e = new LogicalExp(e.loc, TOK.orOr, new VarExp(e.loc, gate), e);
+ tmp.edtor = e.expressionSemantic(sc);
+ //printf("edtor: %s\n", tmp.edtor.toChars());
+ }
+ }
+
+ // eprefix => (eprefix, auto __pfx/y = arg)
+ auto ae = new DeclarationExp(loc, tmp);
+ eprefix = Expression.combine(eprefix, ae.expressionSemantic(sc));
+
+ // arg => __pfx/y
+ arg = new VarExp(loc, tmp);
+ arg = arg.expressionSemantic(sc);
+ if (isRef)
+ {
+ arg = new PtrExp(loc, arg);
+ arg = arg.expressionSemantic(sc);
+ }
+
+ /* Problem 3: Last throwing arg?
+ * Then finalize eprefix => (eprefix, gate = true), i.e., disable the
+ * dtors right after constructing the last throwing arg.
+ * From now on, the callee will take care of destructing the args because
+ * the args are implicitly moved into function parameters.
+ */
+ if (!callerDestroysArgs && i == lastPrefix)
+ {
+ auto e = new AssignExp(gate.loc, new VarExp(gate.loc, gate), IntegerExp.createBool(true));
+ eprefix = Expression.combine(eprefix, e.expressionSemantic(sc));
+ }
+ }
+ else // not part of 'eprefix'
+ {
+ /* Handle problem 2) by calling the copy constructor for value structs
+ * (or static arrays of them) if appropriate.
+ */
+ Type tv = arg.type.baseElemOf();
+ if (!isRef && tv.ty == Tstruct)
+ arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null);
+ }
+
+ (*arguments)[i] = arg;
+ }
+ }
+ //if (eprefix) printf("eprefix: %s\n", eprefix.toChars());
+
+ /* Test compliance with DIP1021
+ */
+ if (global.params.useDIP1021 &&
+ tf.trust != TRUST.system && tf.trust != TRUST.trusted)
+ err |= checkMutableArguments(sc, fd, tf, ethis, arguments, false);
+
+ // If D linkage and variadic, add _arguments[] as first argument
+ if (tf.isDstyleVariadic())
+ {
+ assert(arguments.dim >= nparams);
+
+ auto args = new Parameters(arguments.dim - nparams);
+ for (size_t i = 0; i < arguments.dim - nparams; i++)
+ {
+ auto arg = new Parameter(STC.in_, (*arguments)[nparams + i].type, null, null, null);
+ (*args)[i] = arg;
+ }
+ auto tup = new TypeTuple(args);
+ Expression e = (new TypeidExp(loc, tup)).expressionSemantic(sc);
+ arguments.insert(0, e);
+ }
+
+ /* Determine function return type: tret
+ */
+ Type tret = tf.next;
+ if (isCtorCall)
+ {
+ //printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd.toChars(), fd.type.toChars(),
+ // wildmatch, tf.isWild(), fd.isReturnIsolated());
+ if (!tthis)
+ {
+ assert(sc.intypeof || global.errors);
+ tthis = fd.isThis().type.addMod(fd.type.mod);
+ }
+ if (tf.isWild() && !fd.isReturnIsolated())
+ {
+ if (wildmatch)
+ tret = tret.substWildTo(wildmatch);
+ int offset;
+ if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0))
+ {
+ const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars();
+ const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars();
+ .error(loc, "`inout` constructor `%s` creates%s object, not%s", fd.toPrettyChars(), s1, s2);
+ err = true;
+ }
+ }
+ tret = tthis;
+ }
+ else if (wildmatch && tret)
+ {
+ /* Adjust function return type based on wildmatch
+ */
+ //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret.toChars());
+ tret = tret.substWildTo(wildmatch);
+ }
+
+ *prettype = tret;
+ *peprefix = eprefix;
+ return (err || olderrors != global.errors);
+}
+
+/**
+ * Determines whether a symbol represents a module or package
+ * (Used as a helper for is(type == module) and is(type == package))
+ *
+ * Params:
+ * sym = the symbol to be checked
+ *
+ * Returns:
+ * the symbol which `sym` represents (or `null` if it doesn't represent a `Package`)
+ */
+Package resolveIsPackage(Dsymbol sym)
+{
+ Package pkg;
+ if (Import imp = sym.isImport())
+ {
+ if (imp.pkg is null)
+ {
+ .error(sym.loc, "Internal Compiler Error: unable to process forward-referenced import `%s`",
+ imp.toChars());
+ assert(0);
+ }
+ pkg = imp.pkg;
+ }
+ else if (auto mod = sym.isModule())
+ pkg = mod.isPackageFile ? mod.pkg : sym.isPackage();
+ else
+ pkg = sym.isPackage();
+ if (pkg)
+ pkg.resolvePKGunknown();
+ return pkg;
+}
+
+private Module loadStdMath()
+{
+ __gshared Import impStdMath = null;
+ __gshared Identifier[1] stdID;
+ if (!impStdMath)
+ {
+ stdID[0] = Id.std;
+ auto s = new Import(Loc.initial, stdID[], Id.math, null, false);
+ // Module.load will call fatal() if there's no std.math available.
+ // Gag the error here, pushing the error handling to the caller.
+ uint errors = global.startGagging();
+ s.load(null);
+ if (s.mod)
+ {
+ s.mod.importAll(null);
+ s.mod.dsymbolSemantic(null);
+ }
+ global.endGagging(errors);
+ impStdMath = s;
+ }
+ return impStdMath.mod;
+}
+
+private extern (C++) final class ExpressionSemanticVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+
+ Scope* sc;
+ Expression result;
+
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ private void setError()
+ {
+ result = ErrorExp.get();
+ }
+
+ /**************************
+ * Semantically analyze Expression.
+ * Determine types, fold constants, etc.
+ */
+ override void visit(Expression e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("Expression::semantic() %s\n", e.toChars());
+ }
+ if (e.type)
+ e.type = e.type.typeSemantic(e.loc, sc);
+ else
+ e.type = Type.tvoid;
+ result = e;
+ }
+
+ override void visit(IntegerExp e)
+ {
+ assert(e.type);
+ if (e.type.ty == Terror)
+ return setError();
+
+ assert(e.type.deco);
+ e.setInteger(e.getInteger());
+ result = e;
+ }
+
+ override void visit(RealExp e)
+ {
+ if (!e.type)
+ e.type = Type.tfloat64;
+ else
+ e.type = e.type.typeSemantic(e.loc, sc);
+ result = e;
+ }
+
+ override void visit(ComplexExp e)
+ {
+ if (!e.type)
+ e.type = Type.tcomplex80;
+ else
+ e.type = e.type.typeSemantic(e.loc, sc);
+ result = e;
+ }
+
+ override void visit(IdentifierExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars());
+ }
+ if (exp.type) // This is used as the dummy expression
+ {
+ result = exp;
+ return;
+ }
+
+ Dsymbol scopesym;
+ Dsymbol s = sc.search(exp.loc, exp.ident, &scopesym);
+ if (s)
+ {
+ if (s.errors)
+ return setError();
+
+ Expression e;
+
+ /* See if the symbol was a member of an enclosing 'with'
+ */
+ WithScopeSymbol withsym = scopesym.isWithScopeSymbol();
+ if (withsym && withsym.withstate.wthis && symbolIsVisible(sc, s))
+ {
+ /* Disallow shadowing
+ */
+ // First find the scope of the with
+ Scope* scwith = sc;
+ while (scwith.scopesym != scopesym)
+ {
+ scwith = scwith.enclosing;
+ assert(scwith);
+ }
+ // Look at enclosing scopes for symbols with the same name,
+ // in the same function
+ for (Scope* scx = scwith; scx && scx.func == scwith.func; scx = scx.enclosing)
+ {
+ Dsymbol s2;
+ if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2)
+ {
+ exp.error("with symbol `%s` is shadowing local symbol `%s`", s.toPrettyChars(), s2.toPrettyChars());
+ return setError();
+ }
+ }
+ s = s.toAlias();
+
+ // Same as wthis.ident
+ // TODO: DotIdExp.semantic will find 'ident' from 'wthis' again.
+ // The redudancy should be removed.
+ e = new VarExp(exp.loc, withsym.withstate.wthis);
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ e = e.expressionSemantic(sc);
+ }
+ else
+ {
+ if (withsym)
+ {
+ if (withsym.withstate.exp.type.ty != Tvoid)
+ {
+ // 'with (exp)' is a type expression
+ // or 's' is not visible there (for error message)
+ e = new TypeExp(exp.loc, withsym.withstate.exp.type);
+ }
+ else
+ {
+ // 'with (exp)' is a Package/Module
+ e = withsym.withstate.exp;
+ }
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+
+ /* If f is really a function template,
+ * then replace f with the function template declaration.
+ */
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ TemplateDeclaration td = getFuncTemplateDecl(f);
+ if (td)
+ {
+ if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+ td = td.overroot; // then get the start
+ e = new TemplateExp(exp.loc, td, f);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+
+ if (global.params.fixAliasThis)
+ {
+ ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol();
+ if (expDsym)
+ {
+ //printf("expDsym = %s\n", expDsym.exp.toChars());
+ result = expDsym.exp.expressionSemantic(sc);
+ return;
+ }
+ }
+ // Haven't done overload resolution yet, so pass 1
+ e = symbolToExp(s, exp.loc, sc, true);
+ }
+ result = e;
+ return;
+ }
+
+ if (!global.params.fixAliasThis && hasThis(sc))
+ {
+ for (AggregateDeclaration ad = sc.getStructClassScope(); ad;)
+ {
+ if (ad.aliasthis)
+ {
+ Expression e;
+ e = new ThisExp(exp.loc);
+ e = new DotIdExp(exp.loc, e, ad.aliasthis.ident);
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ e = e.trySemantic(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+
+ auto cd = ad.isClassDeclaration();
+ if (cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
+ {
+ ad = cd.baseClass;
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (exp.ident == Id.ctfe)
+ {
+ if (sc.flags & SCOPE.ctfe)
+ {
+ exp.error("variable `__ctfe` cannot be read at compile time");
+ return setError();
+ }
+
+ // Create the magic __ctfe bool variable
+ auto vd = new VarDeclaration(exp.loc, Type.tbool, Id.ctfe, null);
+ vd.storage_class |= STC.temp;
+ vd.semanticRun = PASS.semanticdone;
+ Expression e = new VarExp(exp.loc, vd);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ // If we've reached this point and are inside a with() scope then we may
+ // try one last attempt by checking whether the 'wthis' object supports
+ // dynamic dispatching via opDispatch.
+ // This is done by rewriting this expression as wthis.ident.
+ // The innermost with() scope of the hierarchy to satisfy the condition
+ // above wins.
+ // https://issues.dlang.org/show_bug.cgi?id=6400
+ for (Scope* sc2 = sc; sc2; sc2 = sc2.enclosing)
+ {
+ if (!sc2.scopesym)
+ continue;
+
+ if (auto ss = sc2.scopesym.isWithScopeSymbol())
+ {
+ if (ss.withstate.wthis)
+ {
+ Expression e;
+ e = new VarExp(exp.loc, ss.withstate.wthis);
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ e = e.trySemantic(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+ // Try Type.opDispatch (so the static version)
+ else if (ss.withstate.exp && ss.withstate.exp.op == TOK.type)
+ {
+ if (Type t = ss.withstate.exp.isTypeExp().type)
+ {
+ Expression e;
+ e = new TypeExp(exp.loc, t);
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ e = e.trySemantic(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ /* Look for what user might have meant
+ */
+ if (const n = importHint(exp.ident.toString()))
+ exp.error("`%s` is not defined, perhaps `import %.*s;` is needed?", exp.ident.toChars(), cast(int)n.length, n.ptr);
+ else if (auto s2 = sc.search_correct(exp.ident))
+ exp.error("undefined identifier `%s`, did you mean %s `%s`?", exp.ident.toChars(), s2.kind(), s2.toChars());
+ else if (const p = Scope.search_correct_C(exp.ident))
+ exp.error("undefined identifier `%s`, did you mean `%s`?", exp.ident.toChars(), p);
+ else
+ exp.error("undefined identifier `%s`", exp.ident.toChars());
+
+ result = ErrorExp.get();
+ }
+
+ override void visit(DsymbolExp e)
+ {
+ result = symbolToExp(e.s, e.loc, sc, e.hasOverloads);
+ }
+
+ override void visit(ThisExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ThisExp::semantic()\n");
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
+ AggregateDeclaration ad;
+
+ /* Special case for typeof(this) and typeof(super) since both
+ * should work even if they are not inside a non-static member function
+ */
+ if (!fd && sc.intypeof == 1)
+ {
+ // Find enclosing struct or class
+ for (Dsymbol s = sc.getStructClassScope(); 1; s = s.parent)
+ {
+ if (!s)
+ {
+ e.error("`%s` is not in a class or struct scope", e.toChars());
+ goto Lerr;
+ }
+ ClassDeclaration cd = s.isClassDeclaration();
+ if (cd)
+ {
+ e.type = cd.type;
+ result = e;
+ return;
+ }
+ StructDeclaration sd = s.isStructDeclaration();
+ if (sd)
+ {
+ e.type = sd.type;
+ result = e;
+ return;
+ }
+ }
+ }
+ if (!fd)
+ goto Lerr;
+
+ assert(fd.vthis);
+ e.var = fd.vthis;
+ assert(e.var.parent);
+ ad = fd.isMemberLocal();
+ if (!ad)
+ ad = fd.isMember2();
+ assert(ad);
+ e.type = ad.type.addMod(e.var.type.mod);
+
+ if (e.var.checkNestedReference(sc, e.loc))
+ return setError();
+
+ result = e;
+ return;
+
+ Lerr:
+ e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars());
+ result = ErrorExp.get();
+ }
+
+ override void visit(SuperExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("SuperExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ FuncDeclaration fd = hasThis(sc);
+ ClassDeclaration cd;
+ Dsymbol s;
+
+ /* Special case for typeof(this) and typeof(super) since both
+ * should work even if they are not inside a non-static member function
+ */
+ if (!fd && sc.intypeof == 1)
+ {
+ // Find enclosing class
+ for (s = sc.getStructClassScope(); 1; s = s.parent)
+ {
+ if (!s)
+ {
+ e.error("`%s` is not in a class scope", e.toChars());
+ goto Lerr;
+ }
+ cd = s.isClassDeclaration();
+ if (cd)
+ {
+ cd = cd.baseClass;
+ if (!cd)
+ {
+ e.error("class `%s` has no `super`", s.toChars());
+ goto Lerr;
+ }
+ e.type = cd.type;
+ result = e;
+ return;
+ }
+ }
+ }
+ if (!fd)
+ goto Lerr;
+
+ e.var = fd.vthis;
+ assert(e.var && e.var.parent);
+
+ s = fd.toParentDecl();
+ if (s.isTemplateDeclaration()) // allow inside template constraint
+ s = s.toParent();
+ assert(s);
+ cd = s.isClassDeclaration();
+ //printf("parent is %s %s\n", fd.toParent().kind(), fd.toParent().toChars());
+ if (!cd)
+ goto Lerr;
+ if (!cd.baseClass)
+ {
+ e.error("no base class for `%s`", cd.toChars());
+ e.type = cd.type.addMod(e.var.type.mod);
+ }
+ else
+ {
+ e.type = cd.baseClass.type;
+ e.type = e.type.castMod(e.var.type.mod);
+ }
+
+ if (e.var.checkNestedReference(sc, e.loc))
+ return setError();
+
+ result = e;
+ return;
+
+ Lerr:
+ e.error("`super` is only allowed in non-static class member functions");
+ result = ErrorExp.get();
+ }
+
+ override void visit(NullExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("NullExp::semantic('%s')\n", e.toChars());
+ }
+ // NULL is the same as (void *)0
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+ e.type = Type.tnull;
+ result = e;
+ }
+
+ override void visit(StringExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("StringExp::semantic() %s\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ OutBuffer buffer;
+ size_t newlen = 0;
+ size_t u;
+ dchar c;
+
+ switch (e.postfix)
+ {
+ case 'd':
+ for (u = 0; u < e.len;)
+ {
+ if (const p = utf_decodeChar(e.peekString(), u, c))
+ {
+ e.error("%.*s", cast(int)p.length, p.ptr);
+ return setError();
+ }
+ else
+ {
+ buffer.write4(c);
+ newlen++;
+ }
+ }
+ buffer.write4(0);
+ e.setData(buffer.extractData(), newlen, 4);
+ if (sc && sc.flags & SCOPE.Cfile)
+ e.type = Type.tuns32.pointerTo();
+ else
+ e.type = Type.tdchar.immutableOf().arrayOf();
+ e.committed = 1;
+ break;
+
+ case 'w':
+ for (u = 0; u < e.len;)
+ {
+ if (const p = utf_decodeChar(e.peekString(), u, c))
+ {
+ e.error("%.*s", cast(int)p.length, p.ptr);
+ return setError();
+ }
+ else
+ {
+ buffer.writeUTF16(c);
+ newlen++;
+ if (c >= 0x10000)
+ newlen++;
+ }
+ }
+ buffer.writeUTF16(0);
+ e.setData(buffer.extractData(), newlen, 2);
+ if (sc && sc.flags & SCOPE.Cfile)
+ e.type = Type.tuns16.pointerTo();
+ else
+ e.type = Type.twchar.immutableOf().arrayOf();
+ e.committed = 1;
+ break;
+
+ case 'c':
+ e.committed = 1;
+ goto default;
+
+ default:
+ if (sc && sc.flags & SCOPE.Cfile)
+ e.type = Type.tchar.pointerTo();
+ else
+ e.type = Type.tchar.immutableOf().arrayOf();
+ break;
+ }
+ e.type = e.type.typeSemantic(e.loc, sc);
+ //type = type.immutableOf();
+ //printf("type = %s\n", type.toChars());
+
+ result = e;
+ }
+
+ override void visit(TupleExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("+TupleExp::semantic(%s)\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (exp.e0)
+ exp.e0 = exp.e0.expressionSemantic(sc);
+
+ // Run semantic() on each argument
+ bool err = false;
+ for (size_t i = 0; i < exp.exps.dim; i++)
+ {
+ Expression e = (*exp.exps)[i];
+ e = e.expressionSemantic(sc);
+ if (!e.type)
+ {
+ exp.error("`%s` has no value", e.toChars());
+ err = true;
+ }
+ else if (e.op == TOK.error)
+ err = true;
+ else
+ (*exp.exps)[i] = e;
+ }
+ if (err)
+ return setError();
+
+ expandTuples(exp.exps);
+
+ exp.type = new TypeTuple(exp.exps);
+ exp.type = exp.type.typeSemantic(exp.loc, sc);
+ //printf("-TupleExp::semantic(%s)\n", toChars());
+ result = exp;
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ArrayLiteralExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ /* Perhaps an empty array literal [ ] should be rewritten as null?
+ */
+
+ if (e.basis)
+ e.basis = e.basis.expressionSemantic(sc);
+ if (arrayExpressionSemantic(e.elements, sc) || (e.basis && e.basis.op == TOK.error))
+ return setError();
+
+ expandTuples(e.elements);
+
+ if (e.basis)
+ e.elements.push(e.basis);
+ Type t0 = arrayExpressionToCommonType(sc, *e.elements);
+ if (e.basis)
+ e.basis = e.elements.pop();
+ if (t0 is null)
+ return setError();
+
+ e.type = t0.arrayOf();
+ e.type = e.type.typeSemantic(e.loc, sc);
+
+ /* Disallow array literals of type void being used.
+ */
+ if (e.elements.dim > 0 && t0.ty == Tvoid)
+ {
+ e.error("`%s` of type `%s` has no value", e.toChars(), e.type.toChars());
+ return setError();
+ }
+
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ semanticTypeInfo(sc, e.type);
+
+ result = e;
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("AssocArrayLiteralExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ // Run semantic() on each element
+ bool err_keys = arrayExpressionSemantic(e.keys, sc);
+ bool err_vals = arrayExpressionSemantic(e.values, sc);
+ if (err_keys || err_vals)
+ return setError();
+
+ expandTuples(e.keys);
+ expandTuples(e.values);
+ if (e.keys.dim != e.values.dim)
+ {
+ e.error("number of keys is %llu, must match number of values %llu",
+ cast(ulong) e.keys.dim, cast(ulong) e.values.dim);
+ return setError();
+ }
+
+ Type tkey = arrayExpressionToCommonType(sc, *e.keys);
+ Type tvalue = arrayExpressionToCommonType(sc, *e.values);
+ if (tkey is null || tvalue is null)
+ return setError();
+
+ e.type = new TypeAArray(tvalue, tkey);
+ e.type = e.type.typeSemantic(e.loc, sc);
+
+ semanticTypeInfo(sc, e.type);
+
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (checkAssocArrayLiteralEscape(sc, e, false))
+ return setError();
+ }
+
+ result = e;
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("StructLiteralExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ e.sd.size(e.loc);
+ if (e.sd.sizeok != Sizeok.done)
+ return setError();
+
+ // run semantic() on each element
+ if (arrayExpressionSemantic(e.elements, sc))
+ return setError();
+
+ expandTuples(e.elements);
+
+ /* Fit elements[] to the corresponding type of field[].
+ */
+ if (!e.sd.fit(e.loc, sc, e.elements, e.stype))
+ return setError();
+
+ /* Fill out remainder of elements[] with default initializers for fields[]
+ */
+ if (!e.sd.fill(e.loc, e.elements, false))
+ {
+ /* An error in the initializer needs to be recorded as an error
+ * in the enclosing function or template, since the initializer
+ * will be part of the stuct declaration.
+ */
+ global.increaseErrorCount();
+ return setError();
+ }
+
+ if (checkFrameAccess(e.loc, sc, e.sd, e.elements.dim))
+ return setError();
+
+ e.type = e.stype ? e.stype : e.sd.type;
+ result = e;
+ }
+
+ override void visit(CompoundLiteralExp cle)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CompoundLiteralExp::semantic('%s')\n", cle.toChars());
+ }
+ Type t = cle.type.typeSemantic(cle.loc, sc);
+ auto init = initializerSemantic(cle.initializer, sc, t, INITnointerpret);
+ auto e = initializerToExpression(init, t);
+ if (!e)
+ {
+ error(cle.loc, "cannot convert initializer `%s` to expression", init.toChars());
+ return setError();
+ }
+ result = e;
+ return;
+ }
+
+ override void visit(TypeExp exp)
+ {
+ if (exp.type.ty == Terror)
+ return setError();
+
+ //printf("TypeExp::semantic(%s)\n", exp.type.toChars());
+ Expression e;
+ Type t;
+ Dsymbol s;
+
+ dmd.typesem.resolve(exp.type, exp.loc, sc, e, t, s, true);
+ if (e)
+ {
+ // `(Type)` is actually `(var)` so if `(var)` is a member requiring `this`
+ // then rewrite as `(this.var)` in case it would be followed by a DotVar
+ // to fix https://issues.dlang.org/show_bug.cgi?id=9490
+ VarExp ve = e.isVarExp();
+ if (ve && ve.var && exp.parens && !ve.var.isStatic() && !(sc.stc & STC.static_) &&
+ sc.func && sc.func.needThis && ve.var.toParent2().isAggregateDeclaration())
+ {
+ // printf("apply fix for issue 9490: add `this.` to `%s`...\n", e.toChars());
+ e = new DotVarExp(exp.loc, new ThisExp(exp.loc), ve.var, false);
+ }
+ //printf("e = %s %s\n", Token::toChars(e.op), e.toChars());
+ e = e.expressionSemantic(sc);
+ }
+ else if (t)
+ {
+ //printf("t = %d %s\n", t.ty, t.toChars());
+ exp.type = t.typeSemantic(exp.loc, sc);
+ e = exp;
+ }
+ else if (s)
+ {
+ //printf("s = %s %s\n", s.kind(), s.toChars());
+ e = symbolToExp(s, exp.loc, sc, true);
+ }
+ else
+ assert(0);
+
+ if (global.params.vcomplex)
+ exp.type.checkComplexTransition(exp.loc, sc);
+
+ result = e;
+ }
+
+ override void visit(ScopeExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("+ScopeExp::semantic(%p '%s')\n", exp, exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ ScopeDsymbol sds2 = exp.sds;
+ TemplateInstance ti = sds2.isTemplateInstance();
+ while (ti)
+ {
+ WithScopeSymbol withsym;
+ if (!ti.findTempDecl(sc, &withsym) || !ti.semanticTiargs(sc))
+ return setError();
+ if (withsym && withsym.withstate.wthis)
+ {
+ Expression e = new VarExp(exp.loc, withsym.withstate.wthis);
+ e = new DotTemplateInstanceExp(exp.loc, e, ti);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (ti.needsTypeInference(sc))
+ {
+ if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
+ {
+ Dsymbol p = td.toParentLocal();
+ FuncDeclaration fdthis = hasThis(sc);
+ AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null;
+ if (fdthis && ad && fdthis.isMemberLocal() == ad && (td._scope.stc & STC.static_) == 0)
+ {
+ Expression e = new DotTemplateInstanceExp(exp.loc, new ThisExp(exp.loc), ti);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ }
+ else if (OverloadSet os = ti.tempdecl.isOverloadSet())
+ {
+ FuncDeclaration fdthis = hasThis(sc);
+ AggregateDeclaration ad = os.parent.isAggregateDeclaration();
+ if (fdthis && ad && fdthis.isMemberLocal() == ad)
+ {
+ Expression e = new DotTemplateInstanceExp(exp.loc, new ThisExp(exp.loc), ti);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ }
+ // ti is an instance which requires IFTI.
+ exp.sds = ti;
+ exp.type = Type.tvoid;
+ result = exp;
+ return;
+ }
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors)
+ return setError();
+
+ Dsymbol s = ti.toAlias();
+ if (s == ti)
+ {
+ exp.sds = ti;
+ exp.type = Type.tvoid;
+ result = exp;
+ return;
+ }
+ sds2 = s.isScopeDsymbol();
+ if (sds2)
+ {
+ ti = sds2.isTemplateInstance();
+ //printf("+ sds2 = %s, '%s'\n", sds2.kind(), sds2.toChars());
+ continue;
+ }
+
+ if (auto v = s.isVarDeclaration())
+ {
+ if (!v.type)
+ {
+ exp.error("forward reference of %s `%s`", v.kind(), v.toChars());
+ return setError();
+ }
+ if ((v.storage_class & STC.manifest) && v._init)
+ {
+ /* When an instance that will be converted to a constant exists,
+ * the instance representation "foo!tiargs" is treated like a
+ * variable name, and its recursive appearance check (note that
+ * it's equivalent with a recursive instantiation of foo) is done
+ * separately from the circular initialization check for the
+ * eponymous enum variable declaration.
+ *
+ * template foo(T) {
+ * enum bool foo = foo; // recursive definition check (v.inuse)
+ * }
+ * template bar(T) {
+ * enum bool bar = bar!T; // recursive instantiation check (ti.inuse)
+ * }
+ */
+ if (ti.inuse)
+ {
+ exp.error("recursive expansion of %s `%s`", ti.kind(), ti.toPrettyChars());
+ return setError();
+ }
+ v.checkDeprecated(exp.loc, sc);
+ auto e = v.expandInitializer(exp.loc);
+ ti.inuse++;
+ e = e.expressionSemantic(sc);
+ ti.inuse--;
+ result = e;
+ return;
+ }
+ }
+
+ //printf("s = %s, '%s'\n", s.kind(), s.toChars());
+ auto e = symbolToExp(s, exp.loc, sc, true);
+ //printf("-1ScopeExp::semantic()\n");
+ result = e;
+ return;
+ }
+
+ //printf("sds2 = %s, '%s'\n", sds2.kind(), sds2.toChars());
+ //printf("\tparent = '%s'\n", sds2.parent.toChars());
+ sds2.dsymbolSemantic(sc);
+
+ // (Aggregate|Enum)Declaration
+ if (auto t = sds2.getType())
+ {
+ result = (new TypeExp(exp.loc, t)).expressionSemantic(sc);
+ return;
+ }
+
+ if (auto td = sds2.isTemplateDeclaration())
+ {
+ result = (new TemplateExp(exp.loc, td)).expressionSemantic(sc);
+ return;
+ }
+
+ exp.sds = sds2;
+ exp.type = Type.tvoid;
+ //printf("-2ScopeExp::semantic() %s\n", toChars());
+ result = exp;
+ }
+
+ override void visit(NewExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("NewExp::semantic() %s\n", exp.toChars());
+ if (exp.thisexp)
+ printf("\tthisexp = %s\n", exp.thisexp.toChars());
+ printf("\tnewtype: %s\n", exp.newtype.toChars());
+ }
+ if (exp.type) // if semantic() already run
+ {
+ result = exp;
+ return;
+ }
+
+ //for error messages if the argument in [] is not convertible to size_t
+ const originalNewtype = exp.newtype;
+
+ // https://issues.dlang.org/show_bug.cgi?id=11581
+ // With the syntax `new T[edim]` or `thisexp.new T[edim]`,
+ // T should be analyzed first and edim should go into arguments iff it's
+ // not a tuple.
+ Expression edim = null;
+ if (!exp.arguments && exp.newtype.ty == Tsarray)
+ {
+ edim = (cast(TypeSArray)exp.newtype).dim;
+ exp.newtype = (cast(TypeNext)exp.newtype).next;
+ }
+
+ ClassDeclaration cdthis = null;
+ if (exp.thisexp)
+ {
+ exp.thisexp = exp.thisexp.expressionSemantic(sc);
+ if (exp.thisexp.op == TOK.error)
+ return setError();
+
+ cdthis = exp.thisexp.type.isClassHandle();
+ if (!cdthis)
+ {
+ exp.error("`this` for nested class must be a class type, not `%s`", exp.thisexp.type.toChars());
+ return setError();
+ }
+
+ sc = sc.push(cdthis);
+ exp.type = exp.newtype.typeSemantic(exp.loc, sc);
+ sc = sc.pop();
+ }
+ else
+ {
+ exp.type = exp.newtype.typeSemantic(exp.loc, sc);
+ }
+ if (exp.type.ty == Terror)
+ return setError();
+
+ if (edim)
+ {
+ if (exp.type.toBasetype().ty == Ttuple)
+ {
+ // --> new T[edim]
+ exp.type = new TypeSArray(exp.type, edim);
+ exp.type = exp.type.typeSemantic(exp.loc, sc);
+ if (exp.type.ty == Terror)
+ return setError();
+ }
+ else
+ {
+ // --> new T[](edim)
+ exp.arguments = new Expressions();
+ exp.arguments.push(edim);
+ exp.type = exp.type.arrayOf();
+ }
+ }
+
+ exp.newtype = exp.type; // in case type gets cast to something else
+ Type tb = exp.type.toBasetype();
+ //printf("tb: %s, deco = %s\n", tb.toChars(), tb.deco);
+ if (arrayExpressionSemantic(exp.newargs, sc) ||
+ preFunctionParameters(sc, exp.newargs))
+ {
+ return setError();
+ }
+ if (arrayExpressionSemantic(exp.arguments, sc))
+ {
+ return setError();
+ }
+ //https://issues.dlang.org/show_bug.cgi?id=20547
+ //exp.arguments are the "parameters" to [], not to a real function
+ //so the errors that come from preFunctionParameters are misleading
+ if (originalNewtype.ty == Tsarray)
+ {
+ if (preFunctionParameters(sc, exp.arguments, false))
+ {
+ exp.error("cannot create a `%s` with `new`", originalNewtype.toChars());
+ return setError();
+ }
+ }
+ else if (preFunctionParameters(sc, exp.arguments))
+ {
+ return setError();
+ }
+
+ if (exp.thisexp && tb.ty != Tclass)
+ {
+ exp.error("`.new` is only for allocating nested classes, not `%s`", tb.toChars());
+ return setError();
+ }
+
+ const size_t nargs = exp.arguments ? exp.arguments.dim : 0;
+ Expression newprefix = null;
+
+ if (tb.ty == Tclass)
+ {
+ auto cd = (cast(TypeClass)tb).sym;
+ cd.size(exp.loc);
+ if (cd.sizeok != Sizeok.done)
+ return setError();
+ if (!cd.ctor)
+ cd.ctor = cd.searchCtor();
+ if (cd.noDefaultCtor && !nargs && !cd.defaultCtor)
+ {
+ exp.error("default construction is disabled for type `%s`", cd.type.toChars());
+ return setError();
+ }
+
+ if (cd.isInterfaceDeclaration())
+ {
+ exp.error("cannot create instance of interface `%s`", cd.toChars());
+ return setError();
+ }
+
+ if (cd.isAbstract())
+ {
+ exp.error("cannot create instance of abstract class `%s`", cd.toChars());
+ for (size_t i = 0; i < cd.vtbl.dim; i++)
+ {
+ FuncDeclaration fd = cd.vtbl[i].isFuncDeclaration();
+ if (fd && fd.isAbstract())
+ {
+ errorSupplemental(exp.loc, "function `%s` is not implemented",
+ fd.toFullSignature());
+ }
+ }
+ return setError();
+ }
+ // checkDeprecated() is already done in newtype.typeSemantic().
+
+ if (cd.isNested())
+ {
+ /* We need a 'this' pointer for the nested class.
+ * Ensure we have the right one.
+ */
+ Dsymbol s = cd.toParentLocal();
+
+ //printf("cd isNested, parent = %s '%s'\n", s.kind(), s.toPrettyChars());
+ if (auto cdn = s.isClassDeclaration())
+ {
+ if (!cdthis)
+ {
+ // Supply an implicit 'this' and try again
+ exp.thisexp = new ThisExp(exp.loc);
+ for (Dsymbol sp = sc.parent; 1; sp = sp.toParentLocal())
+ {
+ if (!sp)
+ {
+ exp.error("outer class `%s` `this` needed to `new` nested class `%s`",
+ cdn.toChars(), cd.toChars());
+ return setError();
+ }
+ ClassDeclaration cdp = sp.isClassDeclaration();
+ if (!cdp)
+ continue;
+ if (cdp == cdn || cdn.isBaseOf(cdp, null))
+ break;
+ // Add a '.outer' and try again
+ exp.thisexp = new DotIdExp(exp.loc, exp.thisexp, Id.outer);
+ }
+
+ exp.thisexp = exp.thisexp.expressionSemantic(sc);
+ if (exp.thisexp.op == TOK.error)
+ return setError();
+ cdthis = exp.thisexp.type.isClassHandle();
+ }
+ if (cdthis != cdn && !cdn.isBaseOf(cdthis, null))
+ {
+ //printf("cdthis = %s\n", cdthis.toChars());
+ exp.error("`this` for nested class must be of type `%s`, not `%s`",
+ cdn.toChars(), exp.thisexp.type.toChars());
+ return setError();
+ }
+ if (!MODimplicitConv(exp.thisexp.type.mod, exp.newtype.mod))
+ {
+ exp.error("nested type `%s` should have the same or weaker constancy as enclosing type `%s`",
+ exp.newtype.toChars(), exp.thisexp.type.toChars());
+ return setError();
+ }
+ }
+ else if (exp.thisexp)
+ {
+ exp.error("`.new` is only for allocating nested classes");
+ return setError();
+ }
+ else if (auto fdn = s.isFuncDeclaration())
+ {
+ // make sure the parent context fdn of cd is reachable from sc
+ if (!ensureStaticLinkTo(sc.parent, fdn))
+ {
+ exp.error("outer function context of `%s` is needed to `new` nested class `%s`",
+ fdn.toPrettyChars(), cd.toPrettyChars());
+ return setError();
+ }
+ }
+ else
+ assert(0);
+ }
+ else if (exp.thisexp)
+ {
+ exp.error("`.new` is only for allocating nested classes");
+ return setError();
+ }
+
+ if (cd.vthis2)
+ {
+ if (AggregateDeclaration ad2 = cd.isMember2())
+ {
+ Expression te = new ThisExp(exp.loc).expressionSemantic(sc);
+ if (te.op != TOK.error)
+ te = getRightThis(exp.loc, sc, ad2, te, cd);
+ if (te.op == TOK.error)
+ {
+ exp.error("need `this` of type `%s` needed to `new` nested class `%s`", ad2.toChars(), cd.toChars());
+ return setError();
+ }
+ }
+ }
+
+ if (cd.disableNew)
+ {
+ exp.error("cannot allocate `class %s` with `new` because it is annotated with `@disable new()`",
+ originalNewtype.toChars());
+ return setError();
+ }
+ else
+ {
+ if (exp.newargs && exp.newargs.dim)
+ {
+ exp.error("no allocator for `%s`", cd.toChars());
+ return setError();
+ }
+ }
+
+ if (cd.ctor)
+ {
+ FuncDeclaration f = resolveFuncCall(exp.loc, sc, cd.ctor, null, tb, exp.arguments, FuncResolveFlag.standard);
+ if (!f || f.errors)
+ return setError();
+
+ checkFunctionAttributes(exp, sc, f);
+ checkAccess(cd, exp.loc, sc, f);
+
+ TypeFunction tf = cast(TypeFunction)f.type;
+ if (!exp.arguments)
+ exp.arguments = new Expressions();
+ if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.arguments, f, &exp.type, &exp.argprefix))
+ return setError();
+
+ exp.member = f.isCtorDeclaration();
+ assert(exp.member);
+ }
+ else
+ {
+ if (nargs)
+ {
+ exp.error("no constructor for `%s`", cd.toChars());
+ return setError();
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19941
+ // Run semantic on all field initializers to resolve any forward
+ // references. This is the same as done for structs in sd.fill().
+ for (ClassDeclaration c = cd; c; c = c.baseClass)
+ {
+ foreach (v; c.fields)
+ {
+ if (v.inuse || v._scope is null || v._init is null ||
+ v._init.isVoidInitializer())
+ continue;
+ v.inuse++;
+ v._init = v._init.initializerSemantic(v._scope, v.type, INITinterpret);
+ v.inuse--;
+ }
+ }
+ }
+ }
+ else if (tb.ty == Tstruct)
+ {
+ auto sd = (cast(TypeStruct)tb).sym;
+ sd.size(exp.loc);
+ if (sd.sizeok != Sizeok.done)
+ return setError();
+ if (!sd.ctor)
+ sd.ctor = sd.searchCtor();
+ if (sd.noDefaultCtor && !nargs)
+ {
+ exp.error("default construction is disabled for type `%s`", sd.type.toChars());
+ return setError();
+ }
+ // checkDeprecated() is already done in newtype.typeSemantic().
+
+ if (sd.disableNew)
+ {
+ exp.error("cannot allocate `struct %s` with `new` because it is annotated with `@disable new()`",
+ originalNewtype.toChars());
+ return setError();
+ }
+ else
+ {
+ if (exp.newargs && exp.newargs.dim)
+ {
+ exp.error("no allocator for `%s`", sd.toChars());
+ return setError();
+ }
+ }
+
+ if (sd.ctor && nargs)
+ {
+ FuncDeclaration f = resolveFuncCall(exp.loc, sc, sd.ctor, null, tb, exp.arguments, FuncResolveFlag.standard);
+ if (!f || f.errors)
+ return setError();
+
+ checkFunctionAttributes(exp, sc, f);
+ checkAccess(sd, exp.loc, sc, f);
+
+ TypeFunction tf = cast(TypeFunction)f.type;
+ if (!exp.arguments)
+ exp.arguments = new Expressions();
+ if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.arguments, f, &exp.type, &exp.argprefix))
+ return setError();
+
+ exp.member = f.isCtorDeclaration();
+ assert(exp.member);
+
+ if (checkFrameAccess(exp.loc, sc, sd, sd.fields.dim))
+ return setError();
+ }
+ else
+ {
+ if (!exp.arguments)
+ exp.arguments = new Expressions();
+
+ if (!sd.fit(exp.loc, sc, exp.arguments, tb))
+ return setError();
+
+ if (!sd.fill(exp.loc, exp.arguments, false))
+ return setError();
+
+ if (checkFrameAccess(exp.loc, sc, sd, exp.arguments ? exp.arguments.dim : 0))
+ return setError();
+
+ /* Since a `new` allocation may escape, check each of the arguments for escaping
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ foreach (arg; *exp.arguments)
+ {
+ if (arg && checkNewEscape(sc, arg, false))
+ return setError();
+ }
+ }
+ }
+
+ exp.type = exp.type.pointerTo();
+ }
+ else if (tb.ty == Tarray)
+ {
+ if (!nargs)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20422
+ // Without this check the compiler would give a misleading error
+ exp.error("missing length argument for array");
+ return setError();
+ }
+
+ Type tn = tb.nextOf().baseElemOf();
+ Dsymbol s = tn.toDsymbol(sc);
+ AggregateDeclaration ad = s ? s.isAggregateDeclaration() : null;
+ if (ad && ad.noDefaultCtor)
+ {
+ exp.error("default construction is disabled for type `%s`", tb.nextOf().toChars());
+ return setError();
+ }
+ for (size_t i = 0; i < nargs; i++)
+ {
+ if (tb.ty != Tarray)
+ {
+ exp.error("too many arguments for array");
+ return setError();
+ }
+
+ Expression arg = (*exp.arguments)[i];
+ arg = resolveProperties(sc, arg);
+ arg = arg.implicitCastTo(sc, Type.tsize_t);
+ if (arg.op == TOK.error)
+ return setError();
+ arg = arg.optimize(WANTvalue);
+ if (arg.op == TOK.int64 && cast(sinteger_t)arg.toInteger() < 0)
+ {
+ exp.error("negative array index `%s`", arg.toChars());
+ return setError();
+ }
+ (*exp.arguments)[i] = arg;
+ tb = (cast(TypeDArray)tb).next.toBasetype();
+ }
+ }
+ else if (tb.isscalar())
+ {
+ if (!nargs)
+ {
+ }
+ else if (nargs == 1)
+ {
+ Expression e = (*exp.arguments)[0];
+ e = e.implicitCastTo(sc, tb);
+ (*exp.arguments)[0] = e;
+ }
+ else
+ {
+ exp.error("more than one argument for construction of `%s`", exp.type.toChars());
+ return setError();
+ }
+
+ exp.type = exp.type.pointerTo();
+ }
+ else
+ {
+ exp.error("cannot create a `%s` with `new`", exp.type.toChars());
+ return setError();
+ }
+
+ //printf("NewExp: '%s'\n", toChars());
+ //printf("NewExp:type '%s'\n", type.toChars());
+ semanticTypeInfo(sc, exp.type);
+
+ if (newprefix)
+ {
+ result = Expression.combine(newprefix, exp);
+ return;
+ }
+ result = exp;
+ }
+
+ override void visit(NewAnonClassExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("NewAnonClassExp::semantic() %s\n", e.toChars());
+ //printf("thisexp = %p\n", thisexp);
+ //printf("type: %s\n", type.toChars());
+ }
+
+ Expression d = new DeclarationExp(e.loc, e.cd);
+ sc = sc.push(); // just create new scope
+ sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE
+ d = d.expressionSemantic(sc);
+ sc = sc.pop();
+
+ if (!e.cd.errors && sc.intypeof && !sc.parent.inNonRoot())
+ {
+ ScopeDsymbol sds = sc.tinst ? cast(ScopeDsymbol)sc.tinst : sc._module;
+ if (!sds.members)
+ sds.members = new Dsymbols();
+ sds.members.push(e.cd);
+ }
+
+ Expression n = new NewExp(e.loc, e.thisexp, e.newargs, e.cd.type, e.arguments);
+
+ Expression c = new CommaExp(e.loc, d, n);
+ result = c.expressionSemantic(sc);
+ }
+
+ override void visit(SymOffExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("SymOffExp::semantic('%s')\n", e.toChars());
+ }
+ //var.dsymbolSemantic(sc);
+ if (!e.type)
+ e.type = e.var.type.pointerTo();
+
+ if (auto v = e.var.isVarDeclaration())
+ {
+ if (v.checkNestedReference(sc, e.loc))
+ return setError();
+ }
+ else if (auto f = e.var.isFuncDeclaration())
+ {
+ if (f.checkNestedReference(sc, e.loc))
+ return setError();
+ }
+
+ result = e;
+ }
+
+ override void visit(VarExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("VarExp::semantic(%s)\n", e.toChars());
+ }
+
+ auto vd = e.var.isVarDeclaration();
+ auto fd = e.var.isFuncDeclaration();
+
+ if (fd)
+ {
+ //printf("L%d fd = %s\n", __LINE__, f.toChars());
+ if (!fd.functionSemantic())
+ return setError();
+ }
+
+ if (!e.type)
+ e.type = e.var.type;
+ if (e.type && !e.type.deco)
+ {
+ auto decl = e.var.isDeclaration();
+ if (decl)
+ decl.inuse++;
+ e.type = e.type.typeSemantic(e.loc, sc);
+ if (decl)
+ decl.inuse--;
+ }
+
+ /* Fix for 1161 doesn't work because it causes visibility
+ * problems when instantiating imported templates passing private
+ * variables as alias template parameters.
+ */
+ //checkAccess(loc, sc, NULL, var);
+
+ if (vd)
+ {
+ if (vd.checkNestedReference(sc, e.loc))
+ return setError();
+
+ // https://issues.dlang.org/show_bug.cgi?id=12025
+ // If the variable is not actually used in runtime code,
+ // the purity violation error is redundant.
+ //checkPurity(sc, vd);
+ }
+ else if (fd)
+ {
+ // TODO: If fd isn't yet resolved its overload, the checkNestedReference
+ // call would cause incorrect validation.
+ // Maybe here should be moved in CallExp, or AddrExp for functions.
+ if (fd.checkNestedReference(sc, e.loc))
+ return setError();
+ }
+ else if (auto od = e.var.isOverDeclaration())
+ {
+ e.type = Type.tvoid; // ambiguous type?
+ }
+
+ result = e;
+ }
+
+ override void visit(FuncExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("FuncExp::semantic(%s)\n", exp.toChars());
+ if (exp.fd.treq)
+ printf(" treq = %s\n", exp.fd.treq.toChars());
+ }
+
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp;
+ uint olderrors;
+
+ sc = sc.push(); // just create new scope
+ sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE
+ sc.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=12506
+
+ /* fd.treq might be incomplete type,
+ * so should not semantic it.
+ * void foo(T)(T delegate(int) dg){}
+ * foo(a=>a); // in IFTI, treq == T delegate(int)
+ */
+ //if (fd.treq)
+ // fd.treq = fd.treq.dsymbolSemantic(loc, sc);
+
+ exp.genIdent(sc);
+
+ // Set target of return type inference
+ if (exp.fd.treq && !exp.fd.type.nextOf())
+ {
+ TypeFunction tfv = null;
+ if (exp.fd.treq.ty == Tdelegate || exp.fd.treq.isPtrToFunction())
+ tfv = cast(TypeFunction)exp.fd.treq.nextOf();
+ if (tfv)
+ {
+ TypeFunction tfl = cast(TypeFunction)exp.fd.type;
+ tfl.next = tfv.nextOf();
+ }
+ }
+
+ //printf("td = %p, treq = %p\n", td, fd.treq);
+ if (exp.td)
+ {
+ assert(exp.td.parameters && exp.td.parameters.dim);
+ exp.td.dsymbolSemantic(sc);
+ exp.type = Type.tvoid; // temporary type
+
+ if (exp.fd.treq) // defer type determination
+ {
+ FuncExp fe;
+ if (exp.matchType(exp.fd.treq, sc, &fe) > MATCH.nomatch)
+ e = fe;
+ else
+ e = ErrorExp.get();
+ }
+ goto Ldone;
+ }
+
+ olderrors = global.errors;
+ exp.fd.dsymbolSemantic(sc);
+ if (olderrors == global.errors)
+ {
+ exp.fd.semantic2(sc);
+ if (olderrors == global.errors)
+ exp.fd.semantic3(sc);
+ }
+ if (olderrors != global.errors)
+ {
+ if (exp.fd.type && exp.fd.type.ty == Tfunction && !exp.fd.type.nextOf())
+ (cast(TypeFunction)exp.fd.type).next = Type.terror;
+ e = ErrorExp.get();
+ goto Ldone;
+ }
+
+ // Type is a "delegate to" or "pointer to" the function literal
+ if ((exp.fd.isNested() && exp.fd.tok == TOK.delegate_) || (exp.tok == TOK.reserved && exp.fd.treq && exp.fd.treq.ty == Tdelegate))
+ {
+ exp.type = new TypeDelegate(exp.fd.type.isTypeFunction());
+ exp.type = exp.type.typeSemantic(exp.loc, sc);
+
+ exp.fd.tok = TOK.delegate_;
+ }
+ else
+ {
+ exp.type = new TypePointer(exp.fd.type);
+ exp.type = exp.type.typeSemantic(exp.loc, sc);
+ //type = fd.type.pointerTo();
+
+ /* A lambda expression deduced to function pointer might become
+ * to a delegate literal implicitly.
+ *
+ * auto foo(void function() fp) { return 1; }
+ * assert(foo({}) == 1);
+ *
+ * So, should keep fd.tok == TOKreserve if fd.treq == NULL.
+ */
+ if (exp.fd.treq && exp.fd.treq.ty == Tpointer)
+ {
+ // change to non-nested
+ exp.fd.tok = TOK.function_;
+ exp.fd.vthis = null;
+ }
+ }
+ exp.fd.tookAddressOf++;
+
+ Ldone:
+ sc = sc.pop();
+ result = e;
+ }
+
+ /**
+ * Perform semantic analysis on function literals
+ *
+ * Test the following construct:
+ * ---
+ * (x, y, z) { return x + y + z; }(42, 84, 1992);
+ * ---
+ */
+ Expression callExpSemantic(FuncExp exp, Scope* sc, Expressions* arguments)
+ {
+ if ((!exp.type || exp.type == Type.tvoid) && exp.td && arguments && arguments.dim)
+ {
+ for (size_t k = 0; k < arguments.dim; k++)
+ {
+ Expression checkarg = (*arguments)[k];
+ if (checkarg.op == TOK.error)
+ return checkarg;
+ }
+
+ exp.genIdent(sc);
+
+ assert(exp.td.parameters && exp.td.parameters.dim);
+ exp.td.dsymbolSemantic(sc);
+
+ TypeFunction tfl = cast(TypeFunction)exp.fd.type;
+ size_t dim = tfl.parameterList.length;
+ if (arguments.dim < dim)
+ {
+ // Default arguments are always typed, so they don't need inference.
+ Parameter p = tfl.parameterList[arguments.dim];
+ if (p.defaultArg)
+ dim = arguments.dim;
+ }
+
+ if ((tfl.parameterList.varargs == VarArg.none && arguments.dim > dim) ||
+ arguments.dim < dim)
+ {
+ OutBuffer buf;
+ foreach (idx, ref arg; *arguments)
+ buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars());
+ exp.error("function literal `%s%s` is not callable using argument types `(%s)`",
+ exp.fd.toChars(), parametersTypeToChars(tfl.parameterList),
+ buf.peekChars());
+ exp.errorSupplemental("too %s arguments, expected `%d`, got `%d`",
+ arguments.dim < dim ? "few".ptr : "many".ptr,
+ cast(int)dim, cast(int)arguments.dim);
+ return ErrorExp.get();
+ }
+
+ auto tiargs = new Objects();
+ tiargs.reserve(exp.td.parameters.dim);
+
+ for (size_t i = 0; i < exp.td.parameters.dim; i++)
+ {
+ TemplateParameter tp = (*exp.td.parameters)[i];
+ assert(dim <= tfl.parameterList.length);
+ foreach (u, p; tfl.parameterList)
+ {
+ if (u == dim)
+ break;
+
+ if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
+ {
+ Expression e = (*arguments)[u];
+ tiargs.push(e.type);
+ break;
+ }
+ }
+ }
+
+ auto ti = new TemplateInstance(exp.loc, exp.td, tiargs);
+ return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc);
+ }
+ return exp.expressionSemantic(sc);
+ }
+
+ override void visit(CallExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CallExp::semantic() %s\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return; // semantic() already run
+ }
+
+ Objects* tiargs = null; // initial list of template arguments
+ Expression ethis = null;
+ Type tthis = null;
+ Expression e1org = exp.e1;
+
+ if (exp.e1.op == TOK.comma)
+ {
+ /* Rewrite (a,b)(args) as (a,(b(args)))
+ */
+ auto ce = cast(CommaExp)exp.e1;
+ exp.e1 = ce.e2;
+ ce.e2 = exp;
+ result = ce.expressionSemantic(sc);
+ return;
+ }
+ if (exp.e1.op == TOK.delegate_)
+ {
+ DelegateExp de = cast(DelegateExp)exp.e1;
+ exp.e1 = new DotVarExp(de.loc, de.e1, de.func, de.hasOverloads);
+ visit(exp);
+ return;
+ }
+ if (exp.e1.op == TOK.function_)
+ {
+ if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
+ return setError();
+
+ // Run e1 semantic even if arguments have any errors
+ FuncExp fe = cast(FuncExp)exp.e1;
+ exp.e1 = callExpSemantic(fe, sc, exp.arguments);
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ }
+
+ if (Expression ex = resolveUFCS(sc, exp))
+ {
+ result = ex;
+ return;
+ }
+
+ /* This recognizes:
+ * foo!(tiargs)(funcargs)
+ */
+ if (exp.e1.op == TOK.scope_)
+ {
+ ScopeExp se = cast(ScopeExp)exp.e1;
+ TemplateInstance ti = se.sds.isTemplateInstance();
+ if (ti)
+ {
+ /* Attempt to instantiate ti. If that works, go with it.
+ * If not, go with partial explicit specialization.
+ */
+ WithScopeSymbol withsym;
+ if (!ti.findTempDecl(sc, &withsym) || !ti.semanticTiargs(sc))
+ return setError();
+ if (withsym && withsym.withstate.wthis)
+ {
+ exp.e1 = new VarExp(exp.e1.loc, withsym.withstate.wthis);
+ exp.e1 = new DotTemplateInstanceExp(exp.e1.loc, exp.e1, ti);
+ goto Ldotti;
+ }
+ if (ti.needsTypeInference(sc, 1))
+ {
+ /* Go with partial explicit specialization
+ */
+ tiargs = ti.tiargs;
+ assert(ti.tempdecl);
+ if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
+ exp.e1 = new TemplateExp(exp.loc, td);
+ else if (OverDeclaration od = ti.tempdecl.isOverDeclaration())
+ exp.e1 = new VarExp(exp.loc, od);
+ else
+ exp.e1 = new OverExp(exp.loc, ti.tempdecl.isOverloadSet());
+ }
+ else
+ {
+ Expression e1x = exp.e1.expressionSemantic(sc);
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ exp.e1 = e1x;
+ }
+ }
+ }
+
+ /* This recognizes:
+ * expr.foo!(tiargs)(funcargs)
+ */
+ Ldotti:
+ if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type)
+ {
+ DotTemplateInstanceExp se = cast(DotTemplateInstanceExp)exp.e1;
+ TemplateInstance ti = se.ti;
+ {
+ /* Attempt to instantiate ti. If that works, go with it.
+ * If not, go with partial explicit specialization.
+ */
+ if (!se.findTempDecl(sc) || !ti.semanticTiargs(sc))
+ return setError();
+ if (ti.needsTypeInference(sc, 1))
+ {
+ /* Go with partial explicit specialization
+ */
+ tiargs = ti.tiargs;
+ assert(ti.tempdecl);
+ if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
+ exp.e1 = new DotTemplateExp(exp.loc, se.e1, td);
+ else if (OverDeclaration od = ti.tempdecl.isOverDeclaration())
+ {
+ exp.e1 = new DotVarExp(exp.loc, se.e1, od, true);
+ }
+ else
+ exp.e1 = new DotExp(exp.loc, se.e1, new OverExp(exp.loc, ti.tempdecl.isOverloadSet()));
+ }
+ else
+ {
+ Expression e1x = exp.e1.expressionSemantic(sc);
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ exp.e1 = e1x;
+ }
+ }
+ }
+
+ Lagain:
+ //printf("Lagain: %s\n", toChars());
+ exp.f = null;
+ if (exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_)
+ {
+ // semantic() run later for these
+ }
+ else
+ {
+ if (exp.e1.op == TOK.dotIdentifier)
+ {
+ DotIdExp die = cast(DotIdExp)exp.e1;
+ exp.e1 = die.expressionSemantic(sc);
+ /* Look for e1 having been rewritten to expr.opDispatch!(string)
+ * We handle such earlier, so go back.
+ * Note that in the rewrite, we carefully did not run semantic() on e1
+ */
+ if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type)
+ {
+ goto Ldotti;
+ }
+ }
+ else
+ {
+ __gshared int nest;
+ if (++nest > global.recursionLimit)
+ {
+ exp.error("recursive evaluation of `%s`", exp.toChars());
+ --nest;
+ return setError();
+ }
+ Expression ex = unaSemantic(exp, sc);
+ --nest;
+ if (ex)
+ {
+ result = ex;
+ return;
+ }
+ }
+
+ /* Look for e1 being a lazy parameter
+ */
+ if (exp.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)exp.e1;
+ if (ve.var.storage_class & STC.lazy_)
+ {
+ // lazy parameters can be called without violating purity and safety
+ Type tw = ve.var.type;
+ Type tc = ve.var.type.substWildTo(MODFlags.const_);
+ auto tf = new TypeFunction(ParameterList(), tc, LINK.d, STC.safe | STC.pure_);
+ (tf = cast(TypeFunction)tf.typeSemantic(exp.loc, sc)).next = tw; // hack for bug7757
+ auto t = new TypeDelegate(tf);
+ ve.type = t.typeSemantic(exp.loc, sc);
+ }
+ VarDeclaration v = ve.var.isVarDeclaration();
+ if (v && ve.checkPurity(sc, v))
+ return setError();
+ }
+
+ if (exp.e1.op == TOK.symbolOffset && (cast(SymOffExp)exp.e1).hasOverloads)
+ {
+ SymOffExp se = cast(SymOffExp)exp.e1;
+ exp.e1 = new VarExp(se.loc, se.var, true);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ }
+ else if (exp.e1.op == TOK.dot)
+ {
+ DotExp de = cast(DotExp)exp.e1;
+
+ if (de.e2.op == TOK.overloadSet)
+ {
+ ethis = de.e1;
+ tthis = de.e1.type;
+ exp.e1 = de.e2;
+ }
+ }
+ else if (exp.e1.op == TOK.star && exp.e1.type.ty == Tfunction)
+ {
+ // Rewrite (*fp)(arguments) to fp(arguments)
+ exp.e1 = (cast(PtrExp)exp.e1).e1;
+ }
+ else if (exp.e1.op == TOK.type && (sc && sc.flags & SCOPE.Cfile))
+ {
+ /* Ambiguous cases arise from CParser where there is not enough
+ * information to determine if we have a function call or declaration.
+ * type-name ( identifier ) ;
+ * identifier ( identifier ) ;
+ * If exp.e1 is a type-name, then this is a declaration. C11 does not
+ * have type construction syntax, so don't convert this to a cast().
+ */
+ if (exp.arguments && exp.arguments.dim == 1)
+ {
+ Expression arg = (*exp.arguments)[0];
+ if (auto ie = (*exp.arguments)[0].isIdentifierExp())
+ {
+ TypeExp te = cast(TypeExp)exp.e1;
+ auto initializer = new VoidInitializer(ie.loc);
+ Dsymbol s = new VarDeclaration(ie.loc, te.type, ie.ident, initializer);
+ auto decls = new Dsymbols(1);
+ (*decls)[0] = s;
+ s = new LinkDeclaration(s.loc, LINK.c, decls);
+ result = new DeclarationExp(exp.loc, s);
+ result = result.expressionSemantic(sc);
+ }
+ else
+ {
+ arg.error("identifier or `(` expected");
+ result = ErrorExp.get();
+ }
+ return;
+ }
+ exp.error("identifier or `(` expected before `)`");
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ Type t1 = exp.e1.type ? exp.e1.type.toBasetype() : null;
+
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
+ return setError();
+
+ // Check for call operator overload
+ if (t1)
+ {
+ if (t1.ty == Tstruct)
+ {
+ auto sd = (cast(TypeStruct)t1).sym;
+ sd.size(exp.loc); // Resolve forward references to construct object
+ if (sd.sizeok != Sizeok.done)
+ return setError();
+ if (!sd.ctor)
+ sd.ctor = sd.searchCtor();
+ /* If `sd.ctor` is a generated copy constructor, this means that it
+ is the single constructor that this struct has. In order to not
+ disable default construction, the ctor is nullified. The side effect
+ of this is that the generated copy constructor cannot be called
+ explicitly, but that is ok, because when calling a constructor the
+ default constructor should have priority over the generated copy
+ constructor.
+ */
+ if (sd.ctor)
+ {
+ auto ctor = sd.ctor.isCtorDeclaration();
+ if (ctor && ctor.isCpCtor && ctor.generated)
+ sd.ctor = null;
+ }
+
+ // First look for constructor
+ if (exp.e1.op == TOK.type && sd.ctor)
+ {
+ if (!sd.noDefaultCtor && !(exp.arguments && exp.arguments.dim))
+ goto Lx;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=20695
+ If all constructors are copy constructors, then
+ try default construction.
+ */
+ if (!sd.hasRegularCtor)
+ goto Lx;
+
+ auto sle = new StructLiteralExp(exp.loc, sd, null, exp.e1.type);
+ if (!sd.fill(exp.loc, sle.elements, true))
+ return setError();
+ if (checkFrameAccess(exp.loc, sc, sd, sle.elements.dim))
+ return setError();
+
+ // https://issues.dlang.org/show_bug.cgi?id=14556
+ // Set concrete type to avoid further redundant semantic().
+ sle.type = exp.e1.type;
+
+ /* Constructor takes a mutable object, so don't use
+ * the immutable initializer symbol.
+ */
+ sle.useStaticInit = false;
+
+ Expression e = sle;
+ if (auto cf = sd.ctor.isCtorDeclaration())
+ {
+ e = new DotVarExp(exp.loc, e, cf, true);
+ }
+ else if (auto td = sd.ctor.isTemplateDeclaration())
+ {
+ e = new DotIdExp(exp.loc, e, td.ident);
+ }
+ else if (auto os = sd.ctor.isOverloadSet())
+ {
+ e = new DotExp(exp.loc, e, new OverExp(exp.loc, os));
+ }
+ else
+ assert(0);
+ e = new CallExp(exp.loc, e, exp.arguments);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ // No constructor, look for overload of opCall
+ if (search_function(sd, Id.call))
+ goto L1;
+ // overload of opCall, therefore it's a call
+ if (exp.e1.op != TOK.type)
+ {
+ if (sd.aliasthis && !isRecursiveAliasThis(exp.att1, exp.e1.type))
+ {
+ exp.e1 = resolveAliasThis(sc, exp.e1);
+ goto Lagain;
+ }
+ exp.error("%s `%s` does not overload ()", sd.kind(), sd.toChars());
+ return setError();
+ }
+
+ /* It's a struct literal
+ */
+ Lx:
+ Expression e = new StructLiteralExp(exp.loc, sd, exp.arguments, exp.e1.type);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ else if (t1.ty == Tclass)
+ {
+ L1:
+ // Rewrite as e1.call(arguments)
+ Expression e = new DotIdExp(exp.loc, exp.e1, Id.call);
+ e = new CallExp(exp.loc, e, exp.arguments);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ else if (exp.e1.op == TOK.type && t1.isscalar())
+ {
+ Expression e;
+
+ // Make sure to use the the enum type itself rather than its
+ // base type
+ // https://issues.dlang.org/show_bug.cgi?id=16346
+ if (exp.e1.type.ty == Tenum)
+ {
+ t1 = exp.e1.type;
+ }
+
+ if (!exp.arguments || exp.arguments.dim == 0)
+ {
+ e = t1.defaultInitLiteral(exp.loc);
+ }
+ else if (exp.arguments.dim == 1)
+ {
+ e = (*exp.arguments)[0];
+ e = e.implicitCastTo(sc, t1);
+ e = new CastExp(exp.loc, e, t1);
+ }
+ else
+ {
+ exp.error("more than one argument for construction of `%s`", t1.toChars());
+ return setError();
+ }
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+
+ static FuncDeclaration resolveOverloadSet(Loc loc, Scope* sc,
+ OverloadSet os, Objects* tiargs, Type tthis, Expressions* arguments)
+ {
+ FuncDeclaration f = null;
+ foreach (s; os.a)
+ {
+ if (tiargs && s.isFuncDeclaration())
+ continue;
+ if (auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, FuncResolveFlag.quiet))
+ {
+ if (f2.errors)
+ return null;
+ if (f)
+ {
+ /* Error if match in more than one overload set,
+ * even if one is a 'better' match than the other.
+ */
+ ScopeDsymbol.multiplyDefined(loc, f, f2);
+ }
+ else
+ f = f2;
+ }
+ }
+ if (!f)
+ .error(loc, "no overload matches for `%s`", os.toChars());
+ else if (f.errors)
+ f = null;
+ return f;
+ }
+
+ bool isSuper = false;
+ if (exp.e1.op == TOK.dotVariable && t1.ty == Tfunction || exp.e1.op == TOK.dotTemplateDeclaration)
+ {
+ UnaExp ue = cast(UnaExp)exp.e1;
+
+ Expression ue1 = ue.e1;
+ Expression ue1old = ue1; // need for 'right this' check
+ VarDeclaration v;
+ if (ue1.op == TOK.variable && (v = (cast(VarExp)ue1).var.isVarDeclaration()) !is null && v.needThis())
+ {
+ ue.e1 = new TypeExp(ue1.loc, ue1.type);
+ ue1 = null;
+ }
+
+ DotVarExp dve;
+ DotTemplateExp dte;
+ Dsymbol s;
+ if (exp.e1.op == TOK.dotVariable)
+ {
+ dve = cast(DotVarExp)exp.e1;
+ dte = null;
+ s = dve.var;
+ tiargs = null;
+ }
+ else
+ {
+ dve = null;
+ dte = cast(DotTemplateExp)exp.e1;
+ s = dte.td;
+ }
+
+ // Do overload resolution
+ exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, ue1 ? ue1.type : null, exp.arguments, FuncResolveFlag.standard);
+ if (!exp.f || exp.f.errors || exp.f.type.ty == Terror)
+ return setError();
+
+ if (exp.f.interfaceVirtual)
+ {
+ /* Cast 'this' to the type of the interface, and replace f with the interface's equivalent
+ */
+ auto b = exp.f.interfaceVirtual;
+ auto ad2 = b.sym;
+ ue.e1 = ue.e1.castTo(sc, ad2.type.addMod(ue.e1.type.mod));
+ ue.e1 = ue.e1.expressionSemantic(sc);
+ ue1 = ue.e1;
+ auto vi = exp.f.findVtblIndex(&ad2.vtbl, cast(int)ad2.vtbl.dim);
+ assert(vi >= 0);
+ exp.f = ad2.vtbl[vi].isFuncDeclaration();
+ assert(exp.f);
+ }
+ if (exp.f.needThis())
+ {
+ AggregateDeclaration ad = exp.f.toParentLocal().isAggregateDeclaration();
+ ue.e1 = getRightThis(exp.loc, sc, ad, ue.e1, exp.f);
+ if (ue.e1.op == TOK.error)
+ {
+ result = ue.e1;
+ return;
+ }
+ ethis = ue.e1;
+ tthis = ue.e1.type;
+ if (!(exp.f.type.ty == Tfunction && (cast(TypeFunction)exp.f.type).isScopeQual))
+ {
+ if (global.params.useDIP1000 == FeatureState.enabled && checkParamArgumentEscape(sc, exp.f, null, ethis, false, false))
+ return setError();
+ }
+ }
+
+ /* Cannot call public functions from inside invariant
+ * (because then the invariant would have infinite recursion)
+ */
+ if (sc.func && sc.func.isInvariantDeclaration() && ue.e1.op == TOK.this_ && exp.f.addPostInvariant())
+ {
+ exp.error("cannot call `public`/`export` function `%s` from invariant", exp.f.toChars());
+ return setError();
+ }
+
+ if (!exp.ignoreAttributes)
+ checkFunctionAttributes(exp, sc, exp.f);
+ checkAccess(exp.loc, sc, ue.e1, exp.f);
+ if (!exp.f.needThis())
+ {
+ exp.e1 = Expression.combine(ue.e1, new VarExp(exp.loc, exp.f, false));
+ }
+ else
+ {
+ if (ue1old.checkRightThis(sc))
+ return setError();
+ if (exp.e1.op == TOK.dotVariable)
+ {
+ dve.var = exp.f;
+ exp.e1.type = exp.f.type;
+ }
+ else
+ {
+ exp.e1 = new DotVarExp(exp.loc, dte.e1, exp.f, false);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ if (exp.e1.op == TOK.error)
+ return setError();
+ ue = cast(UnaExp)exp.e1;
+ }
+ version (none)
+ {
+ printf("ue.e1 = %s\n", ue.e1.toChars());
+ printf("f = %s\n", exp.f.toChars());
+ printf("t1 = %s\n", t1.toChars());
+ printf("e1 = %s\n", exp.e1.toChars());
+ printf("e1.type = %s\n", exp.e1.type.toChars());
+ }
+
+ // See if we need to adjust the 'this' pointer
+ AggregateDeclaration ad = exp.f.isThis();
+ ClassDeclaration cd = ue.e1.type.isClassHandle();
+ if (ad && cd && ad.isClassDeclaration())
+ {
+ if (ue.e1.op == TOK.dotType)
+ {
+ ue.e1 = (cast(DotTypeExp)ue.e1).e1;
+ exp.directcall = true;
+ }
+ else if (ue.e1.op == TOK.super_)
+ exp.directcall = true;
+ else if ((cd.storage_class & STC.final_) != 0) // https://issues.dlang.org/show_bug.cgi?id=14211
+ exp.directcall = true;
+
+ if (ad != cd)
+ {
+ ue.e1 = ue.e1.castTo(sc, ad.type.addMod(ue.e1.type.mod));
+ ue.e1 = ue.e1.expressionSemantic(sc);
+ }
+ }
+ }
+ // If we've got a pointer to a function then deference it
+ // https://issues.dlang.org/show_bug.cgi?id=16483
+ if (exp.e1.type.isPtrToFunction())
+ {
+ Expression e = new PtrExp(exp.loc, exp.e1);
+ e.type = exp.e1.type.nextOf();
+ exp.e1 = e;
+ }
+ t1 = exp.e1.type;
+ }
+ else if (exp.e1.op == TOK.super_ || exp.e1.op == TOK.this_)
+ {
+ auto ad = sc.func ? sc.func.isThis() : null;
+ auto cd = ad ? ad.isClassDeclaration() : null;
+
+ isSuper = exp.e1.op == TOK.super_;
+ if (isSuper)
+ {
+ // Base class constructor call
+ if (!cd || !cd.baseClass || !sc.func.isCtorDeclaration())
+ {
+ exp.error("super class constructor call must be in a constructor");
+ return setError();
+ }
+ if (!cd.baseClass.ctor)
+ {
+ exp.error("no super class constructor for `%s`", cd.baseClass.toChars());
+ return setError();
+ }
+ }
+ else
+ {
+ // `this` call expression must be inside a
+ // constructor
+ if (!ad || !sc.func.isCtorDeclaration())
+ {
+ exp.error("constructor call must be in a constructor");
+ return setError();
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=18719
+ // If `exp` is a call expression to another constructor
+ // then it means that all struct/class fields will be
+ // initialized after this call.
+ foreach (ref field; sc.ctorflow.fieldinit)
+ {
+ field.csx |= CSX.this_ctor;
+ }
+ }
+
+ if (!sc.intypeof && !(sc.ctorflow.callSuper & CSX.halt))
+ {
+ if (sc.inLoop || sc.ctorflow.callSuper & CSX.label)
+ exp.error("constructor calls not allowed in loops or after labels");
+ if (sc.ctorflow.callSuper & (CSX.super_ctor | CSX.this_ctor))
+ exp.error("multiple constructor calls");
+ if ((sc.ctorflow.callSuper & CSX.return_) && !(sc.ctorflow.callSuper & CSX.any_ctor))
+ exp.error("an earlier `return` statement skips constructor");
+ sc.ctorflow.callSuper |= CSX.any_ctor | (isSuper ? CSX.super_ctor : CSX.this_ctor);
+ }
+
+ tthis = ad.type.addMod(sc.func.type.mod);
+ auto ctor = isSuper ? cd.baseClass.ctor : ad.ctor;
+ if (auto os = ctor.isOverloadSet())
+ exp.f = resolveOverloadSet(exp.loc, sc, os, null, tthis, exp.arguments);
+ else
+ exp.f = resolveFuncCall(exp.loc, sc, ctor, null, tthis, exp.arguments, FuncResolveFlag.standard);
+
+ if (!exp.f || exp.f.errors)
+ return setError();
+
+ checkFunctionAttributes(exp, sc, exp.f);
+ checkAccess(exp.loc, sc, null, exp.f);
+
+ exp.e1 = new DotVarExp(exp.e1.loc, exp.e1, exp.f, false);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ // https://issues.dlang.org/show_bug.cgi?id=21095
+ if (exp.e1.op == TOK.error)
+ return setError();
+ t1 = exp.e1.type;
+
+ // BUG: this should really be done by checking the static
+ // call graph
+ if (exp.f == sc.func)
+ {
+ exp.error("cyclic constructor call");
+ return setError();
+ }
+ }
+ else if (exp.e1.op == TOK.overloadSet)
+ {
+ auto os = (cast(OverExp)exp.e1).vars;
+ exp.f = resolveOverloadSet(exp.loc, sc, os, tiargs, tthis, exp.arguments);
+ if (!exp.f)
+ return setError();
+ if (ethis)
+ exp.e1 = new DotVarExp(exp.loc, ethis, exp.f, false);
+ else
+ exp.e1 = new VarExp(exp.loc, exp.f, false);
+ goto Lagain;
+ }
+ else if (!t1)
+ {
+ exp.error("function expected before `()`, not `%s`", exp.e1.toChars());
+ return setError();
+ }
+ else if (t1.ty == Terror)
+ {
+ return setError();
+ }
+ else if (t1.ty != Tfunction)
+ {
+ TypeFunction tf;
+ const(char)* p;
+ Dsymbol s;
+ exp.f = null;
+ if (exp.e1.op == TOK.function_)
+ {
+ // function literal that direct called is always inferred.
+ assert((cast(FuncExp)exp.e1).fd);
+ exp.f = (cast(FuncExp)exp.e1).fd;
+ tf = cast(TypeFunction)exp.f.type;
+ p = "function literal";
+ }
+ else if (t1.ty == Tdelegate)
+ {
+ TypeDelegate td = cast(TypeDelegate)t1;
+ assert(td.next.ty == Tfunction);
+ tf = cast(TypeFunction)td.next;
+ p = "delegate";
+ }
+ else if (auto tfx = t1.isPtrToFunction())
+ {
+ tf = tfx;
+ p = "function pointer";
+ }
+ else if (exp.e1.op == TOK.dotVariable && (cast(DotVarExp)exp.e1).var.isOverDeclaration())
+ {
+ DotVarExp dve = cast(DotVarExp)exp.e1;
+ exp.f = resolveFuncCall(exp.loc, sc, dve.var, tiargs, dve.e1.type, exp.arguments, FuncResolveFlag.overloadOnly);
+ if (!exp.f)
+ return setError();
+ if (exp.f.needThis())
+ {
+ dve.var = exp.f;
+ dve.type = exp.f.type;
+ dve.hasOverloads = false;
+ goto Lagain;
+ }
+ exp.e1 = new VarExp(dve.loc, exp.f, false);
+ Expression e = new CommaExp(exp.loc, dve.e1, exp);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ else if (exp.e1.op == TOK.variable && (cast(VarExp)exp.e1).var.isOverDeclaration())
+ {
+ s = (cast(VarExp)exp.e1).var;
+ goto L2;
+ }
+ else if (exp.e1.op == TOK.template_)
+ {
+ s = (cast(TemplateExp)exp.e1).td;
+ L2:
+ exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, null, exp.arguments, FuncResolveFlag.standard);
+ if (!exp.f || exp.f.errors)
+ return setError();
+ if (exp.f.needThis())
+ {
+ if (hasThis(sc))
+ {
+ // Supply an implicit 'this', as in
+ // this.ident
+ exp.e1 = new DotVarExp(exp.loc, (new ThisExp(exp.loc)).expressionSemantic(sc), exp.f, false);
+ goto Lagain;
+ }
+ else if (isNeedThisScope(sc, exp.f))
+ {
+ exp.error("need `this` for `%s` of type `%s`", exp.f.toChars(), exp.f.type.toChars());
+ return setError();
+ }
+ }
+ exp.e1 = new VarExp(exp.e1.loc, exp.f, false);
+ goto Lagain;
+ }
+ else
+ {
+ exp.error("function expected before `()`, not `%s` of type `%s`", exp.e1.toChars(), exp.e1.type.toChars());
+ return setError();
+ }
+
+ const(char)* failMessage;
+ Expression[] fargs = exp.arguments ? (*exp.arguments)[] : null;
+ if (!tf.callMatch(null, fargs, 0, &failMessage, sc))
+ {
+ OutBuffer buf;
+ buf.writeByte('(');
+ argExpTypesToCBuffer(&buf, exp.arguments);
+ buf.writeByte(')');
+ if (tthis)
+ tthis.modToBuffer(&buf);
+
+ //printf("tf = %s, args = %s\n", tf.deco, (*arguments)[0].type.deco);
+ .error(exp.loc, "%s `%s%s` is not callable using argument types `%s`",
+ p, exp.e1.toChars(), parametersTypeToChars(tf.parameterList), buf.peekChars());
+ if (failMessage)
+ errorSupplemental(exp.loc, "%s", failMessage);
+ return setError();
+ }
+ // Purity and safety check should run after testing arguments matching
+ if (exp.f)
+ {
+ exp.checkPurity(sc, exp.f);
+ exp.checkSafety(sc, exp.f);
+ exp.checkNogc(sc, exp.f);
+ if (exp.f.checkNestedReference(sc, exp.loc))
+ return setError();
+ }
+ else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_)))
+ {
+ bool err = false;
+ if (!tf.purity && sc.func.setImpure())
+ {
+ exp.error("`pure` %s `%s` cannot call impure %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars());
+ err = true;
+ }
+ if (!tf.isnogc && sc.func.setGC())
+ {
+ exp.error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars());
+ err = true;
+ }
+ if (tf.trust <= TRUST.system && sc.func.setUnsafe())
+ {
+ exp.error("`@safe` %s `%s` cannot call `@system` %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars());
+ err = true;
+ }
+ if (err)
+ return setError();
+ }
+
+ if (t1.ty == Tpointer)
+ {
+ Expression e = new PtrExp(exp.loc, exp.e1);
+ e.type = tf;
+ exp.e1 = e;
+ }
+ t1 = tf;
+ }
+ else if (exp.e1.op == TOK.variable)
+ {
+ // Do overload resolution
+ VarExp ve = cast(VarExp)exp.e1;
+
+ exp.f = ve.var.isFuncDeclaration();
+ assert(exp.f);
+ tiargs = null;
+
+ if (exp.f.overnext)
+ exp.f = resolveFuncCall(exp.loc, sc, exp.f, tiargs, null, exp.arguments, FuncResolveFlag.overloadOnly);
+ else
+ {
+ exp.f = exp.f.toAliasFunc();
+ TypeFunction tf = cast(TypeFunction)exp.f.type;
+ const(char)* failMessage;
+ Expression[] fargs = exp.arguments ? (*exp.arguments)[] : null;
+ if (!tf.callMatch(null, fargs, 0, &failMessage, sc))
+ {
+ OutBuffer buf;
+ buf.writeByte('(');
+ argExpTypesToCBuffer(&buf, exp.arguments);
+ buf.writeByte(')');
+
+ //printf("tf = %s, args = %s\n", tf.deco, (*arguments)[0].type.deco);
+ .error(exp.loc, "%s `%s%s` is not callable using argument types `%s`",
+ exp.f.kind(), exp.f.toPrettyChars(), parametersTypeToChars(tf.parameterList), buf.peekChars());
+ if (failMessage)
+ errorSupplemental(exp.loc, "%s", failMessage);
+ exp.f = null;
+ }
+ }
+ if (!exp.f || exp.f.errors)
+ return setError();
+
+ if (exp.f.needThis())
+ {
+ // Change the ancestor lambdas to delegate before hasThis(sc) call.
+ if (exp.f.checkNestedReference(sc, exp.loc))
+ return setError();
+
+ if (hasThis(sc))
+ {
+ // Supply an implicit 'this', as in
+ // this.ident
+ exp.e1 = new DotVarExp(exp.loc, (new ThisExp(exp.loc)).expressionSemantic(sc), ve.var);
+ // Note: we cannot use f directly, because further overload resolution
+ // through the supplied 'this' may cause different result.
+ goto Lagain;
+ }
+ else if (isNeedThisScope(sc, exp.f))
+ {
+ // At this point it is possible that `exp.f` had an ambiguity error that was
+ // silenced because the previous call to `resolveFuncCall` was done using
+ // `FuncResolveFlag.overloadOnly`. To make sure that a proper error message
+ // is printed, redo the call with `FuncResolveFlag.standard`.
+ //
+ // https://issues.dlang.org/show_bug.cgi?id=22157
+ if (exp.f.overnext)
+ exp.f = resolveFuncCall(exp.loc, sc, exp.f, tiargs, null, exp.arguments, FuncResolveFlag.standard);
+
+ if (!exp.f || exp.f.errors)
+ return setError();
+
+ // If no error is printed, it means that `f` is the single matching overload
+ // and it needs `this`.
+ exp.error("need `this` for `%s` of type `%s`", exp.f.toChars(), exp.f.type.toChars());
+ return setError();
+ }
+ }
+
+ checkFunctionAttributes(exp, sc, exp.f);
+ checkAccess(exp.loc, sc, null, exp.f);
+ if (exp.f.checkNestedReference(sc, exp.loc))
+ return setError();
+
+ ethis = null;
+ tthis = null;
+
+ if (ve.hasOverloads)
+ {
+ exp.e1 = new VarExp(ve.loc, exp.f, false);
+ exp.e1.type = exp.f.type;
+ }
+ t1 = exp.f.type;
+ }
+ assert(t1.ty == Tfunction);
+
+ Expression argprefix;
+ if (!exp.arguments)
+ exp.arguments = new Expressions();
+ if (functionParameters(exp.loc, sc, cast(TypeFunction)t1, ethis, tthis, exp.arguments, exp.f, &exp.type, &argprefix))
+ return setError();
+
+ if (!exp.type)
+ {
+ exp.e1 = e1org; // https://issues.dlang.org/show_bug.cgi?id=10922
+ // avoid recursive expression printing
+ exp.error("forward reference to inferred return type of function call `%s`", exp.toChars());
+ return setError();
+ }
+
+ if (exp.f && exp.f.tintro)
+ {
+ Type t = exp.type;
+ int offset = 0;
+ TypeFunction tf = cast(TypeFunction)exp.f.tintro;
+ if (tf.next.isBaseOf(t, &offset) && offset)
+ {
+ exp.type = tf.next;
+ result = Expression.combine(argprefix, exp.castTo(sc, t));
+ return;
+ }
+ }
+
+ // Handle the case of a direct lambda call
+ if (exp.f && exp.f.isFuncLiteralDeclaration() && sc.func && !sc.intypeof)
+ {
+ exp.f.tookAddressOf = 0;
+ }
+
+ result = Expression.combine(argprefix, exp);
+
+ if (isSuper)
+ {
+ auto ad = sc.func ? sc.func.isThis() : null;
+ auto cd = ad ? ad.isClassDeclaration() : null;
+ if (cd && cd.classKind == ClassKind.cpp && exp.f && !exp.f.fbody)
+ {
+ // if super is defined in C++, it sets the vtable pointer to the base class
+ // so we have to restore it, but still return 'this' from super() call:
+ // (auto __vptrTmp = this.__vptr, auto __superTmp = super()), (this.__vptr = __vptrTmp, __superTmp)
+ Loc loc = exp.loc;
+
+ auto vptr = new DotIdExp(loc, new ThisExp(loc), Id.__vptr);
+ auto vptrTmpDecl = copyToTemp(0, "__vptrTmp", vptr);
+ auto declareVptrTmp = new DeclarationExp(loc, vptrTmpDecl);
+
+ auto superTmpDecl = copyToTemp(0, "__superTmp", result);
+ auto declareSuperTmp = new DeclarationExp(loc, superTmpDecl);
+
+ auto declareTmps = new CommaExp(loc, declareVptrTmp, declareSuperTmp);
+
+ auto restoreVptr = new AssignExp(loc, vptr.syntaxCopy(), new VarExp(loc, vptrTmpDecl));
+
+ Expression e = new CommaExp(loc, declareTmps, new CommaExp(loc, restoreVptr, new VarExp(loc, superTmpDecl)));
+ result = e.expressionSemantic(sc);
+ }
+ }
+
+ // declare dual-context container
+ if (exp.f && exp.f.isThis2 && !sc.intypeof && sc.func)
+ {
+ // check access to second `this`
+ if (AggregateDeclaration ad2 = exp.f.isMember2())
+ {
+ Expression te = new ThisExp(exp.loc).expressionSemantic(sc);
+ if (te.op != TOK.error)
+ te = getRightThis(exp.loc, sc, ad2, te, exp.f);
+ if (te.op == TOK.error)
+ {
+ exp.error("need `this` of type `%s` to call function `%s`", ad2.toChars(), exp.f.toChars());
+ return setError();
+ }
+ }
+ exp.vthis2 = makeThis2Argument(exp.loc, sc, exp.f);
+ Expression de = new DeclarationExp(exp.loc, exp.vthis2);
+ result = Expression.combine(de, result);
+ result = result.expressionSemantic(sc);
+ }
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+ static if (LOGSEMANTIC)
+ {
+ printf("DeclarationExp::semantic() %s\n", e.toChars());
+ }
+
+ uint olderrors = global.errors;
+
+ /* This is here to support extern(linkage) declaration,
+ * where the extern(linkage) winds up being an AttribDeclaration
+ * wrapper.
+ */
+ Dsymbol s = e.declaration;
+
+ while (1)
+ {
+ AttribDeclaration ad = s.isAttribDeclaration();
+ if (ad)
+ {
+ if (ad.decl && ad.decl.dim == 1)
+ {
+ s = (*ad.decl)[0];
+ continue;
+ }
+ }
+ break;
+ }
+
+ VarDeclaration v = s.isVarDeclaration();
+ if (v)
+ {
+ // Do semantic() on initializer first, so:
+ // int a = a;
+ // will be illegal.
+ e.declaration.dsymbolSemantic(sc);
+ s.parent = sc.parent;
+ }
+
+ //printf("inserting '%s' %p into sc = %p\n", s.toChars(), s, sc);
+ // Insert into both local scope and function scope.
+ // Must be unique in both.
+ if (s.ident)
+ {
+ if (!sc.insert(s))
+ {
+ auto conflict = sc.search(Loc.initial, s.ident, null);
+ e.error("declaration `%s` is already defined", s.toPrettyChars());
+ errorSupplemental(conflict.loc, "`%s` `%s` is defined here",
+ conflict.kind(), conflict.toChars());
+ return setError();
+ }
+ else if (sc.func)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=11720
+ if ((s.isFuncDeclaration() ||
+ s.isAggregateDeclaration() ||
+ s.isEnumDeclaration() ||
+ s.isTemplateDeclaration() ||
+ v
+ ) && !sc.func.localsymtab.insert(s))
+ {
+ // Get the previous symbol
+ Dsymbol originalSymbol = sc.func.localsymtab.lookup(s.ident);
+
+ // Perturb the name mangling so that the symbols can co-exist
+ // instead of colliding
+ s.localNum = cast(ushort)(originalSymbol.localNum + 1);
+ assert(s.localNum); // 65535 should be enough for anyone
+
+ // Replace originalSymbol with s, which updates the localCount
+ sc.func.localsymtab.update(s);
+
+ // The mangling change only works for D mangling
+ }
+// else
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=21272
+ * If we are in a foreach body we need to extract the
+ * function containing the foreach
+ */
+ FuncDeclaration fes_enclosing_func;
+ if (sc.func && sc.func.fes)
+ fes_enclosing_func = sc.enclosing.enclosing.func;
+
+ // Disallow shadowing
+ for (Scope* scx = sc.enclosing; scx && (scx.func == sc.func || (fes_enclosing_func && scx.func == fes_enclosing_func)); scx = scx.enclosing)
+ {
+ Dsymbol s2;
+ if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2)
+ {
+ // allow STC.local symbols to be shadowed
+ // TODO: not really an optimal design
+ auto decl = s2.isDeclaration();
+ if (!decl || !(decl.storage_class & STC.local))
+ {
+ if (sc.func.fes)
+ {
+ e.deprecation("%s `%s` is shadowing %s `%s`. Rename the `foreach` variable.", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
+ }
+ else
+ {
+ e.error("%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
+ return setError();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!s.isVarDeclaration())
+ {
+ Scope* sc2 = sc;
+ if (sc2.stc & (STC.pure_ | STC.nothrow_ | STC.nogc))
+ sc2 = sc.push();
+ sc2.stc &= ~(STC.pure_ | STC.nothrow_ | STC.nogc);
+ e.declaration.dsymbolSemantic(sc2);
+ if (sc2 != sc)
+ sc2.pop();
+ s.parent = sc.parent;
+ }
+ if (global.errors == olderrors)
+ {
+ e.declaration.semantic2(sc);
+ if (global.errors == olderrors)
+ {
+ e.declaration.semantic3(sc);
+ }
+ }
+ // todo: error in declaration should be propagated.
+
+ e.type = Type.tvoid;
+ result = e;
+ }
+
+ override void visit(TypeidExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("TypeidExp::semantic() %s\n", exp.toChars());
+ }
+ Type ta = isType(exp.obj);
+ Expression ea = isExpression(exp.obj);
+ Dsymbol sa = isDsymbol(exp.obj);
+ //printf("ta %p ea %p sa %p\n", ta, ea, sa);
+
+ if (ta)
+ {
+ dmd.typesem.resolve(ta, exp.loc, sc, ea, ta, sa, true);
+ }
+
+ if (ea)
+ {
+ if (auto sym = getDsymbol(ea))
+ ea = symbolToExp(sym, exp.loc, sc, false);
+ else
+ ea = ea.expressionSemantic(sc);
+ ea = resolveProperties(sc, ea);
+ ta = ea.type;
+ if (ea.op == TOK.type)
+ ea = null;
+ }
+
+ if (!ta)
+ {
+ //printf("ta %p ea %p sa %p\n", ta, ea, sa);
+ exp.error("no type for `typeid(%s)`", ea ? ea.toChars() : (sa ? sa.toChars() : ""));
+ return setError();
+ }
+
+ if (global.params.vcomplex)
+ ta.checkComplexTransition(exp.loc, sc);
+
+ Expression e;
+ auto tb = ta.toBasetype();
+ if (ea && tb.ty == Tclass)
+ {
+ if (tb.toDsymbol(sc).isClassDeclaration().classKind == ClassKind.cpp)
+ {
+ error(exp.loc, "Runtime type information is not supported for `extern(C++)` classes");
+ e = ErrorExp.get();
+ }
+ else if (!Type.typeinfoclass)
+ {
+ error(exp.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
+ e = ErrorExp.get();
+ }
+ else
+ {
+ /* Get the dynamic type, which is .classinfo
+ */
+ ea = ea.expressionSemantic(sc);
+ e = new TypeidExp(ea.loc, ea);
+ e.type = Type.typeinfoclass.type;
+ }
+ }
+ else if (ta.ty == Terror)
+ {
+ e = ErrorExp.get();
+ }
+ else
+ {
+ // Handle this in the glue layer
+ e = new TypeidExp(exp.loc, ta);
+ e.type = getTypeInfoType(exp.loc, ta, sc);
+
+ semanticTypeInfo(sc, ta);
+
+ if (ea)
+ {
+ e = new CommaExp(exp.loc, ea, e); // execute ea
+ e = e.expressionSemantic(sc);
+ }
+ }
+ result = e;
+ }
+
+ override void visit(TraitsExp e)
+ {
+ result = semanticTraits(e, sc);
+ }
+
+ override void visit(HaltExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("HaltExp::semantic()\n");
+ }
+ e.type = Type.tnoreturn;
+ result = e;
+ }
+
+ override void visit(IsExp e)
+ {
+ /* is(targ id tok tspec)
+ * is(targ id : tok2)
+ * is(targ id == tok2)
+ */
+ Type tded = null;
+
+ void yes()
+ {
+ //printf("yes\n");
+ if (!e.id)
+ {
+ result = IntegerExp.createBool(true);
+ return;
+ }
+
+ Dsymbol s;
+ Tuple tup = isTuple(tded);
+ if (tup)
+ s = new TupleDeclaration(e.loc, e.id, &tup.objects);
+ else
+ s = new AliasDeclaration(e.loc, e.id, tded);
+ s.dsymbolSemantic(sc);
+
+ /* The reason for the !tup is unclear. It fails Phobos unittests if it is not there.
+ * More investigation is needed.
+ */
+ if (!tup && !sc.insert(s))
+ {
+ auto conflict = sc.search(Loc.initial, s.ident, null);
+ e.error("declaration `%s` is already defined", s.toPrettyChars());
+ errorSupplemental(conflict.loc, "`%s` `%s` is defined here",
+ conflict.kind(), conflict.toChars());
+ }
+
+ unSpeculative(sc, s);
+
+ result = IntegerExp.createBool(true);
+ }
+ void no()
+ {
+ result = IntegerExp.createBool(false);
+ //printf("no\n");
+ }
+
+ static if (LOGSEMANTIC)
+ {
+ printf("IsExp::semantic(%s)\n", e.toChars());
+ }
+ if (e.id && !(sc.flags & SCOPE.condition))
+ {
+ e.error("can only declare type aliases within `static if` conditionals or `static assert`s");
+ return setError();
+ }
+
+ if (e.tok2 == TOK.package_ || e.tok2 == TOK.module_) // These is() expressions are special because they can work on modules, not just types.
+ {
+ const oldErrors = global.startGagging();
+ Dsymbol sym = e.targ.toDsymbol(sc);
+ global.endGagging(oldErrors);
+
+ if (sym is null)
+ return no();
+ Package p = resolveIsPackage(sym);
+ if (p is null)
+ return no();
+ if (e.tok2 == TOK.package_ && p.isModule()) // Note that isModule() will return null for package modules because they're not actually instances of Module.
+ return no();
+ else if(e.tok2 == TOK.module_ && !(p.isModule() || p.isPackageMod()))
+ return no();
+ tded = e.targ;
+ return yes();
+ }
+
+ {
+ Scope* sc2 = sc.copy(); // keep sc.flags
+ sc2.tinst = null;
+ sc2.minst = null;
+ sc2.flags |= SCOPE.fullinst;
+ Type t = e.targ.trySemantic(e.loc, sc2);
+ sc2.pop();
+ if (!t) // errors, so condition is false
+ return no();
+ e.targ = t;
+ }
+
+ if (e.tok2 != TOK.reserved)
+ {
+ switch (e.tok2)
+ {
+ case TOK.struct_:
+ if (e.targ.ty != Tstruct)
+ return no();
+ if ((cast(TypeStruct)e.targ).sym.isUnionDeclaration())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.union_:
+ if (e.targ.ty != Tstruct)
+ return no();
+ if (!(cast(TypeStruct)e.targ).sym.isUnionDeclaration())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.class_:
+ if (e.targ.ty != Tclass)
+ return no();
+ if ((cast(TypeClass)e.targ).sym.isInterfaceDeclaration())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.interface_:
+ if (e.targ.ty != Tclass)
+ return no();
+ if (!(cast(TypeClass)e.targ).sym.isInterfaceDeclaration())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.const_:
+ if (!e.targ.isConst())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.immutable_:
+ if (!e.targ.isImmutable())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.shared_:
+ if (!e.targ.isShared())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.inout_:
+ if (!e.targ.isWild())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.super_:
+ // If class or interface, get the base class and interfaces
+ if (e.targ.ty != Tclass)
+ return no();
+ else
+ {
+ ClassDeclaration cd = (cast(TypeClass)e.targ).sym;
+ auto args = new Parameters();
+ args.reserve(cd.baseclasses.dim);
+ if (cd.semanticRun < PASS.semanticdone)
+ cd.dsymbolSemantic(null);
+ for (size_t i = 0; i < cd.baseclasses.dim; i++)
+ {
+ BaseClass* b = (*cd.baseclasses)[i];
+ args.push(new Parameter(STC.in_, b.type, null, null, null));
+ }
+ tded = new TypeTuple(args);
+ }
+ break;
+
+ case TOK.enum_:
+ if (e.targ.ty != Tenum)
+ return no();
+ if (e.id)
+ tded = (cast(TypeEnum)e.targ).sym.getMemtype(e.loc);
+ else
+ tded = e.targ;
+
+ if (tded.ty == Terror)
+ return setError();
+ break;
+
+ case TOK.delegate_:
+ if (e.targ.ty != Tdelegate)
+ return no();
+ tded = (cast(TypeDelegate)e.targ).next; // the underlying function type
+ break;
+
+ case TOK.function_:
+ case TOK.parameters:
+ {
+ if (e.targ.ty != Tfunction)
+ return no();
+ tded = e.targ;
+
+ /* Generate tuple from function parameter types.
+ */
+ assert(tded.ty == Tfunction);
+ auto tdedf = tded.isTypeFunction();
+ auto args = new Parameters();
+ foreach (i, arg; tdedf.parameterList)
+ {
+ assert(arg && arg.type);
+ /* If one of the default arguments was an error,
+ don't return an invalid tuple
+ */
+ if (e.tok2 == TOK.parameters && arg.defaultArg && arg.defaultArg.op == TOK.error)
+ return setError();
+ args.push(new Parameter(arg.storageClass, arg.type, (e.tok2 == TOK.parameters) ? arg.ident : null, (e.tok2 == TOK.parameters) ? arg.defaultArg : null, arg.userAttribDecl));
+ }
+ tded = new TypeTuple(args);
+ break;
+ }
+ case TOK.return_:
+ /* Get the 'return type' for the function,
+ * delegate, or pointer to function.
+ */
+ if (auto tf = e.targ.isFunction_Delegate_PtrToFunction())
+ tded = tf.next;
+ else
+ return no();
+ break;
+
+ case TOK.argumentTypes:
+ /* Generate a type tuple of the equivalent types used to determine if a
+ * function argument of this type can be passed in registers.
+ * The results of this are highly platform dependent, and intended
+ * primarly for use in implementing va_arg().
+ */
+ tded = target.toArgTypes(e.targ);
+ if (!tded)
+ return no();
+ // not valid for a parameter
+ break;
+
+ case TOK.vector:
+ if (e.targ.ty != Tvector)
+ return no();
+ tded = (cast(TypeVector)e.targ).basetype;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=18753
+ if (tded)
+ return yes();
+ return no();
+ }
+ else if (e.tspec && !e.id && !(e.parameters && e.parameters.dim))
+ {
+ /* Evaluate to true if targ matches tspec
+ * is(targ == tspec)
+ * is(targ : tspec)
+ */
+ e.tspec = e.tspec.typeSemantic(e.loc, sc);
+ //printf("targ = %s, %s\n", e.targ.toChars(), e.targ.deco);
+ //printf("tspec = %s, %s\n", e.tspec.toChars(), e.tspec.deco);
+
+ if (e.tok == TOK.colon)
+ {
+ // current scope is itself deprecated, or deprecations are not errors
+ const bool deprecationAllowed = sc.isDeprecated
+ || global.params.useDeprecated != DiagnosticReporting.error;
+ const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed;
+
+ // baseClass might not be set if either targ or tspec is forward referenced.
+ if (auto tc = e.targ.isTypeClass())
+ tc.sym.dsymbolSemantic(null);
+ if (auto tc = e.tspec.isTypeClass())
+ tc.sym.dsymbolSemantic(null);
+
+ if (preventAliasThis && e.targ.ty == Tstruct)
+ {
+ if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec))
+ return yes();
+ else
+ return no();
+ }
+ else if (preventAliasThis && e.targ.ty == Tclass)
+ {
+ if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec))
+ return yes();
+ else
+ return no();
+ }
+ else if (e.targ.implicitConvTo(e.tspec))
+ return yes();
+ else
+ return no();
+ }
+ else /* == */
+ {
+ if (e.targ.equals(e.tspec))
+ return yes();
+ else
+ return no();
+ }
+ }
+ else if (e.tspec)
+ {
+ /* Evaluate to true if targ matches tspec.
+ * If true, declare id as an alias for the specialized type.
+ * is(targ == tspec, tpl)
+ * is(targ : tspec, tpl)
+ * is(targ id == tspec)
+ * is(targ id : tspec)
+ * is(targ id == tspec, tpl)
+ * is(targ id : tspec, tpl)
+ */
+ Identifier tid = e.id ? e.id : Identifier.generateId("__isexp_id");
+ e.parameters.insert(0, new TemplateTypeParameter(e.loc, tid, null, null));
+
+ Objects dedtypes = Objects(e.parameters.dim);
+ dedtypes.zero();
+
+ MATCH m = deduceType(e.targ, sc, e.tspec, e.parameters, &dedtypes, null, 0, e.tok == TOK.equal);
+ //printf("targ: %s\n", targ.toChars());
+ //printf("tspec: %s\n", tspec.toChars());
+ if (m == MATCH.nomatch || (m != MATCH.exact && e.tok == TOK.equal))
+ {
+ return no();
+ }
+ else
+ {
+ tded = cast(Type)dedtypes[0];
+ if (!tded)
+ tded = e.targ;
+ Objects tiargs = Objects(1);
+ tiargs[0] = e.targ;
+
+ /* Declare trailing parameters
+ */
+ for (size_t i = 1; i < e.parameters.dim; i++)
+ {
+ TemplateParameter tp = (*e.parameters)[i];
+ Declaration s = null;
+
+ m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, &dedtypes, &s);
+ if (m == MATCH.nomatch)
+ return no();
+ s.dsymbolSemantic(sc);
+ if (!sc.insert(s))
+ {
+ auto conflict = sc.search(Loc.initial, s.ident, null);
+ e.error("declaration `%s` is already defined", s.toPrettyChars());
+ errorSupplemental(conflict.loc, "`%s` `%s` is defined here",
+ conflict.kind(), conflict.toChars());
+ }
+
+ unSpeculative(sc, s);
+ }
+ return yes();
+ }
+ }
+ else if (e.id)
+ {
+ /* Declare id as an alias for type targ. Evaluate to true
+ * is(targ id)
+ */
+ tded = e.targ;
+ }
+ return yes();
+ }
+
+ override void visit(BinAssignExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.op == TOK.arrayLength)
+ {
+ // arr.length op= e2;
+ e = rewriteOpAssign(exp);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ if (exp.e1.op == TOK.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray)
+ {
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+
+ if (exp.e1.op == TOK.slice)
+ (cast(SliceExp)exp.e1).arrayop = true;
+
+ // T[] op= ...
+ if (exp.e2.implicitConvTo(exp.e1.type.nextOf()))
+ {
+ // T[] op= T
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type.nextOf());
+ }
+ else if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ exp.type = exp.e1.type;
+ result = arrayOp(exp, sc);
+ return;
+ }
+
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true);
+ exp.type = exp.e1.type;
+
+ if (auto ad = isAggregate(exp.e1.type))
+ {
+ if (const s = search_function(ad, Id.opOpAssign))
+ {
+ error(exp.loc, "none of the `opOpAssign` overloads of `%s` are callable for `%s` of type `%s`", ad.toChars(), exp.e1.toChars(), exp.e1.type.toChars());
+ return setError();
+ }
+ }
+ if (exp.e1.checkScalar() ||
+ exp.e1.checkReadModifyWrite(exp.op, exp.e2) ||
+ exp.e1.checkSharedAccess(sc))
+ return setError();
+
+ int arith = (exp.op == TOK.addAssign || exp.op == TOK.minAssign || exp.op == TOK.mulAssign || exp.op == TOK.divAssign || exp.op == TOK.modAssign || exp.op == TOK.powAssign);
+ int bitwise = (exp.op == TOK.andAssign || exp.op == TOK.orAssign || exp.op == TOK.xorAssign);
+ int shift = (exp.op == TOK.leftShiftAssign || exp.op == TOK.rightShiftAssign || exp.op == TOK.unsignedRightShiftAssign);
+
+ if (bitwise && exp.type.toBasetype().ty == Tbool)
+ exp.e2 = exp.e2.implicitCastTo(sc, exp.type);
+ else if (exp.checkNoBool())
+ return setError();
+
+ if ((exp.op == TOK.addAssign || exp.op == TOK.minAssign) && exp.e1.type.toBasetype().ty == Tpointer && exp.e2.type.toBasetype().isintegral())
+ {
+ result = scaleFactor(exp, sc);
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ if (arith && (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)))
+ return setError();
+ if ((bitwise || shift) && (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)))
+ return setError();
+
+ if (shift)
+ {
+ if (exp.e2.type.toBasetype().ty != Tvector)
+ exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+ }
+
+ if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ if (exp.e1.op == TOK.error || exp.e2.op == TOK.error)
+ return setError();
+
+ e = exp.checkOpAssignTypes(sc);
+ if (e.op == TOK.error)
+ {
+ result = e;
+ return;
+ }
+
+ assert(e.op == TOK.assign || e == exp);
+ result = (cast(BinExp)e).reorderSettingAAElem(sc);
+ }
+
+ private Expression compileIt(MixinExp exp)
+ {
+ OutBuffer buf;
+ if (expressionsToString(buf, sc, exp.exps))
+ return null;
+
+ uint errors = global.errors;
+ const len = buf.length;
+ const str = buf.extractChars()[0 .. len];
+ scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false);
+ p.nextToken();
+ //printf("p.loc.linnum = %d\n", p.loc.linnum);
+
+ Expression e = p.parseExpression();
+ if (global.errors != errors)
+ return null;
+
+ if (p.token.value != TOK.endOfFile)
+ {
+ exp.error("incomplete mixin expression `%s`", str.ptr);
+ return null;
+ }
+ return e;
+ }
+
+ override void visit(MixinExp exp)
+ {
+ /* https://dlang.org/spec/expression.html#mixin_expressions
+ */
+
+ static if (LOGSEMANTIC)
+ {
+ printf("MixinExp::semantic('%s')\n", exp.toChars());
+ }
+
+ auto e = compileIt(exp);
+ if (!e)
+ return setError();
+ result = e.expressionSemantic(sc);
+ }
+
+ override void visit(ImportExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ImportExp::semantic('%s')\n", e.toChars());
+ }
+
+ auto se = semanticString(sc, e.e1, "file name argument");
+ if (!se)
+ return setError();
+ se = se.toUTF8(sc);
+
+ auto namez = se.toStringz().ptr;
+ if (!global.filePath)
+ {
+ e.error("need `-J` switch to import text file `%s`", namez);
+ return setError();
+ }
+
+ /* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
+ * ('Path Traversal') attacks.
+ * http://cwe.mitre.org/data/definitions/22.html
+ */
+
+ if (FileName.absolute(namez))
+ {
+ e.error("absolute path is not allowed in import expression: `%s`", se.toChars());
+ return setError();
+ }
+
+ auto idxReserved = FileName.findReservedChar(namez);
+ if (idxReserved != size_t.max)
+ {
+ e.error("`%s` is not a valid filename on this platform", se.toChars());
+ e.errorSupplemental("Character `'%c'` is reserved and cannot be used", namez[idxReserved]);
+ return setError();
+ }
+
+ if (FileName.refersToParentDir(namez))
+ {
+ e.error("path refers to parent (`..`) directory: `%s`", se.toChars());
+ return setError();
+ }
+
+ auto name = FileName.searchPath(global.filePath, namez, false);
+ if (!name)
+ {
+ e.error("file `%s` cannot be found or not in a path specified with `-J`", se.toChars());
+ e.errorSupplemental("Path(s) searched (as provided by `-J`):");
+ foreach (idx, path; *global.filePath)
+ {
+ const attr = FileName.exists(path);
+ const(char)* err = attr == 2 ? "" :
+ (attr == 1 ? " (not a directory)" : " (path not found)");
+ e.errorSupplemental("[%zu]: `%s`%s", idx, path, err);
+ }
+ return setError();
+ }
+
+ sc._module.contentImportedFiles.push(name);
+ if (global.params.verbose)
+ {
+ const slice = se.peekString();
+ message("file %.*s\t(%s)", cast(int)slice.length, slice.ptr, name);
+ }
+ if (global.params.moduleDeps !is null)
+ {
+ OutBuffer* ob = global.params.moduleDeps;
+ Module imod = sc._module;
+
+ if (!global.params.moduleDepsFile)
+ ob.writestring("depsFile ");
+ ob.writestring(imod.toPrettyChars());
+ ob.writestring(" (");
+ escapePath(ob, imod.srcfile.toChars());
+ ob.writestring(") : ");
+ if (global.params.moduleDepsFile)
+ ob.writestring("string : ");
+ ob.write(se.peekString());
+ ob.writestring(" (");
+ escapePath(ob, name);
+ ob.writestring(")");
+ ob.writenl();
+ }
+ if (global.params.emitMakeDeps)
+ {
+ global.params.makeDeps.push(name);
+ }
+
+ {
+ auto readResult = File.read(name);
+ if (!readResult.success)
+ {
+ e.error("cannot read file `%s`", name);
+ return setError();
+ }
+ else
+ {
+ // take ownership of buffer (probably leaking)
+ auto data = readResult.extractSlice();
+ se = new StringExp(e.loc, data);
+ }
+ }
+ result = se.expressionSemantic(sc);
+ }
+
+ override void visit(AssertExp exp)
+ {
+ // https://dlang.org/spec/expression.html#assert_expressions
+ static if (LOGSEMANTIC)
+ {
+ printf("AssertExp::semantic('%s')\n", exp.toChars());
+ }
+
+ const generateMsg = !exp.msg && global.params.checkAction == CHECKACTION.context && global.params.useAssert == CHECKENABLE.on;
+ Expression temporariesPrefix;
+
+ if (generateMsg)
+ // no message - use assert expression as msg
+ {
+ if (!verifyHookExist(exp.loc, *sc, Id._d_assert_fail, "generating assert messages"))
+ return setError();
+
+ /*
+ {
+ auto a = e1, b = e2;
+ assert(a == b, _d_assert_fail!"=="(a, b));
+ }()
+ */
+
+ /*
+ Stores the result of an operand expression into a temporary
+ if necessary, e.g. if it is an impure fuction call containing side
+ effects as in https://issues.dlang.org/show_bug.cgi?id=20114
+
+ Params:
+ op = an expression which may require a temporary (added to
+ `temporariesPrefix`: `auto tmp = op`) and will be replaced
+ by `tmp` if necessary
+
+ Returns: (possibly replaced) `op`
+ */
+ Expression maybePromoteToTmp(ref Expression op)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20989
+ // Flag that _d_assert_fail will never dereference `array.ptr` to avoid safety
+ // errors for `assert(!array.ptr)` => `_d_assert_fail!"!"(array.ptr)`
+ {
+ auto die = op.isDotIdExp();
+ if (die && die.ident == Id.ptr)
+ die.noderef = true;
+ }
+
+ op = op.expressionSemantic(sc);
+ op = resolveProperties(sc, op);
+
+ // Detect assert's using static operator overloads (e.g. `"var" in environment`)
+ if (auto te = op.isTypeExp())
+ {
+ // Replace the TypeExp with it's textual representation
+ // Including "..." in the error message isn't quite right but
+ // proper solutions require more drastic changes, e.g. directly
+ // using miniFormat and combine instead of calling _d_assert_fail
+ auto name = new StringExp(te.loc, te.toString());
+ return name.expressionSemantic(sc);
+ }
+
+ // Create a temporary for expressions with side effects
+ // Defensively assume that function calls may have side effects even
+ // though it's not detected by hasSideEffect (e.g. `debug puts("Hello")` )
+ // Rewriting CallExp's also avoids some issues with the inliner/debug generation
+ if (op.hasSideEffect(true))
+ {
+ // Don't create an invalid temporary for void-expressions
+ // Further semantic will issue an appropriate error
+ if (op.type.ty == Tvoid)
+ return op;
+
+ // https://issues.dlang.org/show_bug.cgi?id=21590
+ // Don't create unnecessary temporaries and detect `assert(a = b)`
+ if (op.isAssignExp() || op.isBinAssignExp())
+ {
+ auto left = (cast(BinExp) op).e1;
+
+ // Find leftmost expression to handle other rewrites,
+ // e.g. --(++a) => a += 1 -= 1
+ while (left.isAssignExp() || left.isBinAssignExp())
+ left = (cast(BinExp) left).e1;
+
+ // Only use the assignee if it's a variable and skip
+ // other lvalues (e.g. ref's returned by functions)
+ if (left.isVarExp())
+ return left;
+
+ // Sanity check that `op` can be converted to boolean
+ // But don't raise errors for assignments enclosed in another expression
+ if (op is exp.e1)
+ op.toBoolean(sc);
+ }
+
+ // Tuples with side-effects already receive a temporary during semantic
+ if (op.type.isTypeTuple())
+ {
+ auto te = op.isTupleExp();
+ assert(te);
+
+ // Create a new tuple without the associated temporary
+ auto res = new TupleExp(op.loc, te.exps);
+ return res.expressionSemantic(sc);
+ }
+
+ const stc = op.isLvalue() ? STC.ref_ : 0;
+ auto tmp = copyToTemp(stc, "__assertOp", op);
+ tmp.dsymbolSemantic(sc);
+
+ auto decl = new DeclarationExp(op.loc, tmp);
+ temporariesPrefix = Expression.combine(temporariesPrefix, decl);
+
+ op = new VarExp(op.loc, tmp);
+ op = op.expressionSemantic(sc);
+ }
+ return op;
+ }
+
+ // if the assert condition is a mixin expression, try to compile it
+ if (auto ce = exp.e1.isMixinExp())
+ {
+ if (auto e1 = compileIt(ce))
+ exp.e1 = e1;
+ }
+
+ Expressions* es;
+ Objects* tiargs;
+ Loc loc = exp.e1.loc;
+
+ const tok = exp.e1.op;
+ bool isEqualsCallExpression;
+ if (tok == TOK.call)
+ {
+ const callExp = cast(CallExp) exp.e1;
+
+ // https://issues.dlang.org/show_bug.cgi?id=20331
+ // callExp.f may be null if the assert contains a call to
+ // a function pointer or literal
+ if (const callExpFunc = callExp.f)
+ {
+ const callExpIdent = callExpFunc.ident;
+ isEqualsCallExpression = callExpIdent == Id.__equals ||
+ callExpIdent == Id.eq;
+ }
+ }
+ if (tok == TOK.equal || tok == TOK.notEqual ||
+ tok == TOK.lessThan || tok == TOK.greaterThan ||
+ tok == TOK.lessOrEqual || tok == TOK.greaterOrEqual ||
+ tok == TOK.identity || tok == TOK.notIdentity ||
+ tok == TOK.in_ ||
+ isEqualsCallExpression)
+ {
+ es = new Expressions(3);
+ tiargs = new Objects(1);
+
+ if (isEqualsCallExpression)
+ {
+ auto callExp = cast(CallExp) exp.e1;
+ auto args = callExp.arguments;
+
+ // structs with opEquals get rewritten to a DotVarExp:
+ // a.opEquals(b)
+ // https://issues.dlang.org/show_bug.cgi?id=20100
+ if (args.length == 1)
+ {
+ auto dv = callExp.e1.isDotVarExp();
+ assert(dv);
+
+ // runtime args
+ (*es)[1] = maybePromoteToTmp(dv.e1);
+ (*es)[2] = maybePromoteToTmp((*args)[0]);
+ }
+ else
+ {
+ // runtime args
+ (*es)[1] = maybePromoteToTmp((*args)[0]);
+ (*es)[2] = maybePromoteToTmp((*args)[1]);
+ }
+ }
+ else
+ {
+ auto binExp = cast(EqualExp) exp.e1;
+
+ // runtime args
+ (*es)[1] = maybePromoteToTmp(binExp.e1);
+ (*es)[2] = maybePromoteToTmp(binExp.e2);
+ }
+
+ // template args
+ Expression comp = new StringExp(loc, isEqualsCallExpression ? "==" : Token.toString(exp.e1.op));
+ comp = comp.expressionSemantic(sc);
+ (*es)[0] = comp;
+ (*tiargs)[0] = (*es)[1].type;
+ }
+
+ // Format exp.e1 before any additional boolean conversion
+ // Ignore &&/|| because "assert(...) failed" is more informative than "false != true"
+ else if (tok != TOK.andAnd && tok != TOK.orOr)
+ {
+ es = new Expressions(2);
+ tiargs = new Objects(1);
+
+ if (auto ne = exp.e1.isNotExp())
+ {
+ // Fetch the (potential non-bool) expression and fold
+ // (n) negations into (n % 2) negations, e.g. !!a => a
+ for (bool neg = true; ; neg = !neg)
+ {
+ if (auto ne2 = ne.e1.isNotExp())
+ ne = ne2;
+ else
+ {
+ (*es)[0] = new StringExp(loc, neg ? "!" : "");
+ (*es)[1] = maybePromoteToTmp(ne.e1);
+ break;
+ }
+ }
+ }
+ else
+ { // Simply format exp.e1
+ (*es)[0] = new StringExp(loc, "");
+ (*es)[1] = maybePromoteToTmp(exp.e1);
+ }
+
+ (*tiargs)[0] = (*es)[1].type;
+
+ // Passing __ctfe to auto ref infers ref and aborts compilation:
+ // "cannot modify compiler-generated variable __ctfe"
+ auto ve = (*es)[1].isVarExp();
+ if (ve && ve.var.ident == Id.ctfe)
+ {
+ exp.msg = new StringExp(loc, "assert(__ctfe) failed!");
+ goto LSkip;
+ }
+ }
+ else
+ {
+ OutBuffer buf;
+ buf.printf("%s failed", exp.toChars());
+ exp.msg = new StringExp(Loc.initial, buf.extractSlice());
+ goto LSkip;
+ }
+
+ Expression __assertFail = new IdentifierExp(exp.loc, Id.empty);
+ auto assertFail = new DotIdExp(loc, __assertFail, Id.object);
+
+ auto dt = new DotTemplateInstanceExp(loc, assertFail, Id._d_assert_fail, tiargs);
+ auto ec = CallExp.create(loc, dt, es);
+ exp.msg = ec;
+ }
+
+ LSkip:
+ if (Expression ex = unaSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ exp.e1 = resolveProperties(sc, exp.e1);
+ // BUG: see if we can do compile time elimination of the Assert
+ exp.e1 = exp.e1.optimize(WANTvalue);
+ exp.e1 = exp.e1.toBoolean(sc);
+
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+
+ if (exp.msg)
+ {
+ exp.msg = expressionSemantic(exp.msg, sc);
+ exp.msg = resolveProperties(sc, exp.msg);
+ exp.msg = exp.msg.implicitCastTo(sc, Type.tchar.constOf().arrayOf());
+ exp.msg = exp.msg.optimize(WANTvalue);
+ checkParamArgumentEscape(sc, null, null, exp.msg, true, false);
+ }
+
+ if (exp.msg && exp.msg.op == TOK.error)
+ {
+ result = exp.msg;
+ return;
+ }
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = exp.msg && checkNonAssignmentArrayOp(exp.msg);
+ if (f1 || f2)
+ return setError();
+
+ if (exp.e1.isBool(false))
+ {
+ /* This is an `assert(0)` which means halt program execution
+ */
+ FuncDeclaration fd = sc.parent.isFuncDeclaration();
+ if (fd)
+ fd.hasReturnExp |= 4;
+ sc.ctorflow.orCSX(CSX.halt);
+
+ if (global.params.useAssert == CHECKENABLE.off)
+ {
+ Expression e = new HaltExp(exp.loc);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ exp.type = Type.tnoreturn;
+ }
+ else
+ exp.type = Type.tvoid;
+
+ result = !temporariesPrefix
+ ? exp
+ : Expression.combine(temporariesPrefix, exp).expressionSemantic(sc);
+ }
+
+ override void visit(DotIdExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
+ //printf("e1.op = %d, '%s'\n", e1.op, Token::toChars(e1.op));
+ }
+ Expression e = exp.semanticY(sc, 1);
+ if (e && isDotOpDispatch(e))
+ {
+ uint errors = global.startGagging();
+ e = resolvePropertiesX(sc, e);
+ if (global.endGagging(errors))
+ e = null; /* fall down to UFCS */
+ else
+ {
+ result = e;
+ return;
+ }
+ }
+ if (!e) // if failed to find the property
+ {
+ /* If ident is not a valid property, rewrite:
+ * e1.ident
+ * as:
+ * .ident(e1)
+ */
+ e = resolveUFCSProperties(sc, exp);
+ }
+ result = e;
+ }
+
+ override void visit(DotTemplateExp e)
+ {
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+ if (Expression ex = unaSemantic(e, sc))
+ {
+ result = ex;
+ return;
+ }
+ // 'void' like TemplateExp
+ e.type = Type.tvoid;
+ result = e;
+ }
+
+ override void visit(DotVarExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotVarExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.var = exp.var.toAlias().isDeclaration();
+
+ exp.e1 = exp.e1.expressionSemantic(sc);
+
+ if (auto tup = exp.var.isTupleDeclaration())
+ {
+ /* Replace:
+ * e1.tuple(a, b, c)
+ * with:
+ * tuple(e1.a, e1.b, e1.c)
+ */
+ Expression e0;
+ Expression ev = sc.func ? extractSideEffect(sc, "__tup", e0, exp.e1) : exp.e1;
+
+ auto exps = new Expressions();
+ exps.reserve(tup.objects.dim);
+ for (size_t i = 0; i < tup.objects.dim; i++)
+ {
+ RootObject o = (*tup.objects)[i];
+ Expression e;
+ Declaration var;
+ if (o.dyncast() == DYNCAST.expression)
+ {
+ e = cast(Expression)o;
+ if (auto se = e.isDsymbolExp())
+ var = se.s.isDeclaration();
+ else if (auto ve = e.isVarExp())
+ if (!ve.var.isFuncDeclaration())
+ // Exempt functions for backwards compatibility reasons.
+ // See: https://issues.dlang.org/show_bug.cgi?id=20470#c1
+ var = ve.var;
+ }
+ else if (o.dyncast() == DYNCAST.dsymbol)
+ {
+ Dsymbol s = cast(Dsymbol) o;
+ Declaration d = s.isDeclaration();
+ if (!d || d.isFuncDeclaration())
+ // Exempt functions for backwards compatibility reasons.
+ // See: https://issues.dlang.org/show_bug.cgi?id=20470#c1
+ e = new DsymbolExp(exp.loc, s);
+ else
+ var = d;
+ }
+ else if (o.dyncast() == DYNCAST.type)
+ {
+ e = new TypeExp(exp.loc, cast(Type)o);
+ }
+ else
+ {
+ exp.error("`%s` is not an expression", o.toChars());
+ return setError();
+ }
+ if (var)
+ e = new DotVarExp(exp.loc, ev, var);
+ exps.push(e);
+ }
+
+ Expression e = new TupleExp(exp.loc, e0, exps);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ exp.e1 = exp.e1.addDtorHook(sc);
+
+ Type t1 = exp.e1.type;
+
+ if (FuncDeclaration fd = exp.var.isFuncDeclaration())
+ {
+ // for functions, do checks after overload resolution
+ if (!fd.functionSemantic())
+ return setError();
+
+ /* https://issues.dlang.org/show_bug.cgi?id=13843
+ * If fd obviously has no overloads, we should
+ * normalize AST, and it will give a chance to wrap fd with FuncExp.
+ */
+ if ((fd.isNested() && !fd.isThis()) || fd.isFuncLiteralDeclaration())
+ {
+ // (e1, fd)
+ auto e = symbolToExp(fd, exp.loc, sc, false);
+ result = Expression.combine(exp.e1, e);
+ return;
+ }
+
+ exp.type = fd.type;
+ assert(exp.type);
+ }
+ else if (OverDeclaration od = exp.var.isOverDeclaration())
+ {
+ exp.type = Type.tvoid; // ambiguous type?
+ }
+ else
+ {
+ exp.type = exp.var.type;
+ if (!exp.type && global.errors) // var is goofed up, just return error.
+ return setError();
+ assert(exp.type);
+
+ if (t1.ty == Tpointer)
+ t1 = t1.nextOf();
+
+ exp.type = exp.type.addMod(t1.mod);
+
+ Dsymbol vparent = exp.var.toParent();
+ AggregateDeclaration ad = vparent ? vparent.isAggregateDeclaration() : null;
+ if (Expression e1x = getRightThis(exp.loc, sc, ad, exp.e1, exp.var, 1))
+ exp.e1 = e1x;
+ else
+ {
+ /* Later checkRightThis will report correct error for invalid field variable access.
+ */
+ Expression e = new VarExp(exp.loc, exp.var);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ checkAccess(exp.loc, sc, exp.e1, exp.var);
+
+ VarDeclaration v = exp.var.isVarDeclaration();
+ if (v && (v.isDataseg() || (v.storage_class & STC.manifest)))
+ {
+ Expression e = expandVar(WANTvalue, v);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+
+ if (v && (v.isDataseg() || // fix https://issues.dlang.org/show_bug.cgi?id=8238
+ (!v.needThis() && v.semanticRun > PASS.init))) // fix https://issues.dlang.org/show_bug.cgi?id=17258
+ {
+ // (e1, v)
+ checkAccess(exp.loc, sc, exp.e1, v);
+ Expression e = new VarExp(exp.loc, v);
+ e = new CommaExp(exp.loc, exp.e1, e);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+ //printf("-DotVarExp::semantic('%s')\n", toChars());
+ result = exp;
+ }
+
+ override void visit(DotTemplateInstanceExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars());
+ }
+ // Indicate we need to resolve by UFCS.
+ Expression e = exp.semanticY(sc, 1);
+ if (!e)
+ e = resolveUFCSProperties(sc, exp);
+ result = e;
+ }
+
+ override void visit(DelegateExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DelegateExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ e.e1 = e.e1.expressionSemantic(sc);
+
+ e.type = new TypeDelegate(e.func.type.isTypeFunction());
+ e.type = e.type.typeSemantic(e.loc, sc);
+
+ FuncDeclaration f = e.func.toAliasFunc();
+ AggregateDeclaration ad = f.toParentLocal().isAggregateDeclaration();
+ if (f.needThis())
+ e.e1 = getRightThis(e.loc, sc, ad, e.e1, f);
+ if (e.e1.op == TOK.error)
+ return setError();
+
+ /* A delegate takes the address of e.e1 in order to set the .ptr field
+ * https://issues.dlang.org/show_bug.cgi?id=18575
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled && e.e1.type.toBasetype().ty == Tstruct)
+ {
+ if (auto v = expToVariable(e.e1))
+ {
+ if (!checkAddressVar(sc, e.e1, v))
+ return setError();
+ }
+ }
+
+ if (f.type.ty == Tfunction)
+ {
+ TypeFunction tf = cast(TypeFunction)f.type;
+ if (!MODmethodConv(e.e1.type.mod, f.type.mod))
+ {
+ OutBuffer thisBuf, funcBuf;
+ MODMatchToBuffer(&thisBuf, e.e1.type.mod, tf.mod);
+ MODMatchToBuffer(&funcBuf, tf.mod, e.e1.type.mod);
+ e.error("%smethod `%s` is not callable using a %s`%s`",
+ funcBuf.peekChars(), f.toPrettyChars(), thisBuf.peekChars(), e.e1.toChars());
+ return setError();
+ }
+ }
+ if (ad && ad.isClassDeclaration() && ad.type != e.e1.type)
+ {
+ // A downcast is required for interfaces
+ // https://issues.dlang.org/show_bug.cgi?id=3706
+ e.e1 = new CastExp(e.loc, e.e1, ad.type);
+ e.e1 = e.e1.expressionSemantic(sc);
+ }
+ result = e;
+ // declare dual-context container
+ if (f.isThis2 && !sc.intypeof && sc.func)
+ {
+ // check access to second `this`
+ if (AggregateDeclaration ad2 = f.isMember2())
+ {
+ Expression te = new ThisExp(e.loc).expressionSemantic(sc);
+ if (te.op != TOK.error)
+ te = getRightThis(e.loc, sc, ad2, te, f);
+ if (te.op == TOK.error)
+ {
+ e.error("need `this` of type `%s` to make delegate from function `%s`", ad2.toChars(), f.toChars());
+ return setError();
+ }
+ }
+ VarDeclaration vthis2 = makeThis2Argument(e.loc, sc, f);
+ e.vthis2 = vthis2;
+ Expression de = new DeclarationExp(e.loc, vthis2);
+ result = Expression.combine(de, result);
+ result = result.expressionSemantic(sc);
+ }
+ }
+
+ override void visit(DotTypeExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotTypeExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (auto e = unaSemantic(exp, sc))
+ {
+ result = e;
+ return;
+ }
+
+ exp.type = exp.sym.getType().addMod(exp.e1.type.mod);
+ result = exp;
+ }
+
+ override void visit(AddrExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("AddrExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = unaSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ if (sc.flags & SCOPE.Cfile)
+ {
+ /* Special handling for &"string"
+ * since C regards a string literal as an lvalue
+ */
+ if (auto se = exp.e1.isStringExp())
+ {
+ if (auto tp = se.type.toBasetype().isTypePointer())
+ {
+ /* Switch from pointer-to-char to pointer-to-static-array-of-char
+ */
+ auto ts = new TypeSArray(tp.nextOf(), new IntegerExp(Loc.initial, se.len + 1, Type.tsize_t));
+ se.type = typeSemantic(ts, Loc.initial, sc).pointerTo();
+ result = se;
+ return;
+ }
+ }
+ }
+
+ int wasCond = exp.e1.op == TOK.question;
+
+ if (exp.e1.op == TOK.dotTemplateInstance)
+ {
+ DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)exp.e1;
+ TemplateInstance ti = dti.ti;
+ {
+ //assert(ti.needsTypeInference(sc));
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors) // if template failed to expand
+ return setError();
+
+ Dsymbol s = ti.toAlias();
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ exp.e1 = new DotVarExp(exp.e1.loc, dti.e1, f);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ }
+ }
+ }
+ else if (exp.e1.op == TOK.scope_)
+ {
+ TemplateInstance ti = (cast(ScopeExp)exp.e1).sds.isTemplateInstance();
+ if (ti)
+ {
+ //assert(ti.needsTypeInference(sc));
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors) // if template failed to expand
+ return setError();
+
+ Dsymbol s = ti.toAlias();
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ exp.e1 = new VarExp(exp.e1.loc, f);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ }
+ }
+ }
+ /* https://issues.dlang.org/show_bug.cgi?id=809
+ *
+ * If the address of a lazy variable is taken,
+ * the expression is rewritten so that the type
+ * of it is the delegate type. This means that
+ * the symbol is not going to represent a call
+ * to the delegate anymore, but rather, the
+ * actual symbol.
+ */
+ if (auto ve = exp.e1.isVarExp())
+ {
+ if (ve.var.storage_class & STC.lazy_)
+ {
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.e1 = resolveProperties(sc, exp.e1);
+ if (auto callExp = exp.e1.isCallExp())
+ {
+ if (callExp.e1.type.toBasetype().ty == Tdelegate)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=20551
+ *
+ * Cannot take address of lazy parameter in @safe code
+ * because it might end up being a pointer to undefined
+ * memory.
+ */
+ if (sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("cannot take address of lazy parameter `%s` in `@safe` function `%s`",
+ ve.toChars(), sc.func.toChars());
+ setError();
+ }
+ else
+ {
+ VarExp ve2 = callExp.e1.isVarExp();
+ ve2.delegateWasExtracted = true;
+ ve2.var.storage_class |= STC.scope_;
+ result = ve2;
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ exp.e1 = exp.e1.toLvalue(sc, null);
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+
+ if (!exp.e1.type)
+ {
+ exp.error("cannot take address of `%s`", exp.e1.toChars());
+ return setError();
+ }
+
+ bool hasOverloads;
+ if (auto f = isFuncAddress(exp, &hasOverloads))
+ {
+ if (!hasOverloads && f.checkForwardRef(exp.loc))
+ return setError();
+ }
+ else if (!exp.e1.type.deco)
+ {
+ if (exp.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)exp.e1;
+ Declaration d = ve.var;
+ exp.error("forward reference to %s `%s`", d.kind(), d.toChars());
+ }
+ else
+ exp.error("forward reference to `%s`", exp.e1.toChars());
+ return setError();
+ }
+
+ exp.type = exp.e1.type.pointerTo();
+
+ // See if this should really be a delegate
+ if (exp.e1.op == TOK.dotVariable)
+ {
+ DotVarExp dve = cast(DotVarExp)exp.e1;
+ FuncDeclaration f = dve.var.isFuncDeclaration();
+ if (f)
+ {
+ f = f.toAliasFunc(); // FIXME, should see overloads
+ // https://issues.dlang.org/show_bug.cgi?id=1983
+ if (!dve.hasOverloads)
+ f.tookAddressOf++;
+
+ Expression e;
+ if (f.needThis())
+ e = new DelegateExp(exp.loc, dve.e1, f, dve.hasOverloads);
+ else // It is a function pointer. Convert &v.f() --> (v, &V.f())
+ e = new CommaExp(exp.loc, dve.e1, new AddrExp(exp.loc, new VarExp(exp.loc, f, dve.hasOverloads)));
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ // Look for misaligned pointer in @safe mode
+ if (checkUnsafeAccess(sc, dve, !exp.type.isMutable(), true))
+ return setError();
+
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (VarDeclaration v = expToVariable(dve.e1))
+ {
+ if (!checkAddressVar(sc, exp.e1, v))
+ return setError();
+ }
+ }
+ }
+ else if (exp.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)exp.e1;
+ VarDeclaration v = ve.var.isVarDeclaration();
+ if (v)
+ {
+ if (!checkAddressVar(sc, exp.e1, v))
+ return setError();
+
+ ve.checkPurity(sc, v);
+ }
+ FuncDeclaration f = ve.var.isFuncDeclaration();
+ if (f)
+ {
+ /* Because nested functions cannot be overloaded,
+ * mark here that we took its address because castTo()
+ * may not be called with an exact match.
+ */
+ if (!ve.hasOverloads || (f.isNested() && !f.needThis()))
+ f.tookAddressOf++;
+ if (f.isNested() && !f.needThis())
+ {
+ if (f.isFuncLiteralDeclaration())
+ {
+ if (!f.FuncDeclaration.isNested())
+ {
+ /* Supply a 'null' for a this pointer if no this is available
+ */
+ Expression e = new DelegateExp(exp.loc, new NullExp(exp.loc, Type.tnull), f, ve.hasOverloads);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+ Expression e = new DelegateExp(exp.loc, exp.e1, f, ve.hasOverloads);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ if (f.needThis())
+ {
+ if (hasThis(sc))
+ {
+ /* Should probably supply 'this' after overload resolution,
+ * not before.
+ */
+ Expression ethis = new ThisExp(exp.loc);
+ Expression e = new DelegateExp(exp.loc, ethis, f, ve.hasOverloads);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ if (sc.func && !sc.intypeof)
+ {
+ if (!(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("`this` reference necessary to take address of member `%s` in `@safe` function `%s`", f.toChars(), sc.func.toChars());
+ }
+ }
+ }
+ }
+ }
+ else if ((exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_) && global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (VarDeclaration v = expToVariable(exp.e1))
+ {
+ if (!checkAddressVar(sc, exp.e1, v))
+ return setError();
+ }
+ }
+ else if (exp.e1.op == TOK.call)
+ {
+ CallExp ce = cast(CallExp)exp.e1;
+ if (ce.e1.type.ty == Tfunction)
+ {
+ TypeFunction tf = cast(TypeFunction)ce.e1.type;
+ if (tf.isref && sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("cannot take address of `ref return` of `%s()` in `@safe` function `%s`",
+ ce.e1.toChars(), sc.func.toChars());
+ }
+ }
+ }
+ else if (exp.e1.op == TOK.index)
+ {
+ /* For:
+ * int[3] a;
+ * &a[i]
+ * check 'a' the same as for a regular variable
+ */
+ if (VarDeclaration v = expToVariable(exp.e1))
+ {
+ if (global.params.useDIP1000 == FeatureState.enabled && !checkAddressVar(sc, exp.e1, v))
+ return setError();
+
+ exp.e1.checkPurity(sc, v);
+ }
+ }
+ else if (wasCond)
+ {
+ /* a ? b : c was transformed to *(a ? &b : &c), but we still
+ * need to do safety checks
+ */
+ assert(exp.e1.op == TOK.star);
+ PtrExp pe = cast(PtrExp)exp.e1;
+ assert(pe.e1.op == TOK.question);
+ CondExp ce = cast(CondExp)pe.e1;
+ assert(ce.e1.op == TOK.address);
+ assert(ce.e2.op == TOK.address);
+
+ // Re-run semantic on the address expressions only
+ ce.e1.type = null;
+ ce.e1 = ce.e1.expressionSemantic(sc);
+ ce.e2.type = null;
+ ce.e2 = ce.e2.expressionSemantic(sc);
+ }
+ result = exp.optimize(WANTvalue);
+ }
+
+ override void visit(PtrExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("PtrExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb = exp.e1.type.toBasetype();
+ switch (tb.ty)
+ {
+ case Tpointer:
+ exp.type = (cast(TypePointer)tb).next;
+ break;
+
+ case Tsarray:
+ case Tarray:
+ if (isNonAssignmentArrayOp(exp.e1))
+ goto default;
+ exp.error("using `*` on an array is no longer supported; use `*(%s).ptr` instead", exp.e1.toChars());
+ exp.type = (cast(TypeArray)tb).next;
+ exp.e1 = exp.e1.castTo(sc, exp.type.pointerTo());
+ break;
+
+ case Terror:
+ return setError();
+
+ case Tnull:
+ exp.type = Type.tnoreturn; // typeof(*null) is bottom type
+ break;
+
+ default:
+ exp.error("can only `*` a pointer, not a `%s`", exp.e1.type.toChars());
+ goto case Terror;
+ }
+
+ if (exp.checkValue())
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(NegExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("NegExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ fix16997(sc, exp);
+ exp.type = exp.e1.type;
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp.e1))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.e1.checkNoBool())
+ return setError();
+ if (exp.e1.checkArithmetic() ||
+ exp.e1.checkSharedAccess(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(UAddExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("UAddExp::semantic('%s')\n", exp.toChars());
+ }
+ assert(!exp.type);
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ fix16997(sc, exp);
+ if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.e1.checkNoBool())
+ return setError();
+ if (exp.e1.checkArithmetic())
+ return setError();
+ if (exp.e1.checkSharedAccess(sc))
+ return setError();
+
+ result = exp.e1;
+ }
+
+ override void visit(ComExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ fix16997(sc, exp);
+ exp.type = exp.e1.type;
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp.e1))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.e1.checkNoBool())
+ return setError();
+ if (exp.e1.checkIntegral() ||
+ exp.e1.checkSharedAccess(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(NotExp e)
+ {
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ e.setNoderefOperand();
+
+ // Note there is no operator overload
+ if (Expression ex = unaSemantic(e, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e.e1.op == TOK.type)
+ e.e1 = resolveAliasThis(sc, e.e1);
+
+ e.e1 = resolveProperties(sc, e.e1);
+ e.e1 = e.e1.toBoolean(sc);
+ if (e.e1.type == Type.terror)
+ {
+ result = e.e1;
+ return;
+ }
+
+ if (!target.isVectorOpSupported(e.e1.type.toBasetype(), e.op))
+ {
+ result = e.incompatibleTypes();
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=13910
+ // Today NotExp can take an array as its operand.
+ if (checkNonAssignmentArrayOp(e.e1))
+ return setError();
+
+ e.type = Type.tbool;
+ result = e;
+ }
+
+ override void visit(DeleteExp exp)
+ {
+ if (!sc.isDeprecated)
+ {
+ // @@@DEPRECATED_2019-02@@@
+ // 1. Deprecation for 1 year
+ // 2. Error for 1 year
+ // 3. Removal of keyword, "delete" can be used for other identities
+ if (!exp.isRAII)
+ deprecation(exp.loc, "The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.");
+ }
+
+ if (Expression ex = unaSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ exp.e1 = resolveProperties(sc, exp.e1);
+ exp.e1 = exp.e1.modifiableLvalue(sc, null);
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ exp.type = Type.tvoid;
+
+ AggregateDeclaration ad = null;
+ Type tb = exp.e1.type.toBasetype();
+ switch (tb.ty)
+ {
+ case Tclass:
+ {
+ auto cd = (cast(TypeClass)tb).sym;
+ if (cd.isCOMinterface())
+ {
+ /* Because COM classes are deleted by IUnknown.Release()
+ */
+ exp.error("cannot `delete` instance of COM interface `%s`", cd.toChars());
+ return setError();
+ }
+ ad = cd;
+ break;
+ }
+ case Tpointer:
+ tb = (cast(TypePointer)tb).next.toBasetype();
+ if (tb.ty == Tstruct)
+ {
+ ad = (cast(TypeStruct)tb).sym;
+ semanticTypeInfo(sc, tb);
+ }
+ break;
+
+ case Tarray:
+ {
+ Type tv = tb.nextOf().baseElemOf();
+ if (tv.ty == Tstruct)
+ {
+ ad = (cast(TypeStruct)tv).sym;
+ if (ad.dtor)
+ semanticTypeInfo(sc, ad.type);
+ }
+ break;
+ }
+ default:
+ exp.error("cannot delete type `%s`", exp.e1.type.toChars());
+ return setError();
+ }
+
+ bool err = false;
+ if (ad)
+ {
+ if (ad.dtor)
+ {
+ err |= !ad.dtor.functionSemantic();
+ err |= exp.checkPurity(sc, ad.dtor);
+ err |= exp.checkSafety(sc, ad.dtor);
+ err |= exp.checkNogc(sc, ad.dtor);
+ }
+ if (err)
+ return setError();
+ }
+
+ if (!sc.intypeof && sc.func &&
+ !exp.isRAII &&
+ !(sc.flags & SCOPE.debug_) &&
+ sc.func.setUnsafe())
+ {
+ exp.error("`%s` is not `@safe` but is used in `@safe` function `%s`", exp.toChars(), sc.func.toChars());
+ err = true;
+ }
+ if (err)
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(CastExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CastExp::semantic('%s')\n", exp.toChars());
+ }
+ //static int x; assert(++x < 10);
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if ((sc && sc.flags & SCOPE.Cfile) &&
+ exp.to && exp.to.ty == Tident &&
+ (exp.e1.op == TOK.address || exp.e1.op == TOK.star ||
+ exp.e1.op == TOK.uadd || exp.e1.op == TOK.negate))
+ {
+ /* Ambiguous cases arise from CParser if type-name is just an identifier.
+ * ( identifier ) cast-expression
+ * If we determine that `identifier` is a variable, and cast-expression
+ * is one of the unary operators (& * + -), then rewrite this cast
+ * as a binary expression.
+ */
+ Loc loc = exp.loc;
+ Type t;
+ Expression e;
+ Dsymbol s;
+ exp.to.resolve(loc, sc, e, t, s);
+ if (e !is null)
+ {
+ if (auto ex = exp.e1.isAddrExp()) // (ident) &exp -> (ident & exp)
+ result = new AndExp(loc, e, ex.e1);
+ else if (auto ex = exp.e1.isPtrExp()) // (ident) *exp -> (ident * exp)
+ result = new MulExp(loc, e, ex.e1);
+ else if (auto ex = exp.e1.isUAddExp()) // (ident) +exp -> (ident + exp)
+ result = new AddExp(loc, e, ex.e1);
+ else if (auto ex = exp.e1.isNegExp()) // (ident) -exp -> (ident - exp)
+ result = new MinExp(loc, e, ex.e1);
+
+ assert(result);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ }
+
+ if (exp.to)
+ {
+ exp.to = exp.to.typeSemantic(exp.loc, sc);
+ if (exp.to == Type.terror)
+ return setError();
+
+ if (!exp.to.hasPointers())
+ exp.setNoderefOperand();
+
+ // When e1 is a template lambda, this cast may instantiate it with
+ // the type 'to'.
+ exp.e1 = inferType(exp.e1, exp.to);
+ }
+
+ if (auto e = unaSemantic(exp, sc))
+ {
+ result = e;
+ return;
+ }
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (exp.e1.op == TOK.type)
+ exp.e1 = resolveAliasThis(sc, exp.e1);
+
+ auto e1x = resolveProperties(sc, exp.e1);
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ if (e1x.checkType())
+ return setError();
+ exp.e1 = e1x;
+
+ if (!exp.e1.type)
+ {
+ exp.error("cannot cast `%s`", exp.e1.toChars());
+ return setError();
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19954
+ if (exp.e1.type.ty == Ttuple)
+ {
+ TupleExp te = exp.e1.isTupleExp();
+ if (te.exps.dim == 1)
+ exp.e1 = (*te.exps)[0];
+ }
+
+ // only allow S(x) rewrite if cast specified S explicitly.
+ // See https://issues.dlang.org/show_bug.cgi?id=18545
+ const bool allowImplicitConstruction = exp.to !is null;
+
+ if (!exp.to) // Handle cast(const) and cast(immutable), etc.
+ {
+ exp.to = exp.e1.type.castMod(exp.mod);
+ exp.to = exp.to.typeSemantic(exp.loc, sc);
+
+ if (exp.to == Type.terror)
+ return setError();
+ }
+
+ if (exp.to.ty == Ttuple)
+ {
+ exp.error("cannot cast `%s` to tuple type `%s`", exp.e1.toChars(), exp.to.toChars());
+ return setError();
+ }
+
+ // cast(void) is used to mark e1 as unused, so it is safe
+ if (exp.to.ty == Tvoid)
+ {
+ exp.type = exp.to;
+ result = exp;
+ return;
+ }
+
+ if (!exp.to.equals(exp.e1.type) && exp.mod == cast(ubyte)~0)
+ {
+ if (Expression e = exp.op_overload(sc))
+ {
+ result = e.implicitCastTo(sc, exp.to);
+ return;
+ }
+ }
+
+ Type t1b = exp.e1.type.toBasetype();
+ Type tob = exp.to.toBasetype();
+
+ if (allowImplicitConstruction && tob.ty == Tstruct && !tob.equals(t1b))
+ {
+ /* Look to replace:
+ * cast(S)t
+ * with:
+ * S(t)
+ */
+
+ // Rewrite as to.call(e1)
+ Expression e = new TypeExp(exp.loc, exp.to);
+ e = new CallExp(exp.loc, e, exp.e1);
+ e = e.trySemantic(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+
+ if (!t1b.equals(tob) && (t1b.ty == Tarray || t1b.ty == Tsarray))
+ {
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+ }
+
+ // Look for casting to a vector type
+ if (tob.ty == Tvector && t1b.ty != Tvector)
+ {
+ result = new VectorExp(exp.loc, exp.e1, exp.to);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+
+ Expression ex = exp.e1.castTo(sc, exp.to);
+ if (ex.op == TOK.error)
+ {
+ result = ex;
+ return;
+ }
+
+ // Check for unsafe casts
+ if (!sc.intypeof &&
+ !(sc.flags & SCOPE.debug_) &&
+ !isSafeCast(ex, t1b, tob) &&
+ (!sc.func && sc.stc & STC.safe || sc.func && sc.func.setUnsafe()))
+ {
+ exp.error("cast from `%s` to `%s` not allowed in safe code", exp.e1.type.toChars(), exp.to.toChars());
+ return setError();
+ }
+
+ // `object.__ArrayCast` is a rewrite of an old runtime hook `_d_arraycast`. `_d_arraycast` was not built
+ // to handle certain casts. Those casts which `object.__ArrayCast` does not support are filtered out.
+ // See `e2ir.toElemCast` for other types of casts. If `object.__ArrayCast` is improved to support more
+ // casts these conditions and potentially some logic in `e2ir.toElemCast` can be removed.
+ if (tob.ty == Tarray)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=19840
+ if (auto ad = isAggregate(t1b))
+ {
+ if (ad.aliasthis)
+ {
+ Expression e = resolveAliasThis(sc, exp.e1);
+ e = new CastExp(exp.loc, e, exp.to);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ }
+
+ if(t1b.ty == Tarray && exp.e1.op != TOK.arrayLiteral && (sc.flags & SCOPE.ctfe) == 0)
+ {
+ auto tFrom = t1b.nextOf();
+ auto tTo = tob.nextOf();
+
+ // https://issues.dlang.org/show_bug.cgi?id=20130
+ if (exp.e1.op != TOK.string_ || !ex.isStringExp)
+ {
+ const uint fromSize = cast(uint)tFrom.size();
+ const uint toSize = cast(uint)tTo.size();
+
+ // If array element sizes do not match, we must adjust the dimensions
+ if (fromSize != toSize)
+ {
+ if (!verifyHookExist(exp.loc, *sc, Id.__ArrayCast, "casting array of structs"))
+ return setError();
+
+ // A runtime check is needed in case arrays don't line up. That check should
+ // be done in the implementation of `object.__ArrayCast`
+ if (toSize == 0 || (fromSize % toSize) != 0)
+ {
+ // lower to `object.__ArrayCast!(TFrom, TTo)(from)`
+
+ // fully qualify as `object.__ArrayCast`
+ Expression id = new IdentifierExp(exp.loc, Id.empty);
+ auto dotid = new DotIdExp(exp.loc, id, Id.object);
+
+ auto tiargs = new Objects();
+ tiargs.push(tFrom);
+ tiargs.push(tTo);
+ auto dt = new DotTemplateInstanceExp(exp.loc, dotid, Id.__ArrayCast, tiargs);
+
+ auto arguments = new Expressions();
+ arguments.push(exp.e1);
+ Expression ce = new CallExp(exp.loc, dt, arguments);
+
+ result = expressionSemantic(ce, sc);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ if (sc && sc.flags & SCOPE.Cfile)
+ {
+ /* C11 6.5.4-5: A cast does not yield an lvalue.
+ * So ensure that castTo does not strip away the cast so that this
+ * can be enforced in other semantic visitor methods.
+ */
+ if (!ex.isCastExp())
+ {
+ ex = new CastExp(exp.loc, ex, exp.to);
+ ex.type = exp.to;
+ }
+ }
+ result = ex;
+ }
+
+ override void visit(VectorExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("VectorExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.type = exp.to.typeSemantic(exp.loc, sc);
+ if (exp.e1.op == TOK.error || exp.type.ty == Terror)
+ {
+ result = exp.e1;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ assert(tb.ty == Tvector);
+ TypeVector tv = cast(TypeVector)tb;
+ Type te = tv.elementType();
+ exp.dim = cast(int)(tv.size(exp.loc) / te.size(exp.loc));
+
+ bool checkElem(Expression elem)
+ {
+ if (elem.isConst() == 1)
+ return false;
+
+ exp.error("constant expression expected, not `%s`", elem.toChars());
+ return true;
+ }
+
+ exp.e1 = exp.e1.optimize(WANTvalue);
+ bool res;
+ if (exp.e1.op == TOK.arrayLiteral)
+ {
+ foreach (i; 0 .. exp.dim)
+ {
+ // Do not stop on first error - check all AST nodes even if error found
+ res |= checkElem(exp.e1.isArrayLiteralExp()[i]);
+ }
+ }
+ else if (exp.e1.type.ty == Tvoid)
+ checkElem(exp.e1);
+
+ result = res ? ErrorExp.get() : exp;
+ }
+
+ override void visit(VectorArrayExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("VectorArrayExp::semantic('%s')\n", e.toChars());
+ }
+ if (!e.type)
+ {
+ unaSemantic(e, sc);
+ e.e1 = resolveProperties(sc, e.e1);
+
+ if (e.e1.op == TOK.error)
+ {
+ result = e.e1;
+ return;
+ }
+ assert(e.e1.type.ty == Tvector);
+ e.type = e.e1.type.isTypeVector().basetype;
+ }
+ result = e;
+ }
+
+ override void visit(SliceExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("SliceExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ // operator overloading should be handled in ArrayExp already.
+ if (Expression ex = unaSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ exp.e1 = resolveProperties(sc, exp.e1);
+ if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+ {
+ if (exp.lwr || exp.upr)
+ {
+ exp.error("cannot slice type `%s`", exp.e1.toChars());
+ return setError();
+ }
+ Expression e = new TypeExp(exp.loc, exp.e1.type.arrayOf());
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (!exp.lwr && !exp.upr)
+ {
+ if (exp.e1.op == TOK.arrayLiteral)
+ {
+ // Convert [a,b,c][] to [a,b,c]
+ Type t1b = exp.e1.type.toBasetype();
+ Expression e = exp.e1;
+ if (t1b.ty == Tsarray)
+ {
+ e = e.copy();
+ e.type = t1b.nextOf().arrayOf();
+ }
+ result = e;
+ return;
+ }
+ if (exp.e1.op == TOK.slice)
+ {
+ // Convert e[][] to e[]
+ SliceExp se = cast(SliceExp)exp.e1;
+ if (!se.lwr && !se.upr)
+ {
+ result = se;
+ return;
+ }
+ }
+ if (isArrayOpOperand(exp.e1))
+ {
+ // Convert (a[]+b[])[] to a[]+b[]
+ result = exp.e1;
+ return;
+ }
+ }
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (exp.e1.type.ty == Terror)
+ return setError();
+
+ Type t1b = exp.e1.type.toBasetype();
+ if (t1b.ty == Tpointer)
+ {
+ if (t1b.isPtrToFunction())
+ {
+ exp.error("cannot slice function pointer `%s`", exp.e1.toChars());
+ return setError();
+ }
+ if (!exp.lwr || !exp.upr)
+ {
+ exp.error("need upper and lower bound to slice pointer");
+ return setError();
+ }
+ if (sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("pointer slicing not allowed in safe functions");
+ return setError();
+ }
+ }
+ else if (t1b.ty == Tarray)
+ {
+ }
+ else if (t1b.ty == Tsarray)
+ {
+ if (!exp.arrayop && global.params.useDIP1000 == FeatureState.enabled)
+ {
+ /* Slicing a static array is like taking the address of it.
+ * Perform checks as if e[] was &e
+ */
+ if (VarDeclaration v = expToVariable(exp.e1))
+ {
+ if (exp.e1.op == TOK.dotVariable)
+ {
+ DotVarExp dve = cast(DotVarExp)exp.e1;
+ if ((dve.e1.op == TOK.this_ || dve.e1.op == TOK.super_) &&
+ !(v.storage_class & STC.ref_))
+ {
+ // because it's a class
+ v = null;
+ }
+ }
+
+ if (v && !checkAddressVar(sc, exp.e1, v))
+ return setError();
+ }
+ }
+ }
+ else if (t1b.ty == Ttuple)
+ {
+ if (!exp.lwr && !exp.upr)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (!exp.lwr || !exp.upr)
+ {
+ exp.error("need upper and lower bound to slice tuple");
+ return setError();
+ }
+ }
+ else if (t1b.ty == Tvector)
+ {
+ // Convert e1 to corresponding static array
+ TypeVector tv1 = cast(TypeVector)t1b;
+ t1b = tv1.basetype;
+ t1b = t1b.castMod(tv1.mod);
+ exp.e1.type = t1b;
+ }
+ else
+ {
+ exp.error("`%s` cannot be sliced with `[]`", t1b.ty == Tvoid ? exp.e1.toChars() : t1b.toChars());
+ return setError();
+ }
+
+ /* Run semantic on lwr and upr.
+ */
+ Scope* scx = sc;
+ if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple)
+ {
+ // Create scope for 'length' variable
+ ScopeDsymbol sym = new ArrayScopeSymbol(sc, exp);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+ }
+ if (exp.lwr)
+ {
+ if (t1b.ty == Ttuple)
+ sc = sc.startCTFE();
+ exp.lwr = exp.lwr.expressionSemantic(sc);
+ exp.lwr = resolveProperties(sc, exp.lwr);
+ if (t1b.ty == Ttuple)
+ sc = sc.endCTFE();
+ exp.lwr = exp.lwr.implicitCastTo(sc, Type.tsize_t);
+ }
+ if (exp.upr)
+ {
+ if (t1b.ty == Ttuple)
+ sc = sc.startCTFE();
+ exp.upr = exp.upr.expressionSemantic(sc);
+ exp.upr = resolveProperties(sc, exp.upr);
+ if (t1b.ty == Ttuple)
+ sc = sc.endCTFE();
+ exp.upr = exp.upr.implicitCastTo(sc, Type.tsize_t);
+ }
+ if (sc != scx)
+ sc = sc.pop();
+ if (exp.lwr && exp.lwr.type == Type.terror || exp.upr && exp.upr.type == Type.terror)
+ return setError();
+
+ if (t1b.ty == Ttuple)
+ {
+ exp.lwr = exp.lwr.ctfeInterpret();
+ exp.upr = exp.upr.ctfeInterpret();
+ uinteger_t i1 = exp.lwr.toUInteger();
+ uinteger_t i2 = exp.upr.toUInteger();
+
+ TupleExp te;
+ TypeTuple tup;
+ size_t length;
+ if (exp.e1.op == TOK.tuple) // slicing an expression tuple
+ {
+ te = cast(TupleExp)exp.e1;
+ tup = null;
+ length = te.exps.dim;
+ }
+ else if (exp.e1.op == TOK.type) // slicing a type tuple
+ {
+ te = null;
+ tup = cast(TypeTuple)t1b;
+ length = Parameter.dim(tup.arguments);
+ }
+ else
+ assert(0);
+
+ if (i2 < i1 || length < i2)
+ {
+ exp.error("string slice `[%llu .. %llu]` is out of bounds", i1, i2);
+ return setError();
+ }
+
+ size_t j1 = cast(size_t)i1;
+ size_t j2 = cast(size_t)i2;
+ Expression e;
+ if (exp.e1.op == TOK.tuple)
+ {
+ auto exps = new Expressions(j2 - j1);
+ for (size_t i = 0; i < j2 - j1; i++)
+ {
+ (*exps)[i] = (*te.exps)[j1 + i];
+ }
+ e = new TupleExp(exp.loc, te.e0, exps);
+ }
+ else
+ {
+ auto args = new Parameters();
+ args.reserve(j2 - j1);
+ for (size_t i = j1; i < j2; i++)
+ {
+ Parameter arg = Parameter.getNth(tup.arguments, i);
+ args.push(arg);
+ }
+ e = new TypeExp(exp.e1.loc, new TypeTuple(args));
+ }
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ exp.type = t1b.nextOf().arrayOf();
+ // Allow typedef[] -> typedef[]
+ if (exp.type.equals(t1b))
+ exp.type = exp.e1.type;
+
+ // We might know $ now
+ setLengthVarIfKnown(exp.lengthVar, t1b);
+
+ if (exp.lwr && exp.upr)
+ {
+ exp.lwr = exp.lwr.optimize(WANTvalue);
+ exp.upr = exp.upr.optimize(WANTvalue);
+
+ IntRange lwrRange = getIntRange(exp.lwr);
+ IntRange uprRange = getIntRange(exp.upr);
+
+ if (t1b.ty == Tsarray || t1b.ty == Tarray)
+ {
+ Expression el = new ArrayLengthExp(exp.loc, exp.e1);
+ el = el.expressionSemantic(sc);
+ el = el.optimize(WANTvalue);
+ if (el.op == TOK.int64)
+ {
+ // Array length is known at compile-time. Upper is in bounds if it fits length.
+ dinteger_t length = el.toInteger();
+ auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length));
+ exp.upperIsInBounds = bounds.contains(uprRange);
+ }
+ else if (exp.upr.op == TOK.int64 && exp.upr.toInteger() == 0)
+ {
+ // Upper slice expression is '0'. Value is always in bounds.
+ exp.upperIsInBounds = true;
+ }
+ else if (exp.upr.op == TOK.variable && (cast(VarExp)exp.upr).var.ident == Id.dollar)
+ {
+ // Upper slice expression is '$'. Value is always in bounds.
+ exp.upperIsInBounds = true;
+ }
+ }
+ else if (t1b.ty == Tpointer)
+ {
+ exp.upperIsInBounds = true;
+ }
+ else
+ assert(0);
+
+ exp.lowerIsLessThanUpper = (lwrRange.imax <= uprRange.imin);
+
+ //printf("upperIsInBounds = %d lowerIsLessThanUpper = %d\n", exp.upperIsInBounds, exp.lowerIsLessThanUpper);
+ }
+
+ result = exp;
+ }
+
+ override void visit(ArrayLengthExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ArrayLengthExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = unaSemantic(e, sc))
+ {
+ result = ex;
+ return;
+ }
+ e.e1 = resolveProperties(sc, e.e1);
+
+ e.type = Type.tsize_t;
+ result = e;
+ }
+
+ override void visit(ArrayExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ArrayExp::semantic('%s')\n", exp.toChars());
+ }
+ assert(!exp.type);
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (isAggregate(exp.e1.type))
+ exp.error("no `[]` operator overload for type `%s`", exp.e1.type.toChars());
+ else if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+ exp.error("static array of `%s` with multiple lengths not allowed", exp.e1.type.toChars());
+ else if (isIndexableNonAggregate(exp.e1.type))
+ exp.error("only one index allowed to index `%s`", exp.e1.type.toChars());
+ else
+ exp.error("cannot use `[]` operator on expression of type `%s`", exp.e1.type.toChars());
+
+ result = ErrorExp.get();
+ }
+
+ override void visit(DotExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotExp::semantic('%s')\n", exp.toChars());
+ if (exp.type)
+ printf("\ttype = %s\n", exp.type.toChars());
+ }
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.e2 = exp.e2.expressionSemantic(sc);
+
+ if (exp.e1.op == TOK.type)
+ {
+ result = exp.e2;
+ return;
+ }
+ if (exp.e2.op == TOK.type)
+ {
+ result = exp.e2;
+ return;
+ }
+ if (exp.e2.op == TOK.template_)
+ {
+ auto td = (cast(TemplateExp)exp.e2).td;
+ Expression e = new DotTemplateExp(exp.loc, exp.e1, td);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (!exp.type)
+ exp.type = exp.e2.type;
+ result = exp;
+ }
+
+ override void visit(CommaExp e)
+ {
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ // Allow `((a,b),(x,y))`
+ if (e.allowCommaExp)
+ {
+ CommaExp.allow(e.e1);
+ CommaExp.allow(e.e2);
+ }
+
+ if (Expression ex = binSemanticProp(e, sc))
+ {
+ result = ex;
+ return;
+ }
+ e.e1 = e.e1.addDtorHook(sc);
+
+ if (checkNonAssignmentArrayOp(e.e1))
+ return setError();
+
+ e.type = e.e2.type;
+ if (e.type is Type.tvoid)
+ discardValue(e.e1);
+ else if (!e.allowCommaExp && !e.isGenerated)
+ e.error("Using the result of a comma expression is not allowed");
+ result = e;
+ }
+
+ override void visit(IntervalExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("IntervalExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ Expression le = e.lwr;
+ le = le.expressionSemantic(sc);
+ le = resolveProperties(sc, le);
+
+ Expression ue = e.upr;
+ ue = ue.expressionSemantic(sc);
+ ue = resolveProperties(sc, ue);
+
+ if (le.op == TOK.error)
+ {
+ result = le;
+ return;
+ }
+ if (ue.op == TOK.error)
+ {
+ result = ue;
+ return;
+ }
+
+ e.lwr = le;
+ e.upr = ue;
+
+ e.type = Type.tvoid;
+ result = e;
+ }
+
+ override void visit(DelegatePtrExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DelegatePtrExp::semantic('%s')\n", e.toChars());
+ }
+ if (!e.type)
+ {
+ unaSemantic(e, sc);
+ e.e1 = resolveProperties(sc, e.e1);
+
+ if (e.e1.op == TOK.error)
+ {
+ result = e.e1;
+ return;
+ }
+ e.type = Type.tvoidptr;
+ }
+ result = e;
+ }
+
+ override void visit(DelegateFuncptrExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DelegateFuncptrExp::semantic('%s')\n", e.toChars());
+ }
+ if (!e.type)
+ {
+ unaSemantic(e, sc);
+ e.e1 = resolveProperties(sc, e.e1);
+ if (e.e1.op == TOK.error)
+ {
+ result = e.e1;
+ return;
+ }
+ e.type = e.e1.type.nextOf().pointerTo();
+ }
+ result = e;
+ }
+
+ override void visit(IndexExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("IndexExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ // operator overloading should be handled in ArrayExp already.
+ if (!exp.e1.type)
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ assert(exp.e1.type); // semantic() should already be run on it
+ if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+ {
+ exp.e2 = exp.e2.expressionSemantic(sc);
+ exp.e2 = resolveProperties(sc, exp.e2);
+ Type nt;
+ if (exp.e2.op == TOK.type)
+ nt = new TypeAArray(exp.e1.type, exp.e2.type);
+ else
+ nt = new TypeSArray(exp.e1.type, exp.e2);
+ Expression e = new TypeExp(exp.loc, nt);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (exp.e1.type.ty == Terror)
+ return setError();
+
+ // Note that unlike C we do not implement the int[ptr]
+
+ Type t1b = exp.e1.type.toBasetype();
+
+ if (t1b.ty == Tvector)
+ {
+ // Convert e1 to corresponding static array
+ TypeVector tv1 = cast(TypeVector)t1b;
+ t1b = tv1.basetype;
+ t1b = t1b.castMod(tv1.mod);
+ exp.e1.type = t1b;
+ }
+
+ /* Run semantic on e2
+ */
+ Scope* scx = sc;
+ if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple)
+ {
+ // Create scope for 'length' variable
+ ScopeDsymbol sym = new ArrayScopeSymbol(sc, exp);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+ }
+ if (t1b.ty == Ttuple)
+ sc = sc.startCTFE();
+ exp.e2 = exp.e2.expressionSemantic(sc);
+ exp.e2 = resolveProperties(sc, exp.e2);
+ if (t1b.ty == Ttuple)
+ sc = sc.endCTFE();
+ if (exp.e2.op == TOK.tuple)
+ {
+ TupleExp te = cast(TupleExp)exp.e2;
+ if (te.exps && te.exps.dim == 1)
+ exp.e2 = Expression.combine(te.e0, (*te.exps)[0]); // bug 4444 fix
+ }
+ if (sc != scx)
+ sc = sc.pop();
+ if (exp.e2.type == Type.terror)
+ return setError();
+
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+
+ switch (t1b.ty)
+ {
+ case Tpointer:
+ if (t1b.isPtrToFunction())
+ {
+ exp.error("cannot index function pointer `%s`", exp.e1.toChars());
+ return setError();
+ }
+ exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t);
+ if (exp.e2.type == Type.terror)
+ return setError();
+ exp.e2 = exp.e2.optimize(WANTvalue);
+ if (exp.e2.op == TOK.int64 && exp.e2.toInteger() == 0)
+ {
+ }
+ else if (sc.func && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("safe function `%s` cannot index pointer `%s`", sc.func.toPrettyChars(), exp.e1.toChars());
+ return setError();
+ }
+ exp.type = (cast(TypeNext)t1b).next;
+ break;
+
+ case Tarray:
+ exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t);
+ if (exp.e2.type == Type.terror)
+ return setError();
+ exp.type = (cast(TypeNext)t1b).next;
+ break;
+
+ case Tsarray:
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t);
+ if (exp.e2.type == Type.terror)
+ return setError();
+ exp.type = t1b.nextOf();
+ break;
+ }
+ case Taarray:
+ {
+ TypeAArray taa = cast(TypeAArray)t1b;
+ /* We can skip the implicit conversion if they differ only by
+ * constness
+ * https://issues.dlang.org/show_bug.cgi?id=2684
+ * see also bug https://issues.dlang.org/show_bug.cgi?id=2954 b
+ */
+ if (!arrayTypeCompatibleWithoutCasting(exp.e2.type, taa.index))
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, taa.index); // type checking
+ if (exp.e2.type == Type.terror)
+ return setError();
+ }
+
+ semanticTypeInfo(sc, taa);
+
+ exp.type = taa.next;
+ break;
+ }
+ case Ttuple:
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t);
+ if (exp.e2.type == Type.terror)
+ return setError();
+
+ exp.e2 = exp.e2.ctfeInterpret();
+ uinteger_t index = exp.e2.toUInteger();
+
+ TupleExp te;
+ TypeTuple tup;
+ size_t length;
+ if (exp.e1.op == TOK.tuple)
+ {
+ te = cast(TupleExp)exp.e1;
+ tup = null;
+ length = te.exps.dim;
+ }
+ else if (exp.e1.op == TOK.type)
+ {
+ te = null;
+ tup = cast(TypeTuple)t1b;
+ length = Parameter.dim(tup.arguments);
+ }
+ else
+ assert(0);
+
+ if (length <= index)
+ {
+ exp.error("array index `[%llu]` is outside array bounds `[0 .. %llu]`", index, cast(ulong)length);
+ return setError();
+ }
+ Expression e;
+ if (exp.e1.op == TOK.tuple)
+ {
+ e = (*te.exps)[cast(size_t)index];
+ e = Expression.combine(te.e0, e);
+ }
+ else
+ e = new TypeExp(exp.e1.loc, Parameter.getNth(tup.arguments, cast(size_t)index).type);
+ result = e;
+ return;
+ }
+ default:
+ exp.error("`%s` must be an array or pointer type, not `%s`", exp.e1.toChars(), exp.e1.type.toChars());
+ return setError();
+ }
+
+ // We might know $ now
+ setLengthVarIfKnown(exp.lengthVar, t1b);
+
+ if (t1b.ty == Tsarray || t1b.ty == Tarray)
+ {
+ Expression el = new ArrayLengthExp(exp.loc, exp.e1);
+ el = el.expressionSemantic(sc);
+ el = el.optimize(WANTvalue);
+ if (el.op == TOK.int64)
+ {
+ exp.e2 = exp.e2.optimize(WANTvalue);
+ dinteger_t length = el.toInteger();
+ if (length)
+ {
+ auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length - 1));
+ exp.indexIsInBounds = bounds.contains(getIntRange(exp.e2));
+ }
+ }
+ }
+
+ result = exp;
+ }
+
+ override void visit(PostExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("PostExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e1x = resolveProperties(sc, exp.e1);
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ exp.e1 = e1x;
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.checkReadModifyWrite(exp.op))
+ return setError();
+
+ if (exp.e1.op == TOK.slice)
+ {
+ const(char)* s = exp.op == TOK.plusPlus ? "increment" : "decrement";
+ exp.error("cannot post-%s array slice `%s`, use pre-%s instead", s, exp.e1.toChars(), s);
+ return setError();
+ }
+
+ Type t1 = exp.e1.type.toBasetype();
+ if (t1.ty == Tclass || t1.ty == Tstruct || exp.e1.op == TOK.arrayLength)
+ {
+ /* Check for operator overloading,
+ * but rewrite in terms of ++e instead of e++
+ */
+
+ /* If e1 is not trivial, take a reference to it
+ */
+ Expression de = null;
+ if (exp.e1.op != TOK.variable && exp.e1.op != TOK.arrayLength)
+ {
+ // ref v = e1;
+ auto v = copyToTemp(STC.ref_, "__postref", exp.e1);
+ de = new DeclarationExp(exp.loc, v);
+ exp.e1 = new VarExp(exp.e1.loc, v);
+ }
+
+ /* Rewrite as:
+ * auto tmp = e1; ++e1; tmp
+ */
+ auto tmp = copyToTemp(0, "__pitmp", exp.e1);
+ Expression ea = new DeclarationExp(exp.loc, tmp);
+
+ Expression eb = exp.e1.syntaxCopy();
+ eb = new PreExp(exp.op == TOK.plusPlus ? TOK.prePlusPlus : TOK.preMinusMinus, exp.loc, eb);
+
+ Expression ec = new VarExp(exp.loc, tmp);
+
+ // Combine de,ea,eb,ec
+ if (de)
+ ea = new CommaExp(exp.loc, de, ea);
+ e = new CommaExp(exp.loc, ea, eb);
+ e = new CommaExp(exp.loc, e, ec);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true);
+
+ e = exp;
+ if (exp.e1.checkScalar() ||
+ exp.e1.checkSharedAccess(sc))
+ return setError();
+ if (exp.e1.checkNoBool())
+ return setError();
+
+ if (exp.e1.type.ty == Tpointer)
+ e = scaleFactor(exp, sc);
+ else
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type);
+ e.type = exp.e1.type;
+ result = e;
+ }
+
+ override void visit(PreExp exp)
+ {
+ Expression e = exp.op_overload(sc);
+ // printf("PreExp::semantic('%s')\n", toChars());
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ // Rewrite as e1+=1 or e1-=1
+ if (exp.op == TOK.prePlusPlus)
+ e = new AddAssignExp(exp.loc, exp.e1, IntegerExp.literal!1);
+ else
+ e = new MinAssignExp(exp.loc, exp.e1, IntegerExp.literal!1);
+ result = e.expressionSemantic(sc);
+ }
+
+ /*
+ * Get the expression initializer for a specific struct
+ *
+ * Params:
+ * sd = the struct for which the expression initializer is needed
+ * loc = the location of the initializer
+ * sc = the scope where the expression is located
+ * t = the type of the expression
+ *
+ * Returns:
+ * The expression initializer or error expression if any errors occured
+ */
+ private Expression getInitExp(StructDeclaration sd, Loc loc, Scope* sc, Type t)
+ {
+ if (sd.zeroInit && !sd.isNested())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14606
+ // Always use BlitExp for the special expression: (struct = 0)
+ return IntegerExp.literal!0;
+ }
+
+ if (sd.isNested())
+ {
+ auto sle = new StructLiteralExp(loc, sd, null, t);
+ if (!sd.fill(loc, sle.elements, true))
+ return ErrorExp.get();
+ if (checkFrameAccess(loc, sc, sd, sle.elements.dim))
+ return ErrorExp.get();
+
+ sle.type = t;
+ return sle;
+ }
+
+ return t.defaultInit(loc);
+ }
+
+ override void visit(AssignExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("AssignExp::semantic('%s')\n", exp.toChars());
+ }
+ //printf("exp.e1.op = %d, '%s'\n", exp.e1.op, Token.toChars(exp.e1.op));
+ //printf("exp.e2.op = %d, '%s'\n", exp.e2.op, Token.toChars(exp.e2.op));
+
+ void setResult(Expression e, int line = __LINE__)
+ {
+ //printf("line %d\n", line);
+ result = e;
+ }
+
+ if (exp.type)
+ {
+ return setResult(exp);
+ }
+
+ Expression e1old = exp.e1;
+
+ if (auto e2comma = exp.e2.isCommaExp())
+ {
+ if (!e2comma.isGenerated)
+ exp.error("Using the result of a comma expression is not allowed");
+
+ /* Rewrite to get rid of the comma from rvalue
+ * e1=(e0,e2) => e0,(e1=e2)
+ */
+ Expression e0;
+ exp.e2 = Expression.extractLast(e2comma, e0);
+ Expression e = Expression.combine(e0, exp);
+ return setResult(e.expressionSemantic(sc));
+ }
+
+ /* Look for operator overloading of a[arguments] = e2.
+ * Do it before e1.expressionSemantic() otherwise the ArrayExp will have been
+ * converted to unary operator overloading already.
+ */
+ if (auto ae = exp.e1.isArrayExp())
+ {
+ Expression res;
+
+ ae.e1 = ae.e1.expressionSemantic(sc);
+ ae.e1 = resolveProperties(sc, ae.e1);
+ Expression ae1old = ae.e1;
+
+ const(bool) maybeSlice =
+ (ae.arguments.dim == 0 ||
+ ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
+
+ IntervalExp ie = null;
+ if (maybeSlice && ae.arguments.dim)
+ {
+ assert((*ae.arguments)[0].op == TOK.interval);
+ ie = cast(IntervalExp)(*ae.arguments)[0];
+ }
+ while (true)
+ {
+ if (ae.e1.op == TOK.error)
+ return setResult(ae.e1);
+
+ Expression e0 = null;
+ Expression ae1save = ae.e1;
+ ae.lengthVar = null;
+
+ Type t1b = ae.e1.type.toBasetype();
+ AggregateDeclaration ad = isAggregate(t1b);
+ if (!ad)
+ break;
+ if (search_function(ad, Id.indexass))
+ {
+ // Deal with $
+ res = resolveOpDollar(sc, ae, &e0);
+ if (!res) // a[i..j] = e2 might be: a.opSliceAssign(e2, i, j)
+ goto Lfallback;
+ if (res.op == TOK.error)
+ return setResult(res);
+
+ res = exp.e2.expressionSemantic(sc);
+ if (res.op == TOK.error)
+ return setResult(res);
+ exp.e2 = res;
+
+ /* Rewrite (a[arguments] = e2) as:
+ * a.opIndexAssign(e2, arguments)
+ */
+ Expressions* a = ae.arguments.copy();
+ a.insert(0, exp.e2);
+ res = new DotIdExp(exp.loc, ae.e1, Id.indexass);
+ res = new CallExp(exp.loc, res, a);
+ if (maybeSlice) // a[] = e2 might be: a.opSliceAssign(e2)
+ res = res.trySemantic(sc);
+ else
+ res = res.expressionSemantic(sc);
+ if (res)
+ return setResult(Expression.combine(e0, res));
+ }
+
+ Lfallback:
+ if (maybeSlice && search_function(ad, Id.sliceass))
+ {
+ // Deal with $
+ res = resolveOpDollar(sc, ae, ie, &e0);
+ if (res.op == TOK.error)
+ return setResult(res);
+
+ res = exp.e2.expressionSemantic(sc);
+ if (res.op == TOK.error)
+ return setResult(res);
+
+ exp.e2 = res;
+
+ /* Rewrite (a[i..j] = e2) as:
+ * a.opSliceAssign(e2, i, j)
+ */
+ auto a = new Expressions();
+ a.push(exp.e2);
+ if (ie)
+ {
+ a.push(ie.lwr);
+ a.push(ie.upr);
+ }
+ res = new DotIdExp(exp.loc, ae.e1, Id.sliceass);
+ res = new CallExp(exp.loc, res, a);
+ res = res.expressionSemantic(sc);
+ return setResult(Expression.combine(e0, res));
+ }
+
+ // No operator overloading member function found yet, but
+ // there might be an alias this to try.
+ if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+ {
+ /* Rewrite (a[arguments] op e2) as:
+ * a.aliasthis[arguments] op e2
+ */
+ ae.e1 = resolveAliasThis(sc, ae1save, true);
+ if (ae.e1)
+ continue;
+ }
+ break;
+ }
+ ae.e1 = ae1old; // recovery
+ ae.lengthVar = null;
+ }
+
+ /* Run this.e1 semantic.
+ */
+ {
+ Expression e1x = exp.e1;
+
+ /* With UFCS, e.f = value
+ * Could mean:
+ * .f(e, value)
+ * or:
+ * .f(e) = value
+ */
+ if (auto dti = e1x.isDotTemplateInstanceExp())
+ {
+ Expression e = dti.semanticY(sc, 1);
+ if (!e)
+ {
+ return setResult(resolveUFCSProperties(sc, e1x, exp.e2));
+ }
+
+ e1x = e;
+ }
+ else if (auto die = e1x.isDotIdExp())
+ {
+ Expression e = die.semanticY(sc, 1);
+ if (e && isDotOpDispatch(e))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=19687
+ *
+ * On this branch, e2 is semantically analyzed in resolvePropertiesX,
+ * but that call is done with gagged errors. That is the only time when
+ * semantic gets ran on e2, that is why the error never gets to be printed.
+ * In order to make sure that UFCS is tried with correct parameters, e2
+ * needs to have semantic ran on it.
+ */
+ exp.e2 = exp.e2.expressionSemantic(sc);
+ uint errors = global.startGagging();
+ e = resolvePropertiesX(sc, e, exp.e2);
+ if (global.endGagging(errors))
+ e = null; /* fall down to UFCS */
+ else
+ return setResult(e);
+ }
+ if (!e)
+ return setResult(resolveUFCSProperties(sc, e1x, exp.e2));
+ e1x = e;
+ }
+ else
+ {
+ if (auto se = e1x.isSliceExp())
+ se.arrayop = true;
+
+ e1x = e1x.expressionSemantic(sc);
+ }
+
+ /* We have f = value.
+ * Could mean:
+ * f(value)
+ * or:
+ * f() = value
+ */
+ if (Expression e = resolvePropertiesX(sc, e1x, exp.e2))
+ return setResult(e);
+
+ if (e1x.checkRightThis(sc))
+ {
+ return setError();
+ }
+ exp.e1 = e1x;
+ assert(exp.e1.type);
+ }
+ Type t1 = exp.e1.type.toBasetype();
+
+ /* Run this.e2 semantic.
+ * Different from other binary expressions, the analysis of e2
+ * depends on the result of e1 in assignments.
+ */
+ {
+ Expression e2x = inferType(exp.e2, t1.baseElemOf());
+ e2x = e2x.expressionSemantic(sc);
+ e2x = resolveProperties(sc, e2x);
+ if (e2x.op == TOK.type)
+ e2x = resolveAliasThis(sc, e2x); //https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e2x.op == TOK.error)
+ return setResult(e2x);
+ // We skip checking the value for structs/classes as these might have
+ // an opAssign defined.
+ if ((t1.ty != Tstruct && t1.ty != Tclass && e2x.checkValue()) ||
+ e2x.checkSharedAccess(sc))
+ return setError();
+ exp.e2 = e2x;
+ }
+
+ /* Rewrite tuple assignment as a tuple of assignments.
+ */
+ {
+ Expression e2x = exp.e2;
+
+ Ltupleassign:
+ if (exp.e1.op == TOK.tuple && e2x.op == TOK.tuple)
+ {
+ TupleExp tup1 = cast(TupleExp)exp.e1;
+ TupleExp tup2 = cast(TupleExp)e2x;
+ size_t dim = tup1.exps.dim;
+ Expression e = null;
+ if (dim != tup2.exps.dim)
+ {
+ exp.error("mismatched tuple lengths, %d and %d", cast(int)dim, cast(int)tup2.exps.dim);
+ return setError();
+ }
+ if (dim == 0)
+ {
+ e = IntegerExp.literal!0;
+ e = new CastExp(exp.loc, e, Type.tvoid); // avoid "has no effect" error
+ e = Expression.combine(tup1.e0, tup2.e0, e);
+ }
+ else
+ {
+ auto exps = new Expressions(dim);
+ for (size_t i = 0; i < dim; i++)
+ {
+ Expression ex1 = (*tup1.exps)[i];
+ Expression ex2 = (*tup2.exps)[i];
+ (*exps)[i] = new AssignExp(exp.loc, ex1, ex2);
+ }
+ e = new TupleExp(exp.loc, Expression.combine(tup1.e0, tup2.e0), exps);
+ }
+ return setResult(e.expressionSemantic(sc));
+ }
+
+ /* Look for form: e1 = e2.aliasthis.
+ */
+ if (exp.e1.op == TOK.tuple)
+ {
+ TupleDeclaration td = isAliasThisTuple(e2x);
+ if (!td)
+ goto Lnomatch;
+
+ assert(exp.e1.type.ty == Ttuple);
+ TypeTuple tt = cast(TypeTuple)exp.e1.type;
+
+ Expression e0;
+ Expression ev = extractSideEffect(sc, "__tup", e0, e2x);
+
+ auto iexps = new Expressions();
+ iexps.push(ev);
+ for (size_t u = 0; u < iexps.dim; u++)
+ {
+ Lexpand:
+ Expression e = (*iexps)[u];
+
+ Parameter arg = Parameter.getNth(tt.arguments, u);
+ //printf("[%d] iexps.dim = %d, ", u, iexps.dim);
+ //printf("e = (%s %s, %s), ", Token::tochars[e.op], e.toChars(), e.type.toChars());
+ //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
+
+ if (!arg || !e.type.implicitConvTo(arg.type))
+ {
+ // expand initializer to tuple
+ if (expandAliasThisTuples(iexps, u) != -1)
+ {
+ if (iexps.dim <= u)
+ break;
+ goto Lexpand;
+ }
+ goto Lnomatch;
+ }
+ }
+ e2x = new TupleExp(e2x.loc, e0, iexps);
+ e2x = e2x.expressionSemantic(sc);
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+ // Do not need to overwrite this.e2
+ goto Ltupleassign;
+ }
+ Lnomatch:
+ }
+
+ /* Inside constructor, if this is the first assignment of object field,
+ * rewrite this to initializing the field.
+ */
+ if (exp.op == TOK.assign
+ && exp.e1.checkModifiable(sc) == Modifiable.initialization)
+ {
+ //printf("[%s] change to init - %s\n", exp.loc.toChars(), exp.toChars());
+ auto t = exp.type;
+ exp = new ConstructExp(exp.loc, exp.e1, exp.e2);
+ exp.type = t;
+
+ // https://issues.dlang.org/show_bug.cgi?id=13515
+ // set Index::modifiable flag for complex AA element initialization
+ if (auto ie1 = exp.e1.isIndexExp())
+ {
+ Expression e1x = ie1.markSettingAAElem();
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ }
+ }
+ else if (exp.op == TOK.construct && exp.e1.op == TOK.variable &&
+ (cast(VarExp)exp.e1).var.storage_class & (STC.out_ | STC.ref_))
+ {
+ exp.memset = MemorySet.referenceInit;
+ }
+
+ if (exp.op == TOK.assign) // skip TOK.blit and TOK.construct, which are initializations
+ {
+ exp.e1.checkSharedAccess(sc);
+ checkUnsafeAccess(sc, exp.e1, false, true);
+ }
+
+ checkUnsafeAccess(sc, exp.e2, true, true); // Initializer must always be checked
+
+ /* If it is an assignment from a 'foreign' type,
+ * check for operator overloading.
+ */
+ if (exp.memset == MemorySet.referenceInit)
+ {
+ // If this is an initialization of a reference,
+ // do nothing
+ }
+ else if (t1.ty == Tstruct)
+ {
+ auto e1x = exp.e1;
+ auto e2x = exp.e2;
+ auto sd = (cast(TypeStruct)t1).sym;
+
+ if (exp.op == TOK.construct)
+ {
+ Type t2 = e2x.type.toBasetype();
+ if (t2.ty == Tstruct && sd == (cast(TypeStruct)t2).sym)
+ {
+ sd.size(exp.loc);
+ if (sd.sizeok != Sizeok.done)
+ return setError();
+ if (!sd.ctor)
+ sd.ctor = sd.searchCtor();
+
+ // https://issues.dlang.org/show_bug.cgi?id=15661
+ // Look for the form from last of comma chain.
+ auto e2y = lastComma(e2x);
+
+ CallExp ce = (e2y.op == TOK.call) ? cast(CallExp)e2y : null;
+ DotVarExp dve = (ce && ce.e1.op == TOK.dotVariable)
+ ? cast(DotVarExp)ce.e1 : null;
+ if (sd.ctor && ce && dve && dve.var.isCtorDeclaration() &&
+ // https://issues.dlang.org/show_bug.cgi?id=19389
+ dve.e1.op != TOK.dotVariable &&
+ e2y.type.implicitConvTo(t1))
+ {
+ /* Look for form of constructor call which is:
+ * __ctmp.ctor(arguments...)
+ */
+
+ /* Before calling the constructor, initialize
+ * variable with a bit copy of the default
+ * initializer
+ */
+ Expression einit = getInitExp(sd, exp.loc, sc, t1);
+ if (einit.op == TOK.error)
+ {
+ result = einit;
+ return;
+ }
+
+ auto ae = new BlitExp(exp.loc, exp.e1, einit);
+ ae.type = e1x.type;
+
+ /* Replace __ctmp being constructed with e1.
+ * We need to copy constructor call expression,
+ * because it may be used in other place.
+ */
+ auto dvx = cast(DotVarExp)dve.copy();
+ dvx.e1 = e1x;
+ auto cx = cast(CallExp)ce.copy();
+ cx.e1 = dvx;
+ if (checkConstructorEscape(sc, cx, false))
+ return setError();
+
+ Expression e0;
+ Expression.extractLast(e2x, e0);
+
+ auto e = Expression.combine(e0, ae, cx);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=21586
+ // Rewrite CondExp or e1 will miss direct construction, e.g.
+ // e1 = a ? S(1) : ...; -> AST: e1 = a ? (S(0)).this(1) : ...;
+ // a temporary created and an extra destructor call.
+ // AST will be rewritten to:
+ // a ? e1 = 0, e1.this(1) : ...; -> blitting plus construction
+ if (e2x.op == TOK.question)
+ {
+ /* Rewrite as:
+ * a ? e1 = b : e1 = c;
+ */
+ CondExp econd = cast(CondExp)e2x;
+ Expression ea1 = new ConstructExp(econd.e1.loc, e1x, econd.e1);
+ Expression ea2 = new ConstructExp(econd.e2.loc, e1x, econd.e2);
+ Expression e = new CondExp(exp.loc, econd.econd, ea1, ea2);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (sd.postblit || sd.hasCopyCtor)
+ {
+ /* We have a copy constructor for this
+ */
+
+ if (e2x.isLvalue())
+ {
+ if (sd.hasCopyCtor)
+ {
+ /* Rewrite as:
+ * e1 = init, e1.copyCtor(e2);
+ */
+ Expression einit = new BlitExp(exp.loc, exp.e1, getInitExp(sd, exp.loc, sc, t1));
+ einit.type = e1x.type;
+
+ Expression e;
+ e = new DotIdExp(exp.loc, e1x, Id.ctor);
+ e = new CallExp(exp.loc, e, e2x);
+ e = new CommaExp(exp.loc, einit, e);
+
+ //printf("e: %s\n", e.toChars());
+
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ else
+ {
+ if (!e2x.type.implicitConvTo(e1x.type))
+ {
+ exp.error("conversion error from `%s` to `%s`",
+ e2x.type.toChars(), e1x.type.toChars());
+ return setError();
+ }
+
+ /* Rewrite as:
+ * (e1 = e2).postblit();
+ *
+ * Blit assignment e1 = e2 returns a reference to the original e1,
+ * then call the postblit on it.
+ */
+ Expression e = e1x.copy();
+ e.type = e.type.mutableOf();
+ if (e.type.isShared && !sd.type.isShared)
+ e.type = e.type.unSharedOf();
+ e = new BlitExp(exp.loc, e, e2x);
+ e = new DotVarExp(exp.loc, e, sd.postblit, false);
+ e = new CallExp(exp.loc, e);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ }
+ else
+ {
+ /* The struct value returned from the function is transferred
+ * so should not call the destructor on it.
+ */
+ e2x = valueNoDtor(e2x);
+ }
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19251
+ // if e2 cannot be converted to e1.type, maybe there is an alias this
+ if (!e2x.implicitConvTo(t1))
+ {
+ AggregateDeclaration ad2 = isAggregate(e2x.type);
+ if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type))
+ {
+ /* Rewrite (e1 op e2) as:
+ * (e1 op e2.aliasthis)
+ */
+ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident);
+ result = exp.expressionSemantic(sc);
+ return;
+ }
+ }
+ }
+ else if (!e2x.implicitConvTo(t1))
+ {
+ sd.size(exp.loc);
+ if (sd.sizeok != Sizeok.done)
+ return setError();
+ if (!sd.ctor)
+ sd.ctor = sd.searchCtor();
+
+ if (sd.ctor)
+ {
+ /* Look for implicit constructor call
+ * Rewrite as:
+ * e1 = init, e1.ctor(e2)
+ */
+
+ /* Fix Issue 5153 : https://issues.dlang.org/show_bug.cgi?id=5153
+ * Using `new` to initialize a struct object is a common mistake, but
+ * the error message from the compiler is not very helpful in that
+ * case. If exp.e2 is a NewExp and the type of new is the same as
+ * the type as exp.e1 (struct in this case), then we know for sure
+ * that the user wants to instantiate a struct. This is done to avoid
+ * issuing an error when the user actually wants to call a constructor
+ * which receives a class object.
+ *
+ * Foo f = new Foo2(0); is a valid expression if Foo has a constructor
+ * which receives an instance of a Foo2 class
+ */
+ if (exp.e2.op == TOK.new_)
+ {
+ auto newExp = cast(NewExp)(exp.e2);
+ if (newExp.newtype && newExp.newtype == t1)
+ {
+ error(exp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`",
+ newExp.toChars(), newExp.type.toChars(), t1.toChars());
+ errorSupplemental(exp.loc, "Perhaps remove the `new` keyword?");
+ return setError();
+ }
+ }
+
+ Expression einit = new BlitExp(exp.loc, e1x, getInitExp(sd, exp.loc, sc, t1));
+ einit.type = e1x.type;
+
+ Expression e;
+ e = new DotIdExp(exp.loc, e1x, Id.ctor);
+ e = new CallExp(exp.loc, e, e2x);
+ e = new CommaExp(exp.loc, einit, e);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ if (search_function(sd, Id.call))
+ {
+ /* Look for static opCall
+ * https://issues.dlang.org/show_bug.cgi?id=2702
+ * Rewrite as:
+ * e1 = typeof(e1).opCall(arguments)
+ */
+ e2x = typeDotIdExp(e2x.loc, e1x.type, Id.call);
+ e2x = new CallExp(exp.loc, e2x, exp.e2);
+
+ e2x = e2x.expressionSemantic(sc);
+ e2x = resolveProperties(sc, e2x);
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+ if (e2x.checkValue() || e2x.checkSharedAccess(sc))
+ return setError();
+ }
+ }
+ else // https://issues.dlang.org/show_bug.cgi?id=11355
+ {
+ AggregateDeclaration ad2 = isAggregate(e2x.type);
+ if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type))
+ {
+ /* Rewrite (e1 op e2) as:
+ * (e1 op e2.aliasthis)
+ */
+ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident);
+ result = exp.expressionSemantic(sc);
+ return;
+ }
+ }
+ }
+ else if (exp.op == TOK.assign)
+ {
+ if (e1x.op == TOK.index && (cast(IndexExp)e1x).e1.type.toBasetype().ty == Taarray)
+ {
+ /*
+ * Rewrite:
+ * aa[key] = e2;
+ * as:
+ * ref __aatmp = aa;
+ * ref __aakey = key;
+ * ref __aaval = e2;
+ * (__aakey in __aatmp
+ * ? __aatmp[__aakey].opAssign(__aaval)
+ * : ConstructExp(__aatmp[__aakey], __aaval));
+ */
+ // ensure we keep the expr modifiable
+ Expression esetting = (cast(IndexExp)e1x).markSettingAAElem();
+ if (esetting.op == TOK.error)
+ {
+ result = esetting;
+ return;
+ }
+ assert(esetting.op == TOK.index);
+ IndexExp ie = cast(IndexExp) esetting;
+ Type t2 = e2x.type.toBasetype();
+
+ Expression e0 = null;
+ Expression ea = extractSideEffect(sc, "__aatmp", e0, ie.e1);
+ Expression ek = extractSideEffect(sc, "__aakey", e0, ie.e2);
+ Expression ev = extractSideEffect(sc, "__aaval", e0, e2x);
+
+ AssignExp ae = cast(AssignExp)exp.copy();
+ ae.e1 = new IndexExp(exp.loc, ea, ek);
+ ae.e1 = ae.e1.expressionSemantic(sc);
+ ae.e1 = ae.e1.optimize(WANTvalue);
+ ae.e2 = ev;
+ Expression e = ae.op_overload(sc);
+ if (e)
+ {
+ Expression ey = null;
+ if (t2.ty == Tstruct && sd == t2.toDsymbol(sc))
+ {
+ ey = ev;
+ }
+ else if (!ev.implicitConvTo(ie.type) && sd.ctor)
+ {
+ // Look for implicit constructor call
+ // Rewrite as S().ctor(e2)
+ ey = new StructLiteralExp(exp.loc, sd, null);
+ ey = new DotIdExp(exp.loc, ey, Id.ctor);
+ ey = new CallExp(exp.loc, ey, ev);
+ ey = ey.trySemantic(sc);
+ }
+ if (ey)
+ {
+ Expression ex;
+ ex = new IndexExp(exp.loc, ea, ek);
+ ex = ex.expressionSemantic(sc);
+ ex = ex.modifiableLvalue(sc, ex); // allocate new slot
+ ex = ex.optimize(WANTvalue);
+
+ ey = new ConstructExp(exp.loc, ex, ey);
+ ey = ey.expressionSemantic(sc);
+ if (ey.op == TOK.error)
+ {
+ result = ey;
+ return;
+ }
+ ex = e;
+
+ // https://issues.dlang.org/show_bug.cgi?id=14144
+ // The whole expression should have the common type
+ // of opAssign() return and assigned AA entry.
+ // Even if there's no common type, expression should be typed as void.
+ if (!typeMerge(sc, TOK.question, ex, ey))
+ {
+ ex = new CastExp(ex.loc, ex, Type.tvoid);
+ ey = new CastExp(ey.loc, ey, Type.tvoid);
+ }
+ e = new CondExp(exp.loc, new InExp(exp.loc, ek, ea), ex, ey);
+ }
+ e = Expression.combine(e0, e);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+ else
+ {
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+ }
+ else
+ assert(exp.op == TOK.blit);
+
+ exp.e1 = e1x;
+ exp.e2 = e2x;
+ }
+ else if (t1.ty == Tclass)
+ {
+ // Disallow assignment operator overloads for same type
+ if (exp.op == TOK.assign && !exp.e2.implicitConvTo(exp.e1.type))
+ {
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+ }
+ else if (t1.ty == Tsarray)
+ {
+ // SliceExp cannot have static array type without context inference.
+ assert(exp.e1.op != TOK.slice);
+ Expression e1x = exp.e1;
+ Expression e2x = exp.e2;
+
+ if (e2x.implicitConvTo(e1x.type))
+ {
+ if (exp.op != TOK.blit && (e2x.op == TOK.slice && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op == TOK.cast_ && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != TOK.slice && e2x.isLvalue()))
+ {
+ if (e1x.checkPostblit(sc, t1))
+ return setError();
+ }
+
+ // e2 matches to t1 because of the implicit length match, so
+ if (isUnaArrayOp(e2x.op) || isBinArrayOp(e2x.op))
+ {
+ // convert e1 to e1[]
+ // e.g. e1[] = a[] + b[];
+ auto sle = new SliceExp(e1x.loc, e1x, null, null);
+ sle.arrayop = true;
+ e1x = sle.expressionSemantic(sc);
+ }
+ else
+ {
+ // convert e2 to t1 later
+ // e.g. e1 = [1, 2, 3];
+ }
+ }
+ else
+ {
+ if (e2x.implicitConvTo(t1.nextOf().arrayOf()) > MATCH.nomatch)
+ {
+ uinteger_t dim1 = (cast(TypeSArray)t1).dim.toInteger();
+ uinteger_t dim2 = dim1;
+ if (auto ale = e2x.isArrayLiteralExp())
+ {
+ dim2 = ale.elements ? ale.elements.dim : 0;
+ }
+ else if (auto se = e2x.isSliceExp())
+ {
+ Type tx = toStaticArrayType(se);
+ if (tx)
+ dim2 = (cast(TypeSArray)tx).dim.toInteger();
+ }
+ if (dim1 != dim2)
+ {
+ exp.error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2);
+ return setError();
+ }
+ }
+
+ // May be block or element-wise assignment, so
+ // convert e1 to e1[]
+ if (exp.op != TOK.assign)
+ {
+ // If multidimensional static array, treat as one large array
+ //
+ // Find the appropriate array type depending on the assignment, e.g.
+ // int[3] = int => int[3]
+ // int[3][2] = int => int[6]
+ // int[3][2] = int[] => int[3][2]
+ // int[3][2][4] + int => int[24]
+ // int[3][2][4] + int[] => int[3][8]
+ ulong dim = t1.isTypeSArray().dim.toUInteger();
+ auto type = t1.nextOf();
+
+ for (TypeSArray tsa; (tsa = type.isTypeSArray()) !is null; )
+ {
+ import core.checkedint : mulu;
+
+ // Accumulate skipped dimensions
+ bool overflow = false;
+ dim = mulu(dim, tsa.dim.toUInteger(), overflow);
+ if (overflow || dim >= uint.max)
+ {
+ // dym exceeds maximum array size
+ exp.error("static array `%s` size overflowed to %llu",
+ e1x.type.toChars(), cast(ulong) dim);
+ return setError();
+ }
+
+ // Move to the element type
+ type = tsa.nextOf().toBasetype();
+
+ // Rewrite ex1 as a static array if a matching type was found
+ if (e2x.implicitConvTo(type) > MATCH.nomatch)
+ {
+ e1x.type = type.sarrayOf(dim);
+ break;
+ }
+ }
+ }
+ auto sle = new SliceExp(e1x.loc, e1x, null, null);
+ sle.arrayop = true;
+ e1x = sle.expressionSemantic(sc);
+ }
+ if (e1x.op == TOK.error)
+ return setResult(e1x);
+ if (e2x.op == TOK.error)
+ return setResult(e2x);
+
+ exp.e1 = e1x;
+ exp.e2 = e2x;
+ t1 = e1x.type.toBasetype();
+ }
+ /* Check the mutability of e1.
+ */
+ if (auto ale = exp.e1.isArrayLengthExp())
+ {
+ // e1 is not an lvalue, but we let code generator handle it
+
+ auto ale1x = ale.e1.modifiableLvalue(sc, exp.e1);
+ if (ale1x.op == TOK.error)
+ return setResult(ale1x);
+ ale.e1 = ale1x;
+
+ Type tn = ale.e1.type.toBasetype().nextOf();
+ checkDefCtor(ale.loc, tn);
+
+ Identifier hook = global.params.tracegc ? Id._d_arraysetlengthTTrace : Id._d_arraysetlengthT;
+ if (!verifyHookExist(exp.loc, *sc, Id._d_arraysetlengthTImpl, "resizing arrays"))
+ return setError();
+
+ exp.e2 = exp.e2.expressionSemantic(sc);
+ auto lc = lastComma(exp.e2);
+ lc = lc.optimize(WANTvalue);
+ // use slice expression when arr.length = 0 to avoid runtime call
+ if(lc.op == TOK.int64 && lc.toInteger() == 0)
+ {
+ Expression se = new SliceExp(ale.loc, ale.e1, lc, lc);
+ Expression as = new AssignExp(ale.loc, ale.e1, se);
+ as = as.expressionSemantic(sc);
+ auto res = Expression.combine(as, exp.e2);
+ res.type = ale.type;
+ return setResult(res);
+ }
+
+ // Lower to object._d_arraysetlengthTImpl!(typeof(e1))._d_arraysetlengthT{,Trace}(e1, e2)
+ Expression id = new IdentifierExp(ale.loc, Id.empty);
+ id = new DotIdExp(ale.loc, id, Id.object);
+ auto tiargs = new Objects();
+ tiargs.push(ale.e1.type);
+ id = new DotTemplateInstanceExp(ale.loc, id, Id._d_arraysetlengthTImpl, tiargs);
+ id = new DotIdExp(ale.loc, id, hook);
+ id = id.expressionSemantic(sc);
+
+ auto arguments = new Expressions();
+ arguments.reserve(5);
+ if (global.params.tracegc)
+ {
+ auto funcname = (sc.callsc && sc.callsc.func) ? sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars();
+ arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString()));
+ arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32));
+ arguments.push(new StringExp(exp.loc, funcname.toDString()));
+ }
+ arguments.push(ale.e1);
+ arguments.push(exp.e2);
+
+ Expression ce = new CallExp(ale.loc, id, arguments);
+ auto res = ce.expressionSemantic(sc);
+ // if (global.params.verbose)
+ // message("lowered %s =>\n %s", exp.toChars(), res.toChars());
+ return setResult(res);
+ }
+ else if (auto se = exp.e1.isSliceExp())
+ {
+ Type tn = se.type.nextOf();
+ const fun = sc.func;
+ if (exp.op == TOK.assign && !tn.isMutable() &&
+ // allow modifiation in module ctor, see
+ // https://issues.dlang.org/show_bug.cgi?id=9884
+ (!fun || (fun && !fun.isStaticCtorDeclaration())))
+ {
+ exp.error("slice `%s` is not mutable", se.toChars());
+ return setError();
+ }
+
+ if (exp.op == TOK.assign && !tn.baseElemOf().isAssignable())
+ {
+ exp.error("slice `%s` is not mutable, struct `%s` has immutable members",
+ exp.e1.toChars(), tn.baseElemOf().toChars());
+ result = ErrorExp.get();
+ return;
+ }
+
+ // For conditional operator, both branches need conversion.
+ while (se.e1.op == TOK.slice)
+ se = cast(SliceExp)se.e1;
+ if (se.e1.op == TOK.question && se.e1.type.toBasetype().ty == Tsarray)
+ {
+ se.e1 = se.e1.modifiableLvalue(sc, exp.e1);
+ if (se.e1.op == TOK.error)
+ return setResult(se.e1);
+ }
+ }
+ else
+ {
+ if (t1.ty == Tsarray && exp.op == TOK.assign)
+ {
+ Type tn = exp.e1.type.nextOf();
+ if (tn && !tn.baseElemOf().isAssignable())
+ {
+ exp.error("array `%s` is not mutable, struct `%s` has immutable members",
+ exp.e1.toChars(), tn.baseElemOf().toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ Expression e1x = exp.e1;
+
+ // Try to do a decent error message with the expression
+ // before it gets constant folded
+ if (exp.op == TOK.assign)
+ e1x = e1x.modifiableLvalue(sc, e1old);
+
+ e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true);
+
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ exp.e1 = e1x;
+ }
+
+ /* Tweak e2 based on the type of e1.
+ */
+ Expression e2x = exp.e2;
+ Type t2 = e2x.type.toBasetype();
+
+ // If it is a array, get the element type. Note that it may be
+ // multi-dimensional.
+ Type telem = t1;
+ while (telem.ty == Tarray)
+ telem = telem.nextOf();
+
+ if (exp.e1.op == TOK.slice && t1.nextOf() &&
+ (telem.ty != Tvoid || e2x.op == TOK.null_) &&
+ e2x.implicitConvTo(t1.nextOf()))
+ {
+ // Check for block assignment. If it is of type void[], void[][], etc,
+ // '= null' is the only allowable block assignment (Bug 7493)
+ exp.memset = MemorySet.blockAssign; // make it easy for back end to tell what this is
+ e2x = e2x.implicitCastTo(sc, t1.nextOf());
+ if (exp.op != TOK.blit && e2x.isLvalue() && exp.e1.checkPostblit(sc, t1.nextOf()))
+ return setError();
+ }
+ else if (exp.e1.op == TOK.slice &&
+ (t2.ty == Tarray || t2.ty == Tsarray) &&
+ t2.nextOf().implicitConvTo(t1.nextOf()))
+ {
+ // Check element-wise assignment.
+
+ /* If assigned elements number is known at compile time,
+ * check the mismatch.
+ */
+ SliceExp se1 = cast(SliceExp)exp.e1;
+ TypeSArray tsa1 = cast(TypeSArray)toStaticArrayType(se1);
+ TypeSArray tsa2 = null;
+ if (auto ale = e2x.isArrayLiteralExp())
+ tsa2 = cast(TypeSArray)t2.nextOf().sarrayOf(ale.elements.dim);
+ else if (auto se = e2x.isSliceExp())
+ tsa2 = cast(TypeSArray)toStaticArrayType(se);
+ else
+ tsa2 = t2.isTypeSArray();
+ if (tsa1 && tsa2)
+ {
+ uinteger_t dim1 = tsa1.dim.toInteger();
+ uinteger_t dim2 = tsa2.dim.toInteger();
+ if (dim1 != dim2)
+ {
+ exp.error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2);
+ return setError();
+ }
+ }
+
+ if (exp.op != TOK.blit &&
+ (e2x.op == TOK.slice && (cast(UnaExp)e2x).e1.isLvalue() ||
+ e2x.op == TOK.cast_ && (cast(UnaExp)e2x).e1.isLvalue() ||
+ e2x.op != TOK.slice && e2x.isLvalue()))
+ {
+ if (exp.e1.checkPostblit(sc, t1.nextOf()))
+ return setError();
+ }
+
+ if (0 && global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == TOK.assign &&
+ e2x.op != TOK.slice && e2x.op != TOK.assign &&
+ e2x.op != TOK.arrayLiteral && e2x.op != TOK.string_ &&
+ !(e2x.op == TOK.add || e2x.op == TOK.min ||
+ e2x.op == TOK.mul || e2x.op == TOK.div ||
+ e2x.op == TOK.mod || e2x.op == TOK.xor ||
+ e2x.op == TOK.and || e2x.op == TOK.or ||
+ e2x.op == TOK.pow ||
+ e2x.op == TOK.tilde || e2x.op == TOK.negate))
+ {
+ const(char)* e1str = exp.e1.toChars();
+ const(char)* e2str = e2x.toChars();
+ exp.warning("explicit element-wise assignment `%s = (%s)[]` is better than `%s = %s`", e1str, e2str, e1str, e2str);
+ }
+
+ Type t2n = t2.nextOf();
+ Type t1n = t1.nextOf();
+ int offset;
+ if (t2n.equivalent(t1n) ||
+ t1n.isBaseOf(t2n, &offset) && offset == 0)
+ {
+ /* Allow copy of distinct qualifier elements.
+ * eg.
+ * char[] dst; const(char)[] src;
+ * dst[] = src;
+ *
+ * class C {} class D : C {}
+ * C[2] ca; D[] da;
+ * ca[] = da;
+ */
+ if (isArrayOpValid(e2x))
+ {
+ // Don't add CastExp to keep AST for array operations
+ e2x = e2x.copy();
+ e2x.type = exp.e1.type.constOf();
+ }
+ else
+ e2x = e2x.castTo(sc, exp.e1.type.constOf());
+ }
+ else
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=15778
+ * A string literal has an array type of immutable
+ * elements by default, and normally it cannot be convertible to
+ * array type of mutable elements. But for element-wise assignment,
+ * elements need to be const at best. So we should give a chance
+ * to change code unit size for polysemous string literal.
+ */
+ if (e2x.op == TOK.string_)
+ e2x = e2x.implicitCastTo(sc, exp.e1.type.constOf());
+ else
+ e2x = e2x.implicitCastTo(sc, exp.e1.type);
+ }
+ if (t1n.toBasetype.ty == Tvoid && t2n.toBasetype.ty == Tvoid)
+ {
+ if (!sc.intypeof && sc.func && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("cannot copy `void[]` to `void[]` in `@safe` code");
+ return setError();
+ }
+ }
+ }
+ else
+ {
+ if (0 && global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == TOK.assign &&
+ t1.ty == Tarray && t2.ty == Tsarray &&
+ e2x.op != TOK.slice &&
+ t2.implicitConvTo(t1))
+ {
+ // Disallow ar[] = sa (Converted to ar[] = sa[])
+ // Disallow da = sa (Converted to da = sa[])
+ const(char)* e1str = exp.e1.toChars();
+ const(char)* e2str = e2x.toChars();
+ const(char)* atypestr = exp.e1.op == TOK.slice ? "element-wise" : "slice";
+ exp.warning("explicit %s assignment `%s = (%s)[]` is better than `%s = %s`", atypestr, e1str, e2str, e1str, e2str);
+ }
+ if (exp.op == TOK.blit)
+ e2x = e2x.castTo(sc, exp.e1.type);
+ else
+ {
+ e2x = e2x.implicitCastTo(sc, exp.e1.type);
+
+ // Fix Issue 13435: https://issues.dlang.org/show_bug.cgi?id=13435
+
+ // If the implicit cast has failed and the assign expression is
+ // the initialization of a struct member field
+ if (e2x.op == TOK.error && exp.op == TOK.construct && t1.ty == Tstruct)
+ {
+ scope sd = (cast(TypeStruct)t1).sym;
+ Dsymbol opAssign = search_function(sd, Id.assign);
+
+ // and the struct defines an opAssign
+ if (opAssign)
+ {
+ // offer more information about the cause of the problem
+ errorSupplemental(exp.loc,
+ "`%s` is the first assignment of `%s` therefore it represents its initialization",
+ exp.toChars(), exp.e1.toChars());
+ errorSupplemental(exp.loc,
+ "`opAssign` methods are not used for initialization, but for subsequent assignments");
+ }
+ }
+ }
+ }
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+ exp.e2 = e2x;
+ t2 = exp.e2.type.toBasetype();
+
+ /* Look for array operations
+ */
+ if ((t2.ty == Tarray || t2.ty == Tsarray) && isArrayOpValid(exp.e2))
+ {
+ // Look for valid array operations
+ if (exp.memset != MemorySet.blockAssign &&
+ exp.e1.op == TOK.slice &&
+ (isUnaArrayOp(exp.e2.op) || isBinArrayOp(exp.e2.op)))
+ {
+ exp.type = exp.e1.type;
+ if (exp.op == TOK.construct) // https://issues.dlang.org/show_bug.cgi?id=10282
+ // tweak mutability of e1 element
+ exp.e1.type = exp.e1.type.nextOf().mutableOf().arrayOf();
+ result = arrayOp(exp, sc);
+ return;
+ }
+
+ // Drop invalid array operations in e2
+ // d = a[] + b[], d = (a[] + b[])[0..2], etc
+ if (checkNonAssignmentArrayOp(exp.e2, exp.memset != MemorySet.blockAssign && exp.op == TOK.assign))
+ return setError();
+
+ // Remains valid array assignments
+ // d = d[], d = [1,2,3], etc
+ }
+
+ /* Don't allow assignment to classes that were allocated on the stack with:
+ * scope Class c = new Class();
+ */
+ if (exp.e1.op == TOK.variable && exp.op == TOK.assign)
+ {
+ VarExp ve = cast(VarExp)exp.e1;
+ VarDeclaration vd = ve.var.isVarDeclaration();
+ if (vd && (vd.onstack || vd.mynew))
+ {
+ assert(t1.ty == Tclass);
+ exp.error("cannot rebind scope variables");
+ }
+ }
+
+ if (exp.e1.op == TOK.variable && (cast(VarExp)exp.e1).var.ident == Id.ctfe)
+ {
+ exp.error("cannot modify compiler-generated variable `__ctfe`");
+ }
+
+ exp.type = exp.e1.type;
+ assert(exp.type);
+ auto res = exp.op == TOK.assign ? exp.reorderSettingAAElem(sc) : exp;
+ checkAssignEscape(sc, res, false);
+ return setResult(res);
+ }
+
+ override void visit(PowAssignExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.checkReadModifyWrite(exp.op, exp.e2))
+ return setError();
+
+ assert(exp.e1.type && exp.e2.type);
+ if (exp.e1.op == TOK.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray)
+ {
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+
+ // T[] ^^= ...
+ if (exp.e2.implicitConvTo(exp.e1.type.nextOf()))
+ {
+ // T[] ^^= T
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type.nextOf());
+ }
+ else if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ // Check element types are arithmetic
+ Type tb1 = exp.e1.type.nextOf().toBasetype();
+ Type tb2 = exp.e2.type.toBasetype();
+ if (tb2.ty == Tarray || tb2.ty == Tsarray)
+ tb2 = tb2.nextOf().toBasetype();
+ if ((tb1.isintegral() || tb1.isfloating()) && (tb2.isintegral() || tb2.isfloating()))
+ {
+ exp.type = exp.e1.type;
+ result = arrayOp(exp, sc);
+ return;
+ }
+ }
+ else
+ {
+ exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ }
+
+ if ((exp.e1.type.isintegral() || exp.e1.type.isfloating()) && (exp.e2.type.isintegral() || exp.e2.type.isfloating()))
+ {
+ Expression e0 = null;
+ e = exp.reorderSettingAAElem(sc);
+ e = Expression.extractLast(e, e0);
+ assert(e == exp);
+
+ if (exp.e1.op == TOK.variable)
+ {
+ // Rewrite: e1 = e1 ^^ e2
+ e = new PowExp(exp.loc, exp.e1.syntaxCopy(), exp.e2);
+ e = new AssignExp(exp.loc, exp.e1, e);
+ }
+ else
+ {
+ // Rewrite: ref tmp = e1; tmp = tmp ^^ e2
+ auto v = copyToTemp(STC.ref_, "__powtmp", exp.e1);
+ auto de = new DeclarationExp(exp.e1.loc, v);
+ auto ve = new VarExp(exp.e1.loc, v);
+ e = new PowExp(exp.loc, ve, exp.e2);
+ e = new AssignExp(exp.loc, new VarExp(exp.e1.loc, v), e);
+ e = new CommaExp(exp.loc, de, e);
+ }
+ e = Expression.combine(e0, e);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ result = exp.incompatibleTypes();
+ }
+
+ override void visit(CatAssignExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ //printf("CatAssignExp::semantic() %s\n", exp.toChars());
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.op == TOK.slice)
+ {
+ SliceExp se = cast(SliceExp)exp.e1;
+ if (se.e1.type.toBasetype().ty == Tsarray)
+ {
+ exp.error("cannot append to static array `%s`", se.e1.type.toChars());
+ return setError();
+ }
+ }
+
+ exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (exp.e2.op == TOK.error)
+ {
+ result = exp.e2;
+ return;
+ }
+
+ if (checkNonAssignmentArrayOp(exp.e2))
+ return setError();
+
+ Type tb1 = exp.e1.type.toBasetype();
+ Type tb1next = tb1.nextOf();
+ Type tb2 = exp.e2.type.toBasetype();
+
+ /* Possibilities:
+ * TOK.concatenateAssign: appending T[] to T[]
+ * TOK.concatenateElemAssign: appending T to T[]
+ * TOK.concatenateDcharAssign: appending dchar to T[]
+ */
+ if ((tb1.ty == Tarray) &&
+ (tb2.ty == Tarray || tb2.ty == Tsarray) &&
+ (exp.e2.implicitConvTo(exp.e1.type) ||
+ (tb2.nextOf().implicitConvTo(tb1next) &&
+ (tb2.nextOf().size(Loc.initial) == tb1next.size(Loc.initial)))))
+ {
+ // TOK.concatenateAssign
+ assert(exp.op == TOK.concatenateAssign);
+ if (exp.e1.checkPostblit(sc, tb1next))
+ return setError();
+
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type);
+ }
+ else if ((tb1.ty == Tarray) && exp.e2.implicitConvTo(tb1next))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=19782
+ *
+ * If e2 is implicitly convertible to tb1next, the conversion
+ * might be done through alias this, in which case, e2 needs to
+ * be modified accordingly (e2 => e2.aliasthis).
+ */
+ if (tb2.ty == Tstruct && (cast(TypeStruct)tb2).implicitConvToThroughAliasThis(tb1next))
+ goto Laliasthis;
+ if (tb2.ty == Tclass && (cast(TypeClass)tb2).implicitConvToThroughAliasThis(tb1next))
+ goto Laliasthis;
+ // Append element
+ if (exp.e2.checkPostblit(sc, tb2))
+ return setError();
+
+ if (checkNewEscape(sc, exp.e2, false))
+ return setError();
+
+ exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, exp.e2.castTo(sc, tb1next));
+ exp.e2 = doCopyOrMove(sc, exp.e2);
+ }
+ else if (tb1.ty == Tarray &&
+ (tb1next.ty == Tchar || tb1next.ty == Twchar) &&
+ exp.e2.type.ty != tb1next.ty &&
+ exp.e2.implicitConvTo(Type.tdchar))
+ {
+ // Append dchar to char[] or wchar[]
+ exp = new CatDcharAssignExp(exp.loc, exp.type, exp.e1, exp.e2.castTo(sc, Type.tdchar));
+
+ /* Do not allow appending wchar to char[] because if wchar happens
+ * to be a surrogate pair, nothing good can result.
+ */
+ }
+ else
+ {
+ // Try alias this on first operand
+ static Expression tryAliasThisForLhs(BinAssignExp exp, Scope* sc)
+ {
+ AggregateDeclaration ad1 = isAggregate(exp.e1.type);
+ if (!ad1 || !ad1.aliasthis)
+ return null;
+
+ /* Rewrite (e1 op e2) as:
+ * (e1.aliasthis op e2)
+ */
+ if (isRecursiveAliasThis(exp.att1, exp.e1.type))
+ return null;
+ //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
+ Expression e1 = new DotIdExp(exp.loc, exp.e1, ad1.aliasthis.ident);
+ BinExp be = cast(BinExp)exp.copy();
+ be.e1 = e1;
+ return be.trySemantic(sc);
+ }
+
+ // Try alias this on second operand
+ static Expression tryAliasThisForRhs(BinAssignExp exp, Scope* sc)
+ {
+ AggregateDeclaration ad2 = isAggregate(exp.e2.type);
+ if (!ad2 || !ad2.aliasthis)
+ return null;
+ /* Rewrite (e1 op e2) as:
+ * (e1 op e2.aliasthis)
+ */
+ if (isRecursiveAliasThis(exp.att2, exp.e2.type))
+ return null;
+ //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
+ Expression e2 = new DotIdExp(exp.loc, exp.e2, ad2.aliasthis.ident);
+ BinExp be = cast(BinExp)exp.copy();
+ be.e2 = e2;
+ return be.trySemantic(sc);
+ }
+
+ Laliasthis:
+ result = tryAliasThisForLhs(exp, sc);
+ if (result)
+ return;
+
+ result = tryAliasThisForRhs(exp, sc);
+ if (result)
+ return;
+
+ exp.error("cannot append type `%s` to type `%s`", tb2.toChars(), tb1.toChars());
+ return setError();
+ }
+
+ if (exp.e2.checkValue() || exp.e2.checkSharedAccess(sc))
+ return setError();
+
+ exp.type = exp.e1.type;
+ auto res = exp.reorderSettingAAElem(sc);
+ if ((exp.op == TOK.concatenateElemAssign || exp.op == TOK.concatenateDcharAssign) &&
+ global.params.useDIP1000 == FeatureState.enabled)
+ checkAssignEscape(sc, res, false);
+ result = res;
+ }
+
+ override void visit(AddExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("AddExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb1 = exp.e1.type.toBasetype();
+ Type tb2 = exp.e2.type.toBasetype();
+
+ bool err = false;
+ if (tb1.ty == Tdelegate || tb1.isPtrToFunction())
+ {
+ err |= exp.e1.checkArithmetic() || exp.e1.checkSharedAccess(sc);
+ }
+ if (tb2.ty == Tdelegate || tb2.isPtrToFunction())
+ {
+ err |= exp.e2.checkArithmetic() || exp.e2.checkSharedAccess(sc);
+ }
+ if (err)
+ return setError();
+
+ if (tb1.ty == Tpointer && exp.e2.type.isintegral() || tb2.ty == Tpointer && exp.e1.type.isintegral())
+ {
+ result = scaleFactor(exp, sc);
+ return;
+ }
+
+ if (tb1.ty == Tpointer && tb2.ty == Tpointer)
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ tb1 = exp.e1.type.toBasetype();
+ if (!target.isVectorOpSupported(tb1, exp.op, tb2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if ((tb1.isreal() && exp.e2.type.isimaginary()) || (tb1.isimaginary() && exp.e2.type.isreal()))
+ {
+ switch (exp.type.toBasetype().ty)
+ {
+ case Tfloat32:
+ case Timaginary32:
+ exp.type = Type.tcomplex32;
+ break;
+
+ case Tfloat64:
+ case Timaginary64:
+ exp.type = Type.tcomplex64;
+ break;
+
+ case Tfloat80:
+ case Timaginary80:
+ exp.type = Type.tcomplex80;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ result = exp;
+ }
+
+ override void visit(MinExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("MinExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type t1 = exp.e1.type.toBasetype();
+ Type t2 = exp.e2.type.toBasetype();
+
+ bool err = false;
+ if (t1.ty == Tdelegate || t1.isPtrToFunction())
+ {
+ err |= exp.e1.checkArithmetic() || exp.e1.checkSharedAccess(sc);
+ }
+ if (t2.ty == Tdelegate || t2.isPtrToFunction())
+ {
+ err |= exp.e2.checkArithmetic() || exp.e2.checkSharedAccess(sc);
+ }
+ if (err)
+ return setError();
+
+ if (t1.ty == Tpointer)
+ {
+ if (t2.ty == Tpointer)
+ {
+ // https://dlang.org/spec/expression.html#add_expressions
+ // "If both operands are pointers, and the operator is -, the pointers are
+ // subtracted and the result is divided by the size of the type pointed to
+ // by the operands. It is an error if the pointers point to different types."
+ Type p1 = t1.nextOf();
+ Type p2 = t2.nextOf();
+
+ if (!p1.equivalent(p2))
+ {
+ // Deprecation to remain for at least a year, after which this should be
+ // changed to an error
+ // See https://github.com/dlang/dmd/pull/7332
+ deprecation(exp.loc,
+ "cannot subtract pointers to different types: `%s` and `%s`.",
+ t1.toChars(), t2.toChars());
+ }
+
+ // Need to divide the result by the stride
+ // Replace (ptr - ptr) with (ptr - ptr) / stride
+ d_int64 stride;
+
+ // make sure pointer types are compatible
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ exp.type = Type.tptrdiff_t;
+ stride = t2.nextOf().size();
+ if (stride == 0)
+ {
+ e = new IntegerExp(exp.loc, 0, Type.tptrdiff_t);
+ }
+ else
+ {
+ e = new DivExp(exp.loc, exp, new IntegerExp(Loc.initial, stride, Type.tptrdiff_t));
+ e.type = Type.tptrdiff_t;
+ }
+ }
+ else if (t2.isintegral())
+ e = scaleFactor(exp, sc);
+ else
+ {
+ exp.error("can't subtract `%s` from pointer", t2.toChars());
+ e = ErrorExp.get();
+ }
+ result = e;
+ return;
+ }
+ if (t2.ty == Tpointer)
+ {
+ exp.type = exp.e2.type;
+ exp.error("can't subtract pointer from `%s`", exp.e1.type.toChars());
+ return setError();
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ t1 = exp.e1.type.toBasetype();
+ t2 = exp.e2.type.toBasetype();
+ if (!target.isVectorOpSupported(t1, exp.op, t2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if ((t1.isreal() && t2.isimaginary()) || (t1.isimaginary() && t2.isreal()))
+ {
+ switch (exp.type.ty)
+ {
+ case Tfloat32:
+ case Timaginary32:
+ exp.type = Type.tcomplex32;
+ break;
+
+ case Tfloat64:
+ case Timaginary64:
+ exp.type = Type.tcomplex64;
+ break;
+
+ case Tfloat80:
+ case Timaginary80:
+ exp.type = Type.tcomplex80;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ result = exp;
+ return;
+ }
+
+ override void visit(CatExp exp)
+ {
+ // https://dlang.org/spec/expression.html#cat_expressions
+ //printf("CatExp.semantic() %s\n", toChars());
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb1 = exp.e1.type.toBasetype();
+ Type tb2 = exp.e2.type.toBasetype();
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f1 || f2)
+ return setError();
+
+ Type tb1next = tb1.nextOf();
+ Type tb2next = tb2.nextOf();
+
+ // Check for: array ~ array
+ if (tb1next && tb2next && (tb1next.implicitConvTo(tb2next) >= MATCH.constant || tb2next.implicitConvTo(tb1next) >= MATCH.constant || exp.e1.op == TOK.arrayLiteral && exp.e1.implicitConvTo(tb2) || exp.e2.op == TOK.arrayLiteral && exp.e2.implicitConvTo(tb1)))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=9248
+ * Here to avoid the case of:
+ * void*[] a = [cast(void*)1];
+ * void*[] b = [cast(void*)2];
+ * a ~ b;
+ * becoming:
+ * a ~ [cast(void*)b];
+ */
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14682
+ * Also to avoid the case of:
+ * int[][] a;
+ * a ~ [];
+ * becoming:
+ * a ~ cast(int[])[];
+ */
+ goto Lpeer;
+ }
+
+ // Check for: array ~ element
+ if ((tb1.ty == Tsarray || tb1.ty == Tarray) && tb2.ty != Tvoid)
+ {
+ if (exp.e1.op == TOK.arrayLiteral)
+ {
+ exp.e2 = doCopyOrMove(sc, exp.e2);
+ // https://issues.dlang.org/show_bug.cgi?id=14686
+ // Postblit call appears in AST, and this is
+ // finally translated to an ArrayLiteralExp in below optimize().
+ }
+ else if (exp.e1.op == TOK.string_)
+ {
+ // No postblit call exists on character (integer) value.
+ }
+ else
+ {
+ if (exp.e2.checkPostblit(sc, tb2))
+ return setError();
+ // Postblit call will be done in runtime helper function
+ }
+
+ if (exp.e1.op == TOK.arrayLiteral && exp.e1.implicitConvTo(tb2.arrayOf()))
+ {
+ exp.e1 = exp.e1.implicitCastTo(sc, tb2.arrayOf());
+ exp.type = tb2.arrayOf();
+ goto L2elem;
+ }
+ if (exp.e2.implicitConvTo(tb1next) >= MATCH.convert)
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, tb1next);
+ exp.type = tb1next.arrayOf();
+ L2elem:
+ if (tb2.ty == Tarray || tb2.ty == Tsarray)
+ {
+ // Make e2 into [e2]
+ exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.type, exp.e2);
+ }
+ else if (checkNewEscape(sc, exp.e2, false))
+ return setError();
+ result = exp.optimize(WANTvalue);
+ return;
+ }
+ }
+ // Check for: element ~ array
+ if ((tb2.ty == Tsarray || tb2.ty == Tarray) && tb1.ty != Tvoid)
+ {
+ if (exp.e2.op == TOK.arrayLiteral)
+ {
+ exp.e1 = doCopyOrMove(sc, exp.e1);
+ }
+ else if (exp.e2.op == TOK.string_)
+ {
+ }
+ else
+ {
+ if (exp.e1.checkPostblit(sc, tb1))
+ return setError();
+ }
+
+ if (exp.e2.op == TOK.arrayLiteral && exp.e2.implicitConvTo(tb1.arrayOf()))
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, tb1.arrayOf());
+ exp.type = tb1.arrayOf();
+ goto L1elem;
+ }
+ if (exp.e1.implicitConvTo(tb2next) >= MATCH.convert)
+ {
+ exp.e1 = exp.e1.implicitCastTo(sc, tb2next);
+ exp.type = tb2next.arrayOf();
+ L1elem:
+ if (tb1.ty == Tarray || tb1.ty == Tsarray)
+ {
+ // Make e1 into [e1]
+ exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.type, exp.e1);
+ }
+ else if (checkNewEscape(sc, exp.e1, false))
+ return setError();
+ result = exp.optimize(WANTvalue);
+ return;
+ }
+ }
+
+ Lpeer:
+ if ((tb1.ty == Tsarray || tb1.ty == Tarray) && (tb2.ty == Tsarray || tb2.ty == Tarray) && (tb1next.mod || tb2next.mod) && (tb1next.mod != tb2next.mod))
+ {
+ Type t1 = tb1next.mutableOf().constOf().arrayOf();
+ Type t2 = tb2next.mutableOf().constOf().arrayOf();
+ if (exp.e1.op == TOK.string_ && !(cast(StringExp)exp.e1).committed)
+ exp.e1.type = t1;
+ else
+ exp.e1 = exp.e1.castTo(sc, t1);
+ if (exp.e2.op == TOK.string_ && !(cast(StringExp)exp.e2).committed)
+ exp.e2.type = t2;
+ else
+ exp.e2 = exp.e2.castTo(sc, t2);
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ exp.type = exp.type.toHeadMutable();
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tsarray)
+ exp.type = tb.nextOf().arrayOf();
+ if (exp.type.ty == Tarray && tb1next && tb2next && tb1next.mod != tb2next.mod)
+ {
+ exp.type = exp.type.nextOf().toHeadMutable().arrayOf();
+ }
+ if (Type tbn = tb.nextOf())
+ {
+ if (exp.checkPostblit(sc, tbn))
+ return setError();
+ }
+ Type t1 = exp.e1.type.toBasetype();
+ Type t2 = exp.e2.type.toBasetype();
+ if ((t1.ty == Tarray || t1.ty == Tsarray) &&
+ (t2.ty == Tarray || t2.ty == Tsarray))
+ {
+ // Normalize to ArrayLiteralExp or StringExp as far as possible
+ e = exp.optimize(WANTvalue);
+ }
+ else
+ {
+ //printf("(%s) ~ (%s)\n", e1.toChars(), e2.toChars());
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ result = e;
+ }
+
+ override void visit(MulExp exp)
+ {
+ version (none)
+ {
+ printf("MulExp::semantic() %s\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (exp.type.isfloating())
+ {
+ Type t1 = exp.e1.type;
+ Type t2 = exp.e2.type;
+
+ if (t1.isreal())
+ {
+ exp.type = t2;
+ }
+ else if (t2.isreal())
+ {
+ exp.type = t1;
+ }
+ else if (t1.isimaginary())
+ {
+ if (t2.isimaginary())
+ {
+ switch (t1.toBasetype().ty)
+ {
+ case Timaginary32:
+ exp.type = Type.tfloat32;
+ break;
+
+ case Timaginary64:
+ exp.type = Type.tfloat64;
+ break;
+
+ case Timaginary80:
+ exp.type = Type.tfloat80;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ // iy * iv = -yv
+ exp.e1.type = exp.type;
+ exp.e2.type = exp.type;
+ e = new NegExp(exp.loc, exp);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ else
+ exp.type = t2; // t2 is complex
+ }
+ else if (t2.isimaginary())
+ {
+ exp.type = t1; // t1 is complex
+ }
+ }
+ else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ result = exp;
+ }
+
+ override void visit(DivExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (exp.type.isfloating())
+ {
+ Type t1 = exp.e1.type;
+ Type t2 = exp.e2.type;
+
+ if (t1.isreal())
+ {
+ exp.type = t2;
+ if (t2.isimaginary())
+ {
+ // x/iv = i(-x/v)
+ exp.e2.type = t1;
+ e = new NegExp(exp.loc, exp);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+ else if (t2.isreal())
+ {
+ exp.type = t1;
+ }
+ else if (t1.isimaginary())
+ {
+ if (t2.isimaginary())
+ {
+ switch (t1.toBasetype().ty)
+ {
+ case Timaginary32:
+ exp.type = Type.tfloat32;
+ break;
+
+ case Timaginary64:
+ exp.type = Type.tfloat64;
+ break;
+
+ case Timaginary80:
+ exp.type = Type.tfloat80;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ else
+ exp.type = t2; // t2 is complex
+ }
+ else if (t2.isimaginary())
+ {
+ exp.type = t1; // t1 is complex
+ }
+ }
+ else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ result = exp;
+ }
+
+ override void visit(ModExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (exp.type.isfloating())
+ {
+ exp.type = exp.e1.type;
+ if (exp.e2.type.iscomplex())
+ {
+ exp.error("cannot perform modulo complex arithmetic");
+ return setError();
+ }
+ }
+ result = exp;
+ }
+
+ override void visit(PowExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ //printf("PowExp::semantic() %s\n", toChars());
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ // First, attempt to fold the expression.
+ e = exp.optimize(WANTvalue);
+ if (e.op != TOK.pow)
+ {
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ Module mmath = loadStdMath();
+ if (!mmath)
+ {
+ e.error("`%s` requires `std.math` for `^^` operators", e.toChars());
+ return setError();
+ }
+ e = new ScopeExp(exp.loc, mmath);
+
+ if (exp.e2.op == TOK.float64 && exp.e2.toReal() == CTFloat.half)
+ {
+ // Replace e1 ^^ 0.5 with .std.math.sqrt(e1)
+ e = new CallExp(exp.loc, new DotIdExp(exp.loc, e, Id._sqrt), exp.e1);
+ }
+ else
+ {
+ // Replace e1 ^^ e2 with .std.math.pow(e1, e2)
+ e = new CallExp(exp.loc, new DotIdExp(exp.loc, e, Id._pow), exp.e1, exp.e2);
+ }
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ override void visit(ShlExp exp)
+ {
+ //printf("ShlExp::semantic(), type = %p\n", type);
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ exp.e1 = integralPromotions(exp.e1, sc);
+ if (exp.e2.type.toBasetype().ty != Tvector)
+ exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+
+ exp.type = exp.e1.type;
+ result = exp;
+ }
+
+ override void visit(ShrExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ exp.e1 = integralPromotions(exp.e1, sc);
+ if (exp.e2.type.toBasetype().ty != Tvector)
+ exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+
+ exp.type = exp.e1.type;
+ result = exp;
+ }
+
+ override void visit(UshrExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ exp.e1 = integralPromotions(exp.e1, sc);
+ if (exp.e2.type.toBasetype().ty != Tvector)
+ exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+
+ exp.type = exp.e1.type;
+ result = exp;
+ }
+
+ override void visit(AndExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool)
+ {
+ exp.type = exp.e1.type;
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(OrExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool)
+ {
+ exp.type = exp.e1.type;
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(XorExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool)
+ {
+ exp.type = exp.e1.type;
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(LogicalExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("LogicalExp::semantic() %s\n", exp.toChars());
+ }
+
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.setNoderefOperands();
+
+ Expression e1x = exp.e1.expressionSemantic(sc);
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e1x.op == TOK.type)
+ e1x = resolveAliasThis(sc, e1x);
+
+ e1x = resolveProperties(sc, e1x);
+ e1x = e1x.toBoolean(sc);
+
+ if (sc.flags & SCOPE.condition)
+ {
+ /* If in static if, don't evaluate e2 if we don't have to.
+ */
+ e1x = e1x.optimize(WANTvalue);
+ if (e1x.isBool(exp.op == TOK.orOr))
+ {
+ result = IntegerExp.createBool(exp.op == TOK.orOr);
+ return;
+ }
+ }
+
+ CtorFlow ctorflow = sc.ctorflow.clone();
+ Expression e2x = exp.e2.expressionSemantic(sc);
+ sc.merge(exp.loc, ctorflow);
+ ctorflow.freeFieldinit();
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e2x.op == TOK.type)
+ e2x = resolveAliasThis(sc, e2x);
+
+ e2x = resolveProperties(sc, e2x);
+
+ auto f1 = checkNonAssignmentArrayOp(e1x);
+ auto f2 = checkNonAssignmentArrayOp(e2x);
+ if (f1 || f2)
+ return setError();
+
+ // Unless the right operand is 'void', the expression is converted to 'bool'.
+ if (e2x.type.ty != Tvoid)
+ e2x = e2x.toBoolean(sc);
+
+ if (e2x.op == TOK.type || e2x.op == TOK.scope_)
+ {
+ exp.error("`%s` is not an expression", exp.e2.toChars());
+ return setError();
+ }
+ if (e1x.op == TOK.error || e1x.type.ty == Tnoreturn)
+ {
+ result = e1x;
+ return;
+ }
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+
+ // The result type is 'bool', unless the right operand has type 'void'.
+ if (e2x.type.ty == Tvoid)
+ exp.type = Type.tvoid;
+ else
+ exp.type = Type.tbool;
+
+ exp.e1 = e1x;
+ exp.e2 = e2x;
+ result = exp;
+ }
+
+
+ override void visit(CmpExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CmpExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.setNoderefOperands();
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Type t1 = exp.e1.type.toBasetype();
+ Type t2 = exp.e2.type.toBasetype();
+ if (t1.ty == Tclass && exp.e2.op == TOK.null_ || t2.ty == Tclass && exp.e1.op == TOK.null_)
+ {
+ exp.error("do not use `null` when comparing class types");
+ return setError();
+ }
+
+ TOK cmpop;
+ if (auto e = exp.op_overload(sc, &cmpop))
+ {
+ if (!e.type.isscalar() && e.type.equals(exp.e1.type))
+ {
+ exp.error("recursive `opCmp` expansion");
+ return setError();
+ }
+ if (e.op == TOK.call)
+ {
+ e = new CmpExp(cmpop, exp.loc, e, IntegerExp.literal!0);
+ e = e.expressionSemantic(sc);
+ }
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f1 || f2)
+ return setError();
+
+ exp.type = Type.tbool;
+
+ // Special handling for array comparisons
+ Expression arrayLowering = null;
+ t1 = exp.e1.type.toBasetype();
+ t2 = exp.e2.type.toBasetype();
+ if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && (t2.ty == Tarray || t2.ty == Tsarray || t2.ty == Tpointer))
+ {
+ Type t1next = t1.nextOf();
+ Type t2next = t2.nextOf();
+ if (t1next.implicitConvTo(t2next) < MATCH.constant && t2next.implicitConvTo(t1next) < MATCH.constant && (t1next.ty != Tvoid && t2next.ty != Tvoid))
+ {
+ exp.error("array comparison type mismatch, `%s` vs `%s`", t1next.toChars(), t2next.toChars());
+ return setError();
+ }
+ if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray))
+ {
+ if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays"))
+ return setError();
+
+ // Lower to object.__cmp(e1, e2)
+ Expression al = new IdentifierExp(exp.loc, Id.empty);
+ al = new DotIdExp(exp.loc, al, Id.object);
+ al = new DotIdExp(exp.loc, al, Id.__cmp);
+ al = al.expressionSemantic(sc);
+
+ auto arguments = new Expressions(2);
+ (*arguments)[0] = exp.e1;
+ (*arguments)[1] = exp.e2;
+
+ al = new CallExp(exp.loc, al, arguments);
+ al = new CmpExp(exp.op, exp.loc, al, IntegerExp.literal!0);
+
+ arrayLowering = al;
+ }
+ }
+ else if (t1.ty == Tstruct || t2.ty == Tstruct || (t1.ty == Tclass && t2.ty == Tclass))
+ {
+ if (t2.ty == Tstruct)
+ exp.error("need member function `opCmp()` for %s `%s` to compare", t2.toDsymbol(sc).kind(), t2.toChars());
+ else
+ exp.error("need member function `opCmp()` for %s `%s` to compare", t1.toDsymbol(sc).kind(), t1.toChars());
+ return setError();
+ }
+ else if (t1.iscomplex() || t2.iscomplex())
+ {
+ exp.error("compare not defined for complex operands");
+ return setError();
+ }
+ else if (t1.ty == Taarray || t2.ty == Taarray)
+ {
+ exp.error("`%s` is not defined for associative arrays", Token.toChars(exp.op));
+ return setError();
+ }
+ else if (!target.isVectorOpSupported(t1, exp.op, t2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ else
+ {
+ bool r1 = exp.e1.checkValue() || exp.e1.checkSharedAccess(sc);
+ bool r2 = exp.e2.checkValue() || exp.e2.checkSharedAccess(sc);
+ if (r1 || r2)
+ return setError();
+ }
+
+ //printf("CmpExp: %s, type = %s\n", e.toChars(), e.type.toChars());
+ if (arrayLowering)
+ {
+ arrayLowering = arrayLowering.expressionSemantic(sc);
+ result = arrayLowering;
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ override void visit(InExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type t2b = exp.e2.type.toBasetype();
+ switch (t2b.ty)
+ {
+ case Taarray:
+ {
+ TypeAArray ta = cast(TypeAArray)t2b;
+
+ // Special handling for array keys
+ if (!arrayTypeCompatibleWithoutCasting(exp.e1.type, ta.index))
+ {
+ // Convert key to type of key
+ exp.e1 = exp.e1.implicitCastTo(sc, ta.index);
+ }
+
+ semanticTypeInfo(sc, ta.index);
+
+ // Return type is pointer to value
+ exp.type = ta.nextOf().pointerTo();
+ break;
+ }
+
+ case Terror:
+ return setError();
+
+ default:
+ result = exp.incompatibleTypes();
+ return;
+ }
+ result = exp;
+ }
+
+ override void visit(RemoveExp e)
+ {
+ if (Expression ex = binSemantic(e, sc))
+ {
+ result = ex;
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(EqualExp exp)
+ {
+ //printf("EqualExp::semantic('%s')\n", exp.toChars());
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.setNoderefOperands();
+
+ if (auto e = binSemanticProp(exp, sc))
+ {
+ result = e;
+ return;
+ }
+ if (exp.e1.op == TOK.type || exp.e2.op == TOK.type)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=12520
+ * empty tuples are represented as types so special cases are added
+ * so that they can be compared for equality with tuples of values.
+ */
+ static auto extractTypeTupAndExpTup(Expression e)
+ {
+ static struct Result { bool ttEmpty; bool te; }
+ auto tt = e.op == TOK.type ? e.isTypeExp().type.isTypeTuple() : null;
+ return Result(tt && (!tt.arguments || !tt.arguments.dim), e.isTupleExp() !is null);
+ }
+ auto tups1 = extractTypeTupAndExpTup(exp.e1);
+ auto tups2 = extractTypeTupAndExpTup(exp.e2);
+ // AliasSeq!() == AliasSeq!(<at least a value>)
+ if (tups1.ttEmpty && tups2.te)
+ {
+ result = IntegerExp.createBool(exp.op != TOK.equal);
+ return;
+ }
+ // AliasSeq!(<at least a value>) == AliasSeq!()
+ else if (tups1.te && tups2.ttEmpty)
+ {
+ result = IntegerExp.createBool(exp.op != TOK.equal);
+ return;
+ }
+ // AliasSeq!() == AliasSeq!()
+ else if (tups1.ttEmpty && tups2.ttEmpty)
+ {
+ result = IntegerExp.createBool(exp.op == TOK.equal);
+ return;
+ }
+ // otherwise, two types are really not comparable
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ {
+ auto t1 = exp.e1.type;
+ auto t2 = exp.e2.type;
+ if (t1.ty == Tenum && t2.ty == Tenum && !t1.equivalent(t2))
+ exp.error("Comparison between different enumeration types `%s` and `%s`; If this behavior is intended consider using `std.conv.asOriginalType`",
+ t1.toChars(), t2.toChars());
+ }
+
+ /* Before checking for operator overloading, check to see if we're
+ * comparing the addresses of two statics. If so, we can just see
+ * if they are the same symbol.
+ */
+ if (exp.e1.op == TOK.address && exp.e2.op == TOK.address)
+ {
+ AddrExp ae1 = cast(AddrExp)exp.e1;
+ AddrExp ae2 = cast(AddrExp)exp.e2;
+ if (ae1.e1.op == TOK.variable && ae2.e1.op == TOK.variable)
+ {
+ VarExp ve1 = cast(VarExp)ae1.e1;
+ VarExp ve2 = cast(VarExp)ae2.e1;
+ if (ve1.var == ve2.var)
+ {
+ // They are the same, result is 'true' for ==, 'false' for !=
+ result = IntegerExp.createBool(exp.op == TOK.equal);
+ return;
+ }
+ }
+ }
+
+ Type t1 = exp.e1.type.toBasetype();
+ Type t2 = exp.e2.type.toBasetype();
+
+ // Indicates whether the comparison of the 2 specified array types
+ // requires an object.__equals() lowering.
+ static bool needsDirectEq(Type t1, Type t2, Scope* sc)
+ {
+ Type t1n = t1.nextOf().toBasetype();
+ Type t2n = t2.nextOf().toBasetype();
+ if ((t1n.ty.isSomeChar && t2n.ty.isSomeChar) ||
+ (t1n.ty == Tvoid || t2n.ty == Tvoid))
+ {
+ return false;
+ }
+ if (t1n.constOf() != t2n.constOf())
+ return true;
+
+ Type t = t1n;
+ while (t.toBasetype().nextOf())
+ t = t.nextOf().toBasetype();
+ if (auto ts = t.isTypeStruct())
+ {
+ // semanticTypeInfo() makes sure hasIdentityEquals has been computed
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ semanticTypeInfo(sc, ts);
+
+ return ts.sym.hasIdentityEquals; // has custom opEquals
+ }
+
+ return false;
+ }
+
+ if (auto e = exp.op_overload(sc))
+ {
+ result = e;
+ return;
+ }
+
+
+ const isArrayComparison = (t1.ty == Tarray || t1.ty == Tsarray) &&
+ (t2.ty == Tarray || t2.ty == Tsarray);
+ const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc);
+
+ if (!needsArrayLowering)
+ {
+ if (auto e = typeCombine(exp, sc))
+ {
+ result = e;
+ return;
+ }
+ }
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f1 || f2)
+ return setError();
+
+ exp.type = Type.tbool;
+
+ if (!isArrayComparison)
+ {
+ if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating())
+ {
+ // Cast both to complex
+ exp.e1 = exp.e1.castTo(sc, Type.tcomplex80);
+ exp.e2 = exp.e2.castTo(sc, Type.tcomplex80);
+ }
+ }
+
+ // lower some array comparisons to object.__equals(e1, e2)
+ if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray))
+ {
+ //printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars());
+
+ if (!verifyHookExist(exp.loc, *sc, Id.__equals, "equal checks on arrays"))
+ return setError();
+
+ Expression __equals = new IdentifierExp(exp.loc, Id.empty);
+ Identifier id = Identifier.idPool("__equals");
+ __equals = new DotIdExp(exp.loc, __equals, Id.object);
+ __equals = new DotIdExp(exp.loc, __equals, id);
+
+ auto arguments = new Expressions(2);
+ (*arguments)[0] = exp.e1;
+ (*arguments)[1] = exp.e2;
+
+ __equals = new CallExp(exp.loc, __equals, arguments);
+ if (exp.op == TOK.notEqual)
+ {
+ __equals = new NotExp(exp.loc, __equals);
+ }
+ __equals = __equals.trySemantic(sc); // for better error message
+ if (!__equals)
+ {
+ exp.error("incompatible types for array comparison: `%s` and `%s`",
+ exp.e1.type.toChars(), exp.e2.type.toChars());
+ __equals = ErrorExp.get();
+ }
+
+ result = __equals;
+ return;
+ }
+
+ if (exp.e1.type.toBasetype().ty == Taarray)
+ semanticTypeInfo(sc, exp.e1.type.toBasetype());
+
+
+ if (!target.isVectorOpSupported(t1, exp.op, t2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ result = exp;
+ }
+
+ override void visit(IdentityExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.setNoderefOperands();
+
+ if (auto e = binSemanticProp(exp, sc))
+ {
+ result = e;
+ return;
+ }
+
+ if (auto e = typeCombine(exp, sc))
+ {
+ result = e;
+ return;
+ }
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f1 || f2)
+ return setError();
+
+ if (exp.e1.op == TOK.type || exp.e2.op == TOK.type)
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ exp.type = Type.tbool;
+
+ if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating())
+ {
+ // Cast both to complex
+ exp.e1 = exp.e1.castTo(sc, Type.tcomplex80);
+ exp.e2 = exp.e2.castTo(sc, Type.tcomplex80);
+ }
+
+ auto tb1 = exp.e1.type.toBasetype();
+ auto tb2 = exp.e2.type.toBasetype();
+ if (!target.isVectorOpSupported(tb1, exp.op, tb2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ if (exp.e1.op == TOK.call)
+ exp.e1 = (cast(CallExp)exp.e1).addDtorHook(sc);
+ if (exp.e2.op == TOK.call)
+ exp.e2 = (cast(CallExp)exp.e2).addDtorHook(sc);
+
+ if (exp.e1.type.toBasetype().ty == Tsarray ||
+ exp.e2.type.toBasetype().ty == Tsarray)
+ exp.deprecation("identity comparison of static arrays "
+ ~ "implicitly coerces them to slices, "
+ ~ "which are compared by reference");
+
+ result = exp;
+ }
+
+ override void visit(CondExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CondExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (exp.econd.op == TOK.dotIdentifier)
+ (cast(DotIdExp)exp.econd).noderef = true;
+
+ Expression ec = exp.econd.expressionSemantic(sc);
+ ec = resolveProperties(sc, ec);
+ ec = ec.toBoolean(sc);
+
+ CtorFlow ctorflow_root = sc.ctorflow.clone();
+ Expression e1x = exp.e1.expressionSemantic(sc);
+ e1x = resolveProperties(sc, e1x);
+
+ CtorFlow ctorflow1 = sc.ctorflow;
+ sc.ctorflow = ctorflow_root;
+ Expression e2x = exp.e2.expressionSemantic(sc);
+ e2x = resolveProperties(sc, e2x);
+
+ sc.merge(exp.loc, ctorflow1);
+ ctorflow1.freeFieldinit();
+
+ if (ec.op == TOK.error)
+ {
+ result = ec;
+ return;
+ }
+ if (ec.type == Type.terror)
+ return setError();
+ exp.econd = ec;
+
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ if (e1x.type == Type.terror)
+ return setError();
+ exp.e1 = e1x;
+
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+ if (e2x.type == Type.terror)
+ return setError();
+ exp.e2 = e2x;
+
+ auto f0 = checkNonAssignmentArrayOp(exp.econd);
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f0 || f1 || f2)
+ return setError();
+
+ Type t1 = exp.e1.type;
+ Type t2 = exp.e2.type;
+ if (t1.ty == Tnoreturn)
+ {
+ exp.type = t2;
+ }
+ else if (t2.ty == Tnoreturn)
+ {
+ exp.type = t1;
+ }
+ // If either operand is void the result is void, we have to cast both
+ // the expression to void so that we explicitly discard the expression
+ // value if any
+ // https://issues.dlang.org/show_bug.cgi?id=16598
+ else if (t1.ty == Tvoid || t2.ty == Tvoid)
+ {
+ exp.type = Type.tvoid;
+ exp.e1 = exp.e1.castTo(sc, exp.type);
+ exp.e2 = exp.e2.castTo(sc, exp.type);
+ }
+ else if (t1 == t2)
+ exp.type = t1;
+ else
+ {
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ switch (exp.e1.type.toBasetype().ty)
+ {
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type);
+ break;
+ default:
+ break;
+ }
+ switch (exp.e2.type.toBasetype().ty)
+ {
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ exp.e1 = exp.e1.castTo(sc, exp.e2.type);
+ break;
+ default:
+ break;
+ }
+ if (exp.type.toBasetype().ty == Tarray)
+ {
+ exp.e1 = exp.e1.castTo(sc, exp.type);
+ exp.e2 = exp.e2.castTo(sc, exp.type);
+ }
+ }
+ exp.type = exp.type.merge2();
+ version (none)
+ {
+ printf("res: %s\n", exp.type.toChars());
+ printf("e1 : %s\n", exp.e1.type.toChars());
+ printf("e2 : %s\n", exp.e2.type.toChars());
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14696
+ * If either e1 or e2 contain temporaries which need dtor,
+ * make them conditional.
+ * Rewrite:
+ * cond ? (__tmp1 = ..., __tmp1) : (__tmp2 = ..., __tmp2)
+ * to:
+ * (auto __cond = cond) ? (... __tmp1) : (... __tmp2)
+ * and replace edtors of __tmp1 and __tmp2 with:
+ * __tmp1.edtor --> __cond && __tmp1.dtor()
+ * __tmp2.edtor --> __cond || __tmp2.dtor()
+ */
+ exp.hookDtors(sc);
+
+ result = exp;
+ }
+
+ override void visit(GenericExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("GenericExp::semantic('%s')\n", exp.toChars());
+ }
+ // C11 6.5.1.1 Generic Selection
+
+ auto ec = exp.cntlExp.expressionSemantic(sc);
+ bool errors = ec.isErrorExp() !is null;
+ auto tc = ec.type;
+
+ auto types = (*exp.types)[];
+ foreach (i, ref t; types)
+ {
+ if (!t)
+ continue; // `default:` case
+ t = t.typeSemantic(ec.loc, sc);
+ if (t.isTypeError())
+ {
+ errors = true;
+ continue;
+ }
+
+ /* C11 6.5.1-2 duplicate check
+ */
+ /* C11 distinguishes int, long, and long long. But D doesn't, so depending on the
+ * C target, a long may have the same type as `int` in the D type system.
+ * So, skip checks when this may be the case. Later pick the first match
+ */
+ if (
+ (t.ty == Tint32 || t.ty == Tuns32) && target.c.longsize == 4 ||
+ (t.ty == Tint64 || t.ty == Tuns64) && target.c.longsize == 8 ||
+ (t.ty == Tfloat64 || t.ty == Timaginary64 || t.ty == Tcomplex64) && target.c.long_doublesize == 8
+ )
+ continue;
+
+ foreach (t2; types[0 .. i])
+ {
+ if (t2 && t2.equals(t))
+ {
+ error(ec.loc, "generic association type `%s` can only appear once", t.toChars());
+ errors = true;
+ break;
+ }
+ }
+ }
+
+ auto exps = (*exp.exps)[];
+ foreach (ref e; exps)
+ {
+ e = e.expressionSemantic(sc);
+ if (e.isErrorExp())
+ errors = true;
+ }
+
+ if (errors)
+ return setError();
+
+ enum size_t None = ~0;
+ size_t imatch = None;
+ size_t idefault = None;
+ foreach (const i, t; types)
+ {
+ if (t)
+ {
+ /* if tc is compatible with t, it's a match
+ * C11 6.2.7 defines a compatible type as being the same type, including qualifiers
+ */
+ if (tc.equals(t))
+ {
+ assert(imatch == None);
+ imatch = i;
+ break; // pick first match
+ }
+ }
+ else
+ idefault = i; // multiple defaults are not allowed, and are caught by cparse
+ }
+
+ if (imatch == None)
+ imatch = idefault;
+ if (imatch == None)
+ {
+ error(exp.loc, "no compatible generic association type for controlling expression type `%s`", tc.toChars());
+ return setError();
+ }
+
+ result = exps[imatch];
+ }
+
+ override void visit(FileInitExp e)
+ {
+ //printf("FileInitExp::semantic()\n");
+ e.type = Type.tstring;
+ result = e;
+ }
+
+ override void visit(LineInitExp e)
+ {
+ e.type = Type.tint32;
+ result = e;
+ }
+
+ override void visit(ModuleInitExp e)
+ {
+ //printf("ModuleInitExp::semantic()\n");
+ e.type = Type.tstring;
+ result = e;
+ }
+
+ override void visit(FuncInitExp e)
+ {
+ //printf("FuncInitExp::semantic()\n");
+ e.type = Type.tstring;
+ if (sc.func)
+ {
+ result = e.resolveLoc(Loc.initial, sc);
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(PrettyFuncInitExp e)
+ {
+ //printf("PrettyFuncInitExp::semantic()\n");
+ e.type = Type.tstring;
+ if (sc.func)
+ {
+ result = e.resolveLoc(Loc.initial, sc);
+ return;
+ }
+
+ result = e;
+ }
+}
+
+/**********************************
+ * Try to run semantic routines.
+ * If they fail, return NULL.
+ */
+Expression trySemantic(Expression exp, Scope* sc)
+{
+ //printf("+trySemantic(%s)\n", exp.toChars());
+ uint errors = global.startGagging();
+ Expression e = expressionSemantic(exp, sc);
+ if (global.endGagging(errors))
+ {
+ e = null;
+ }
+ //printf("-trySemantic(%s)\n", exp.toChars());
+ return e;
+}
+
+/**************************
+ * Helper function for easy error propagation.
+ * If error occurs, returns ErrorExp. Otherwise returns NULL.
+ */
+Expression unaSemantic(UnaExp e, Scope* sc)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("UnaExp::semantic('%s')\n", e.toChars());
+ }
+ Expression e1x = e.e1.expressionSemantic(sc);
+ if (e1x.op == TOK.error)
+ return e1x;
+ e.e1 = e1x;
+ return null;
+}
+
+/**************************
+ * Helper function for easy error propagation.
+ * If error occurs, returns ErrorExp. Otherwise returns NULL.
+ */
+Expression binSemantic(BinExp e, Scope* sc)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("BinExp::semantic('%s')\n", e.toChars());
+ }
+ Expression e1x = e.e1.expressionSemantic(sc);
+ Expression e2x = e.e2.expressionSemantic(sc);
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e1x.op == TOK.type)
+ e1x = resolveAliasThis(sc, e1x);
+ if (e2x.op == TOK.type)
+ e2x = resolveAliasThis(sc, e2x);
+
+ if (e1x.op == TOK.error)
+ return e1x;
+ if (e2x.op == TOK.error)
+ return e2x;
+ e.e1 = e1x;
+ e.e2 = e2x;
+ return null;
+}
+
+Expression binSemanticProp(BinExp e, Scope* sc)
+{
+ if (Expression ex = binSemantic(e, sc))
+ return ex;
+ Expression e1x = resolveProperties(sc, e.e1);
+ Expression e2x = resolveProperties(sc, e.e2);
+ if (e1x.op == TOK.error)
+ return e1x;
+ if (e2x.op == TOK.error)
+ return e2x;
+ e.e1 = e1x;
+ e.e2 = e2x;
+ return null;
+}
+
+// entrypoint for semantic ExpressionSemanticVisitor
+extern (C++) Expression expressionSemantic(Expression e, Scope* sc)
+{
+ scope v = new ExpressionSemanticVisitor(sc);
+ e.accept(v);
+ return v.result;
+}
+
+Expression semanticX(DotIdExp exp, Scope* sc)
+{
+ //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars());
+ if (Expression ex = unaSemantic(exp, sc))
+ return ex;
+
+ if (exp.ident == Id._mangleof)
+ {
+ // symbol.mangleof
+ Dsymbol ds;
+ switch (exp.e1.op)
+ {
+ case TOK.scope_:
+ ds = (cast(ScopeExp)exp.e1).sds;
+ goto L1;
+ case TOK.variable:
+ ds = (cast(VarExp)exp.e1).var;
+ goto L1;
+ case TOK.dotVariable:
+ ds = (cast(DotVarExp)exp.e1).var;
+ goto L1;
+ case TOK.overloadSet:
+ ds = (cast(OverExp)exp.e1).vars;
+ goto L1;
+ case TOK.template_:
+ {
+ TemplateExp te = cast(TemplateExp)exp.e1;
+ ds = te.fd ? cast(Dsymbol)te.fd : te.td;
+ }
+ L1:
+ {
+ assert(ds);
+ if (auto f = ds.isFuncDeclaration())
+ {
+ if (f.checkForwardRef(exp.loc))
+ {
+ return ErrorExp.get();
+ }
+ if (f.flags & (FUNCFLAG.purityInprocess | FUNCFLAG.safetyInprocess |
+ FUNCFLAG.nothrowInprocess | FUNCFLAG.nogcInprocess))
+ {
+ f.error(exp.loc, "cannot retrieve its `.mangleof` while inferring attributes");
+ return ErrorExp.get();
+ }
+ }
+ OutBuffer buf;
+ mangleToBuffer(ds, &buf);
+ Expression e = new StringExp(exp.loc, buf.extractSlice());
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (exp.e1.op == TOK.variable && exp.e1.type.toBasetype().ty == Tsarray && exp.ident == Id.length)
+ {
+ // bypass checkPurity
+ return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0);
+ }
+
+ if (exp.e1.op == TOK.dot)
+ {
+ }
+ else
+ {
+ exp.e1 = resolvePropertiesX(sc, exp.e1);
+ }
+ if (exp.e1.op == TOK.tuple && exp.ident == Id.offsetof)
+ {
+ /* 'distribute' the .offsetof to each of the tuple elements.
+ */
+ TupleExp te = cast(TupleExp)exp.e1;
+ auto exps = new Expressions(te.exps.dim);
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression e = (*te.exps)[i];
+ e = e.expressionSemantic(sc);
+ e = new DotIdExp(e.loc, e, Id.offsetof);
+ (*exps)[i] = e;
+ }
+ // Don't evaluate te.e0 in runtime
+ Expression e = new TupleExp(exp.loc, null, exps);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (exp.e1.op == TOK.tuple && exp.ident == Id.length)
+ {
+ TupleExp te = cast(TupleExp)exp.e1;
+ // Don't evaluate te.e0 in runtime
+ Expression e = new IntegerExp(exp.loc, te.exps.dim, Type.tsize_t);
+ return e;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=14416
+ // Template has no built-in properties except for 'stringof'.
+ if ((exp.e1.op == TOK.dotTemplateDeclaration || exp.e1.op == TOK.template_) && exp.ident != Id.stringof)
+ {
+ exp.error("template `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars());
+ return ErrorExp.get();
+ }
+ if (!exp.e1.type)
+ {
+ exp.error("expression `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars());
+ return ErrorExp.get();
+ }
+
+ return exp;
+}
+
+// Resolve e1.ident without seeing UFCS.
+// If flag == 1, stop "not a property" error and return NULL.
+Expression semanticY(DotIdExp exp, Scope* sc, int flag)
+{
+ //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars());
+
+ //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
+
+ /* Special case: rewrite this.id and super.id
+ * to be classtype.id and baseclasstype.id
+ * if we have no this pointer.
+ */
+ if ((exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_) && !hasThis(sc))
+ {
+ if (AggregateDeclaration ad = sc.getStructClassScope())
+ {
+ if (exp.e1.op == TOK.this_)
+ {
+ exp.e1 = new TypeExp(exp.e1.loc, ad.type);
+ }
+ else
+ {
+ ClassDeclaration cd = ad.isClassDeclaration();
+ if (cd && cd.baseClass)
+ exp.e1 = new TypeExp(exp.e1.loc, cd.baseClass.type);
+ }
+ }
+ }
+
+ Expression e = semanticX(exp, sc);
+ if (e != exp)
+ return e;
+
+ Expression eleft;
+ Expression eright;
+ if (exp.e1.op == TOK.dot)
+ {
+ DotExp de = cast(DotExp)exp.e1;
+ eleft = de.e1;
+ eright = de.e2;
+ }
+ else
+ {
+ eleft = null;
+ eright = exp.e1;
+ }
+
+ Type t1b = exp.e1.type.toBasetype();
+
+ if (eright.op == TOK.scope_) // also used for template alias's
+ {
+ ScopeExp ie = cast(ScopeExp)eright;
+
+ int flags = SearchLocalsOnly;
+ /* Disable access to another module's private imports.
+ * The check for 'is sds our current module' is because
+ * the current module should have access to its own imports.
+ */
+ if (ie.sds.isModule() && ie.sds != sc._module)
+ flags |= IgnorePrivateImports;
+ if (sc.flags & SCOPE.ignoresymbolvisibility)
+ flags |= IgnoreSymbolVisibility;
+ Dsymbol s = ie.sds.search(exp.loc, exp.ident, flags);
+ /* Check for visibility before resolving aliases because public
+ * aliases to private symbols are public.
+ */
+ if (s && !(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc._module, s))
+ {
+ s = null;
+ }
+ if (s)
+ {
+ auto p = s.isPackage();
+ if (p && checkAccess(sc, p))
+ {
+ s = null;
+ }
+ }
+ if (s)
+ {
+ // if 's' is a tuple variable, the tuple is returned.
+ s = s.toAlias();
+
+ exp.checkDeprecated(sc, s);
+ exp.checkDisabled(sc, s);
+
+ EnumMember em = s.isEnumMember();
+ if (em)
+ {
+ return em.getVarExp(exp.loc, sc);
+ }
+ VarDeclaration v = s.isVarDeclaration();
+ if (v)
+ {
+ //printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v.type.toChars());
+ if (!v.type ||
+ !v.type.deco && v.inuse)
+ {
+ if (v.inuse)
+ exp.error("circular reference to %s `%s`", v.kind(), v.toPrettyChars());
+ else
+ exp.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ if (v.type.ty == Terror)
+ return ErrorExp.get();
+
+ if ((v.storage_class & STC.manifest) && v._init && !exp.wantsym)
+ {
+ /* Normally, the replacement of a symbol with its initializer is supposed to be in semantic2().
+ * Introduced by https://github.com/dlang/dmd/pull/5588 which should probably
+ * be reverted. `wantsym` is the hack to work around the problem.
+ */
+ if (v.inuse)
+ {
+ error(exp.loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ e = v.expandInitializer(exp.loc);
+ v.inuse++;
+ e = e.expressionSemantic(sc);
+ v.inuse--;
+ return e;
+ }
+
+ if (v.needThis())
+ {
+ if (!eleft)
+ eleft = new ThisExp(exp.loc);
+ e = new DotVarExp(exp.loc, eleft, v);
+ e = e.expressionSemantic(sc);
+ }
+ else
+ {
+ e = new VarExp(exp.loc, v);
+ if (eleft)
+ {
+ e = new CommaExp(exp.loc, eleft, e);
+ e.type = v.type;
+ }
+ }
+ e = e.deref();
+ return e.expressionSemantic(sc);
+ }
+
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ //printf("it's a function\n");
+ if (!f.functionSemantic())
+ return ErrorExp.get();
+ if (f.needThis())
+ {
+ if (!eleft)
+ eleft = new ThisExp(exp.loc);
+ e = new DotVarExp(exp.loc, eleft, f, true);
+ e = e.expressionSemantic(sc);
+ }
+ else
+ {
+ e = new VarExp(exp.loc, f, true);
+ if (eleft)
+ {
+ e = new CommaExp(exp.loc, eleft, e);
+ e.type = f.type;
+ }
+ }
+ return e;
+ }
+ if (auto td = s.isTemplateDeclaration())
+ {
+ if (eleft)
+ e = new DotTemplateExp(exp.loc, eleft, td);
+ else
+ e = new TemplateExp(exp.loc, td);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (OverDeclaration od = s.isOverDeclaration())
+ {
+ e = new VarExp(exp.loc, od, true);
+ if (eleft)
+ {
+ e = new CommaExp(exp.loc, eleft, e);
+ e.type = Type.tvoid; // ambiguous type?
+ }
+ return e;
+ }
+ OverloadSet o = s.isOverloadSet();
+ if (o)
+ {
+ //printf("'%s' is an overload set\n", o.toChars());
+ return new OverExp(exp.loc, o);
+ }
+
+ if (auto t = s.getType())
+ {
+ return (new TypeExp(exp.loc, t)).expressionSemantic(sc);
+ }
+
+ TupleDeclaration tup = s.isTupleDeclaration();
+ if (tup)
+ {
+ if (eleft)
+ {
+ e = new DotVarExp(exp.loc, eleft, tup);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ e = new TupleExp(exp.loc, tup);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ ScopeDsymbol sds = s.isScopeDsymbol();
+ if (sds)
+ {
+ //printf("it's a ScopeDsymbol %s\n", ident.toChars());
+ e = new ScopeExp(exp.loc, sds);
+ e = e.expressionSemantic(sc);
+ if (eleft)
+ e = new DotExp(exp.loc, eleft, e);
+ return e;
+ }
+
+ Import imp = s.isImport();
+ if (imp)
+ {
+ ie = new ScopeExp(exp.loc, imp.pkg);
+ return ie.expressionSemantic(sc);
+ }
+ // BUG: handle other cases like in IdentifierExp::semantic()
+ debug
+ {
+ printf("s = '%s', kind = '%s'\n", s.toChars(), s.kind());
+ }
+ assert(0);
+ }
+ else if (exp.ident == Id.stringof)
+ {
+ e = new StringExp(exp.loc, ie.toString());
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (ie.sds.isPackage() || ie.sds.isImport() || ie.sds.isModule())
+ {
+ flag = 0;
+ }
+ if (flag)
+ return null;
+ s = ie.sds.search_correct(exp.ident);
+ if (s && symbolIsVisible(sc, s))
+ {
+ if (s.isPackage())
+ exp.error("undefined identifier `%s` in %s `%s`, perhaps add `static import %s;`", exp.ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars(), s.toPrettyChars());
+ else
+ exp.error("undefined identifier `%s` in %s `%s`, did you mean %s `%s`?", exp.ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars(), s.kind(), s.toChars());
+ }
+ else
+ exp.error("undefined identifier `%s` in %s `%s`", exp.ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars());
+ return ErrorExp.get();
+ }
+ else if (t1b.ty == Tpointer && exp.e1.type.ty != Tenum && exp.ident != Id._init && exp.ident != Id.__sizeof && exp.ident != Id.__xalignof && exp.ident != Id.offsetof && exp.ident != Id._mangleof && exp.ident != Id.stringof)
+ {
+ Type t1bn = t1b.nextOf();
+ if (flag)
+ {
+ AggregateDeclaration ad = isAggregate(t1bn);
+ if (ad && !ad.members) // https://issues.dlang.org/show_bug.cgi?id=11312
+ return null;
+ }
+
+ /* Rewrite:
+ * p.ident
+ * as:
+ * (*p).ident
+ */
+ if (flag && t1bn.ty == Tvoid)
+ return null;
+ e = new PtrExp(exp.loc, exp.e1);
+ e = e.expressionSemantic(sc);
+ return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
+ }
+ else if (exp.ident == Id.__xalignof &&
+ exp.e1.isVarExp() &&
+ exp.e1.isVarExp().var.isVarDeclaration() &&
+ exp.e1.isVarExp().var.isVarDeclaration().alignment)
+ {
+ // For `x.alignof` get the alignment of the variable, not the alignment of its type
+ const explicitAlignment = exp.e1.isVarExp().var.isVarDeclaration().alignment;
+ const naturalAlignment = exp.e1.type.alignsize();
+ const actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment);
+ e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t);
+ return e;
+ }
+ else
+ {
+ if (exp.e1.op == TOK.type || exp.e1.op == TOK.template_)
+ flag = 0;
+ e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
+ if (e)
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+}
+
+// Resolve e1.ident!tiargs without seeing UFCS.
+// If flag == 1, stop "not a property" error and return NULL.
+Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("DotTemplateInstanceExpY::semantic('%s')\n", exp.toChars());
+ }
+
+ static Expression errorExp()
+ {
+ return ErrorExp.get();
+ }
+
+ Expression e1 = exp.e1;
+
+ if (exp.ti.tempdecl && exp.ti.tempdecl.parent && exp.ti.tempdecl.parent.isTemplateMixin())
+ {
+ // if 'ti.tempdecl' happens to be found in a mixin template don't lose that info
+ // and do the symbol search in that context (Issue: 19476)
+ auto tm = cast(TemplateMixin)exp.ti.tempdecl.parent;
+ e1 = new DotExp(exp.e1.loc, exp.e1, new ScopeExp(tm.loc, tm));
+ }
+
+ auto die = new DotIdExp(exp.loc, e1, exp.ti.name);
+
+ Expression e = die.semanticX(sc);
+ if (e == die)
+ {
+ exp.e1 = die.e1; // take back
+ Type t1b = exp.e1.type.toBasetype();
+ if (t1b.ty == Tarray || t1b.ty == Tsarray || t1b.ty == Taarray || t1b.ty == Tnull || (t1b.isTypeBasic() && t1b.ty != Tvoid))
+ {
+ /* No built-in type has templatized properties, so do shortcut.
+ * It is necessary in: 1024.max!"a < b"
+ */
+ if (flag)
+ return null;
+ }
+ e = die.semanticY(sc, flag);
+ if (flag)
+ {
+ if (!e ||
+ isDotOpDispatch(e))
+ {
+ /* opDispatch!tiargs would be a function template that needs IFTI,
+ * so it's not a template
+ */
+ return null;
+ }
+ }
+ }
+ assert(e);
+
+ if (e.op == TOK.error)
+ return e;
+ if (e.op == TOK.dotVariable)
+ {
+ DotVarExp dve = cast(DotVarExp)e;
+ if (FuncDeclaration fd = dve.var.isFuncDeclaration())
+ {
+ if (TemplateDeclaration td = fd.findTemplateDeclRoot())
+ {
+ e = new DotTemplateExp(dve.loc, dve.e1, td);
+ e = e.expressionSemantic(sc);
+ }
+ }
+ else if (OverDeclaration od = dve.var.isOverDeclaration())
+ {
+ exp.e1 = dve.e1; // pull semantic() result
+
+ if (!exp.findTempDecl(sc))
+ goto Lerr;
+ if (exp.ti.needsTypeInference(sc))
+ return exp;
+ exp.ti.dsymbolSemantic(sc);
+ if (!exp.ti.inst || exp.ti.errors) // if template failed to expand
+ return errorExp();
+
+ if (Declaration v = exp.ti.toAlias().isDeclaration())
+ {
+ if (v.type && !v.type.deco)
+ v.type = v.type.typeSemantic(v.loc, sc);
+ return new DotVarExp(exp.loc, exp.e1, v)
+ .expressionSemantic(sc);
+ }
+ return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti))
+ .expressionSemantic(sc);
+ }
+ }
+ else if (e.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)e;
+ if (FuncDeclaration fd = ve.var.isFuncDeclaration())
+ {
+ if (TemplateDeclaration td = fd.findTemplateDeclRoot())
+ {
+ e = new TemplateExp(ve.loc, td)
+ .expressionSemantic(sc);
+ }
+ }
+ else if (OverDeclaration od = ve.var.isOverDeclaration())
+ {
+ exp.ti.tempdecl = od;
+ return new ScopeExp(exp.loc, exp.ti)
+ .expressionSemantic(sc);
+ }
+ }
+
+ if (e.op == TOK.dotTemplateDeclaration)
+ {
+ DotTemplateExp dte = cast(DotTemplateExp)e;
+ exp.e1 = dte.e1; // pull semantic() result
+
+ exp.ti.tempdecl = dte.td;
+ if (!exp.ti.semanticTiargs(sc))
+ return errorExp();
+ if (exp.ti.needsTypeInference(sc))
+ return exp;
+ exp.ti.dsymbolSemantic(sc);
+ if (!exp.ti.inst || exp.ti.errors) // if template failed to expand
+ return errorExp();
+
+ if (Declaration v = exp.ti.toAlias().isDeclaration())
+ {
+ if (v.isFuncDeclaration() || v.isVarDeclaration())
+ {
+ return new DotVarExp(exp.loc, exp.e1, v)
+ .expressionSemantic(sc);
+ }
+ }
+ return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti))
+ .expressionSemantic(sc);
+ }
+ else if (e.op == TOK.template_)
+ {
+ exp.ti.tempdecl = (cast(TemplateExp)e).td;
+ return new ScopeExp(exp.loc, exp.ti)
+ .expressionSemantic(sc);
+ }
+ else if (e.op == TOK.dot)
+ {
+ DotExp de = cast(DotExp)e;
+
+ if (de.e2.op == TOK.overloadSet)
+ {
+ if (!exp.findTempDecl(sc) || !exp.ti.semanticTiargs(sc))
+ {
+ return errorExp();
+ }
+ if (exp.ti.needsTypeInference(sc))
+ return exp;
+ exp.ti.dsymbolSemantic(sc);
+ if (!exp.ti.inst || exp.ti.errors) // if template failed to expand
+ return errorExp();
+
+ if (Declaration v = exp.ti.toAlias().isDeclaration())
+ {
+ if (v.type && !v.type.deco)
+ v.type = v.type.typeSemantic(v.loc, sc);
+ return new DotVarExp(exp.loc, exp.e1, v)
+ .expressionSemantic(sc);
+ }
+ return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti))
+ .expressionSemantic(sc);
+ }
+ }
+ else if (e.op == TOK.overloadSet)
+ {
+ OverExp oe = cast(OverExp)e;
+ exp.ti.tempdecl = oe.vars;
+ return new ScopeExp(exp.loc, exp.ti)
+ .expressionSemantic(sc);
+ }
+
+Lerr:
+ exp.error("`%s` isn't a template", e.toChars());
+ return errorExp();
+}
+
+/***************************************
+ * If expression is shared, check that we can access it.
+ * Give error message if not.
+ *
+ * Params:
+ * e = expression to check
+ * sc = context
+ * returnRef = Whether this expression is for a `return` statement
+ * off a `ref` function, in which case a single level
+ * of dereference is allowed (e.g. `shared(int)*`).
+ * Returns:
+ * true on error
+ */
+bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false)
+{
+ if (!global.params.noSharedAccess ||
+ sc.intypeof ||
+ sc.flags & SCOPE.ctfe)
+ {
+ return false;
+ }
+
+ //printf("checkSharedAccess() %s\n", e.toChars());
+
+ static extern(C++) final class SharedCheckVisitor : SemanticTimeTransitiveVisitor
+ {
+ /// In case we don't know which expression triggered it,
+ /// e.g. for `visit(Type)` overload
+ Expression original;
+ /// Where the result is stored (`true` == error)
+ bool result;
+ /// Whether we should allow one level of dereferencing
+ bool allowRef;
+
+ /// Ctor
+ this(Expression oe, bool allowRef_)
+ {
+ this.original = oe;
+ this.allowRef = allowRef_;
+ }
+
+ void sharedError(Expression e)
+ {
+ // https://dlang.org/phobos/core_atomic.html
+ e.error("direct access to shared `%s` is not allowed, see `core.atomic`", e.toChars());
+ this.result = true;
+ }
+
+ /// Introduce base class overrides
+ alias visit = SemanticTimeTransitiveVisitor.visit;
+
+ // Error by default
+ override void visit(Expression e)
+ {
+ if (e.type.isShared())
+ this.sharedError(e);
+ }
+
+ /// Ditto
+ override void visit(Type t)
+ {
+ // Note: This handles things like `new shared(Throwable).msg`,
+ // where accessing `msg` would violate `shared`.
+ if (t.isShared())
+ this.sharedError(this.original);
+ }
+
+ // Those have no indirections / can be ignored
+ override void visit(ErrorExp e) {}
+ override void visit(ComplexExp e) {}
+ override void visit(IntegerExp e) {}
+ override void visit(NullExp e) {}
+
+ override void visit(VarExp e)
+ {
+ if (!this.allowRef && e.var.type.isShared())
+ this.sharedError(e);
+ }
+
+ override void visit(AddrExp e)
+ {
+ this.allowRef = true;
+ e.e1.accept(this);
+ }
+
+ override void visit(PtrExp e)
+ {
+ if (!this.allowRef && e.type.isShared())
+ return this.sharedError(e);
+
+ if (e.e1.type.isShared())
+ return this.sharedError(e);
+
+ this.allowRef = false;
+ e.e1.accept(this);
+ }
+
+ override void visit(DotVarExp e)
+ {
+ auto fd = e.var.isFuncDeclaration();
+ const sharedFunc = fd && fd.type.isShared;
+
+ if (!this.allowRef && e.type.isShared() && !sharedFunc)
+ return this.sharedError(e);
+
+ // Allow to use `DotVarExp` within value types
+ if (e.e1.type.ty == Tsarray || e.e1.type.ty == Tstruct)
+ return e.e1.accept(this);
+
+ // If we end up with a single `VarExp`, it might be a `ref` param
+ // `shared ref T` param == `shared(T)*`.
+ if (auto ve = e.e1.isVarExp())
+ {
+ this.allowRef = this.allowRef && (ve.var.storage_class & STC.ref_);
+ return e.e1.accept(this);
+ }
+
+ this.allowRef = false;
+ return e.e1.accept(this);
+ }
+
+ override void visit(IndexExp e)
+ {
+ if (!this.allowRef && e.type.isShared())
+ return this.sharedError(e);
+
+ if (e.e1.type.isShared())
+ return this.sharedError(e);
+
+ this.allowRef = false;
+ e.e1.accept(this);
+ }
+
+ override void visit(CommaExp e)
+ {
+ // Cannot be `return ref` since we can't use the return,
+ // but it's better to show that error than an unrelated `shared` one
+ this.allowRef = true;
+ e.e2.accept(this);
+ }
+ }
+
+ scope visitor = new SharedCheckVisitor(e, returnRef);
+ e.accept(visitor);
+ return visitor.result;
+}
+
+
+
+/****************************************************
+ * Determine if `exp`, which gets its address taken, can do so safely.
+ * Params:
+ * sc = context
+ * exp = expression having its address taken
+ * v = the variable getting its address taken
+ * Returns:
+ * `true` if ok, `false` for error
+ */
+bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v)
+{
+ //printf("checkAddressVar(exp: %s, v: %s)\n", exp.toChars(), v.toChars());
+ if (v)
+ {
+ if (!v.canTakeAddressOf())
+ {
+ exp.error("cannot take address of `%s`", exp.toChars());
+ return false;
+ }
+ if (sc.func && !sc.intypeof && !v.isDataseg())
+ {
+ const(char)* p = v.isParameter() ? "parameter" : "local";
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ // Taking the address of v means it cannot be set to 'scope' later
+ v.storage_class &= ~STC.maybescope;
+ v.doNotInferScope = true;
+ if (exp.type.hasPointers() && v.storage_class & STC.scope_ &&
+ !(v.storage_class & STC.temp) &&
+ !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("cannot take address of `scope` %s `%s` in `@safe` function `%s`", p, v.toChars(), sc.func.toChars());
+ return false;
+ }
+ }
+ else if (!(sc.flags & SCOPE.debug_) &&
+ !(v.storage_class & STC.temp) &&
+ sc.func.setUnsafe())
+ {
+ exp.error("cannot take address of %s `%s` in `@safe` function `%s`", p, v.toChars(), sc.func.toChars());
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*******************************
+ * Checks the attributes of a function.
+ * Purity (`pure`), safety (`@safe`), no GC allocations(`@nogc`)
+ * and usage of `deprecated` and `@disabled`-ed symbols are checked.
+ *
+ * Params:
+ * exp = expression to check attributes for
+ * sc = scope of the function
+ * f = function to be checked
+ * Returns: `true` if error occur.
+ */
+private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration f)
+{
+ with(exp)
+ {
+ bool error = checkDisabled(sc, f);
+ error |= checkDeprecated(sc, f);
+ error |= checkPurity(sc, f);
+ error |= checkSafety(sc, f);
+ error |= checkNogc(sc, f);
+ return error;
+ }
+}
+
+/*******************************
+ * Helper function for `getRightThis()`.
+ * Gets `this` of the next outer aggregate.
+ * Params:
+ * loc = location to use for error messages
+ * sc = context
+ * s = the parent symbol of the existing `this`
+ * ad = struct or class we need the correct `this` for
+ * e1 = existing `this`
+ * t = type of the existing `this`
+ * var = the specific member of ad we're accessing
+ * flag = if true, return `null` instead of throwing an error
+ * Returns:
+ * Expression representing the `this` for the var
+ */
+Expression getThisSkipNestedFuncs(const ref Loc loc, Scope* sc, Dsymbol s, AggregateDeclaration ad, Expression e1, Type t, Dsymbol var, bool flag = false)
+{
+ int n = 0;
+ while (s && s.isFuncDeclaration())
+ {
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f.vthis)
+ {
+ n++;
+ e1 = new VarExp(loc, f.vthis);
+ if (f.isThis2)
+ {
+ // (*__this)[i]
+ if (n > 1)
+ e1 = e1.expressionSemantic(sc);
+ e1 = new PtrExp(loc, e1);
+ uint i = f.followInstantiationContext(ad);
+ e1 = new IndexExp(loc, e1, new IntegerExp(i));
+ s = f.toParentP(ad);
+ continue;
+ }
+ }
+ else
+ {
+ if (flag)
+ return null;
+ e1.error("need `this` of type `%s` to access member `%s` from static function `%s`", ad.toChars(), var.toChars(), f.toChars());
+ e1 = ErrorExp.get();
+ return e1;
+ }
+ s = s.toParent2();
+ }
+ if (n > 1 || e1.op == TOK.index)
+ e1 = e1.expressionSemantic(sc);
+ if (s && e1.type.equivalent(Type.tvoidptr))
+ {
+ if (auto sad = s.isAggregateDeclaration())
+ {
+ Type ta = sad.handleType();
+ if (ta.ty == Tstruct)
+ ta = ta.pointerTo();
+ e1.type = ta;
+ }
+ }
+ e1.type = e1.type.addMod(t.mod);
+ return e1;
+}
+
+/*******************************
+ * Make a dual-context container for use as a `this` argument.
+ * Params:
+ * loc = location to use for error messages
+ * sc = current scope
+ * fd = target function that will take the `this` argument
+ * Returns:
+ * Temporary closure variable.
+ * Note:
+ * The function `fd` is added to the nested references of the
+ * newly created variable such that a closure is made for the variable when
+ * the address of `fd` is taken.
+ */
+VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration fd)
+{
+ Type tthis2 = Type.tvoidptr.sarrayOf(2);
+ VarDeclaration vthis2 = new VarDeclaration(loc, tthis2, Identifier.generateId("__this"), null);
+ vthis2.storage_class |= STC.temp;
+ vthis2.dsymbolSemantic(sc);
+ vthis2.parent = sc.parent;
+ // make it a closure var
+ assert(sc.func);
+ sc.func.closureVars.push(vthis2);
+ // add `fd` to the nested refs
+ vthis2.nestedrefs.push(fd);
+ return vthis2;
+}
+
+/*******************************
+ * Make sure that the runtime hook `id` exists.
+ * Params:
+ * loc = location to use for error messages
+ * sc = current scope
+ * id = the hook identifier
+ * description = what the hook does
+ * module_ = what module the hook is located in
+ * Returns:
+ * a `bool` indicating if the hook is present.
+ */
+bool verifyHookExist(const ref Loc loc, ref Scope sc, Identifier id, string description, Identifier module_ = Id.object)
+{
+ auto rootSymbol = sc.search(loc, Id.empty, null);
+ if (auto moduleSymbol = rootSymbol.search(loc, module_))
+ if (moduleSymbol.search(loc, id))
+ return true;
+ error(loc, "`%s.%s` not found. The current runtime does not support %.*s, or the runtime is corrupt.", module_.toChars(), id.toChars(), cast(int)description.length, description.ptr);
+ return false;
+}
+
+/***************************************
+ * Fit elements[] to the corresponding types of the `sd`'s fields.
+ *
+ * Params:
+ * sd = the struct declaration
+ * loc = location to use for error messages
+ * sc = context
+ * elements = explicit arguments used to construct object
+ * stype = the constructed object type.
+ * Returns:
+ * false if any errors occur,
+ * otherwise true and elements[] are rewritten for the output.
+ */
+private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions* elements, Type stype)
+{
+ if (!elements)
+ return true;
+
+ const nfields = sd.nonHiddenFields();
+ size_t offset = 0;
+ for (size_t i = 0; i < elements.dim; i++)
+ {
+ Expression e = (*elements)[i];
+ if (!e)
+ continue;
+
+ e = resolveProperties(sc, e);
+ if (i >= nfields)
+ {
+ if (i <= sd.fields.dim && e.op == TOK.null_)
+ {
+ // CTFE sometimes creates null as hidden pointer; we'll allow this.
+ continue;
+ }
+ .error(loc, "more initializers than fields (%zu) of `%s`", nfields, sd.toChars());
+ return false;
+ }
+ VarDeclaration v = sd.fields[i];
+ if (v.offset < offset)
+ {
+ .error(loc, "overlapping initialization for `%s`", v.toChars());
+ if (!sd.isUnionDeclaration())
+ {
+ enum errorMsg = "`struct` initializers that contain anonymous unions" ~
+ " must initialize only the first member of a `union`. All subsequent" ~
+ " non-overlapping fields are default initialized";
+ .errorSupplemental(loc, errorMsg);
+ }
+ return false;
+ }
+ offset = cast(uint)(v.offset + v.type.size());
+
+ Type t = v.type;
+ if (stype)
+ t = t.addMod(stype.mod);
+ Type origType = t;
+ Type tb = t.toBasetype();
+
+ const hasPointers = tb.hasPointers();
+ if (hasPointers)
+ {
+ if ((stype.alignment() < target.ptrsize ||
+ (v.offset & (target.ptrsize - 1))) &&
+ (sc.func && sc.func.setUnsafe()))
+ {
+ .error(loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code",
+ sd.toChars(), v.toChars());
+ return false;
+ }
+ }
+
+ /* 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 (e.op == TOK.string_ && tb.ty == Tsarray)
+ {
+ StringExp se = cast(StringExp)e;
+ 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())
+ {
+ e = se.castTo(sc, t);
+ goto L1;
+ }
+ }
+
+ while (!e.implicitConvTo(t) && tb.ty == Tsarray)
+ {
+ /* Static array initialization, as in:
+ * T[3][5] = e;
+ */
+ t = tb.nextOf();
+ tb = t.toBasetype();
+ }
+ if (!e.implicitConvTo(t))
+ t = origType; // restore type for better diagnostic
+
+ e = e.implicitCastTo(sc, t);
+ L1:
+ if (e.op == TOK.error)
+ return false;
+
+ (*elements)[i] = doCopyOrMove(sc, e);
+ }
+ return true;
+}
+
+
+/**
+ * Returns `em` as a VariableExp
+ * Params:
+ * em = the EnumMember to wrap
+ * loc = location of use of em
+ * sc = scope of use of em
+ * Returns:
+ * VarExp referenceing `em` or ErrorExp if `em` if disabled/deprecated
+ */
+Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc)
+{
+ dsymbolSemantic(em, sc);
+ if (em.errors)
+ return ErrorExp.get();
+ em.checkDisabled(loc, sc);
+
+ if (em.depdecl && !em.depdecl._scope)
+ em.depdecl._scope = sc;
+ em.checkDeprecated(loc, sc);
+
+ if (em.errors)
+ return ErrorExp.get();
+ Expression e = new VarExp(loc, em);
+ return e.expressionSemantic(sc);
+}
+
+
+/*****************************
+ * Try to treat `exp` as a boolean,
+ * Params:
+ * exp = the expression
+ * sc = scope to evalute `exp` in
+ * Returns:
+ * Modified expression on success, ErrorExp on error
+ */
+Expression toBoolean(Expression exp, Scope* sc)
+{
+ switch(exp.op)
+ {
+ case TOK.delete_:
+ exp.error("`delete` does not give a boolean result");
+ return ErrorExp.get();
+
+ case TOK.comma:
+ auto ce = exp.isCommaExp();
+ auto ex2 = ce.e2.toBoolean(sc);
+ if (ex2.op == TOK.error)
+ return ex2;
+ ce.e2 = ex2;
+ ce.type = ce.e2.type;
+ return ce;
+
+ case TOK.assign:
+ case TOK.construct:
+ case TOK.blit:
+ // Things like:
+ // if (a = b) ...
+ // are usually mistakes.
+ exp.error("assignment cannot be used as a condition, perhaps `==` was meant?");
+ return ErrorExp.get();
+
+ //LogicalExp
+ case TOK.andAnd:
+ case TOK.orOr:
+ auto le = exp.isLogicalExp();
+ auto ex2 = le.e2.toBoolean(sc);
+ if (ex2.op == TOK.error)
+ return ex2;
+ le.e2 = ex2;
+ return le;
+
+ case TOK.question:
+ auto ce = exp.isCondExp();
+ auto ex1 = ce.e1.toBoolean(sc);
+ auto ex2 = ce.e2.toBoolean(sc);
+ if (ex1.op == TOK.error)
+ return ex1;
+ if (ex2.op == TOK.error)
+ return ex2;
+ ce.e1 = ex1;
+ ce.e2 = ex2;
+ return ce;
+
+
+ default:
+ // Default is 'yes' - do nothing
+ Expression e = exp;
+ Type t = exp.type;
+ Type tb = t.toBasetype();
+ Type att = null;
+
+ while (1)
+ {
+ // Structs can be converted to bool using opCast(bool)()
+ if (auto ts = tb.isTypeStruct())
+ {
+ AggregateDeclaration ad = ts.sym;
+ /* Don't really need to check for opCast first, but by doing so we
+ * get better error messages if it isn't there.
+ */
+ if (Dsymbol fd = search_function(ad, Id._cast))
+ {
+ e = new CastExp(exp.loc, e, Type.tbool);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ // Forward to aliasthis.
+ if (ad.aliasthis && !isRecursiveAliasThis(att, tb))
+ {
+ e = resolveAliasThis(sc, e);
+ t = e.type;
+ tb = e.type.toBasetype();
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (!t.isBoolean())
+ {
+ if (tb != Type.terror)
+ exp.error("expression `%s` of type `%s` does not have a boolean value",
+ exp.toChars(), t.toChars());
+ return ErrorExp.get();
+ }
+ return e;
+ }
+}