aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd/dcast.d
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/dcast.d')
-rw-r--r--gcc/d/dmd/dcast.d3741
1 files changed, 3741 insertions, 0 deletions
diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d
new file mode 100644
index 0000000..4c70565
--- /dev/null
+++ b/gcc/d/dmd/dcast.d
@@ -0,0 +1,3741 @@
+/**
+ * Semantic analysis for cast-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/dcast.d, _dcast.d)
+ * Documentation: https://dlang.org/phobos/dmd_dcast.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dcast.d
+ */
+
+module dmd.dcast;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arrayop;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.impcnvtab;
+import dmd.id;
+import dmd.init;
+import dmd.intrange;
+import dmd.mtype;
+import dmd.opover;
+import dmd.root.ctfloat;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.utf;
+import dmd.visitor;
+
+enum LOG = false;
+
+/**
+ * Attempt to implicitly cast the expression into type `t`.
+ *
+ * This routine will change `e`. To check the matching level,
+ * use `implicitConvTo`.
+ *
+ * Params:
+ * e = Expression that is to be casted
+ * sc = Current scope
+ * t = Expected resulting type
+ *
+ * Returns:
+ * The resulting casted expression (mutating `e`), or `ErrorExp`
+ * if such an implicit conversion is not possible.
+ */
+Expression implicitCastTo(Expression e, Scope* sc, Type t)
+{
+ extern (C++) final class ImplicitCastTo : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Type t;
+ Scope* sc;
+ Expression result;
+
+ extern (D) this(Scope* sc, Type t)
+ {
+ this.sc = sc;
+ this.t = t;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars());
+
+ if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t))
+ {
+ if (match == MATCH.constant && (e.type.constConv(t) || !e.isLvalue() && e.type.equivalent(t)))
+ {
+ /* Do not emit CastExp for const conversions and
+ * unique conversions on rvalue.
+ */
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+
+ auto ad = isAggregate(e.type);
+ if (ad && ad.aliasthis)
+ {
+ auto ts = ad.type.isTypeStruct();
+ const adMatch = ts
+ ? ts.implicitConvToWithoutAliasThis(t)
+ : ad.type.isTypeClass().implicitConvToWithoutAliasThis(t);
+
+ if (!adMatch)
+ {
+ Type tob = t.toBasetype();
+ Type t1b = e.type.toBasetype();
+ if (ad != isAggregate(tob))
+ {
+ if (t1b.ty == Tclass && tob.ty == Tclass)
+ {
+ ClassDeclaration t1cd = t1b.isClassHandle();
+ ClassDeclaration tocd = tob.isClassHandle();
+ int offset;
+ if (tocd.isBaseOf(t1cd, &offset))
+ {
+ result = new CastExp(e.loc, e, t);
+ result.type = t;
+ return;
+ }
+ }
+
+ /* Forward the cast to our alias this member, rewrite to:
+ * cast(to)e1.aliasthis
+ */
+ result = resolveAliasThis(sc, e);
+ result = result.castTo(sc, t);
+ return;
+ }
+ }
+ }
+
+ result = e.castTo(sc, t);
+ return;
+ }
+
+ result = e.optimize(WANTvalue);
+ if (result != e)
+ {
+ result.accept(this);
+ return;
+ }
+
+ if (t.ty != Terror && e.type.ty != Terror)
+ {
+ if (!t.deco)
+ {
+ e.error("forward reference to type `%s`", t.toChars());
+ }
+ else
+ {
+ //printf("type %p ty %d deco %p\n", type, type.ty, type.deco);
+ //type = type.typeSemantic(loc, sc);
+ //printf("type %s t %s\n", type.deco, t.deco);
+ auto ts = toAutoQualChars(e.type, t);
+ e.error("cannot implicitly convert expression `%s` of type `%s` to `%s`",
+ e.toChars(), ts[0], ts[1]);
+ }
+ }
+ result = ErrorExp.get();
+ }
+
+ override void visit(StringExp e)
+ {
+ //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars());
+ visit(cast(Expression)e);
+ if (auto se = result.isStringExp())
+ {
+ // Retain polysemous nature if it started out that way
+ se.committed = e.committed;
+ }
+ }
+
+ override void visit(ErrorExp e)
+ {
+ result = e;
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp::implicitCastTo type = %p %s, t = %s\n", e.type, e.type ? e.type.toChars() : NULL, t.toChars());
+ FuncExp fe;
+ if (e.matchType(t, sc, &fe) > MATCH.nomatch)
+ {
+ result = fe;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ visit(cast(Expression)e);
+
+ Type tb = result.type.toBasetype();
+ if (auto ta = tb.isTypeDArray())
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ semanticTypeInfo(sc, ta.next);
+ }
+
+ override void visit(SliceExp e)
+ {
+ visit(cast(Expression)e);
+
+ if (auto se = result.isSliceExp())
+ if (auto ale = se.e1.isArrayLiteralExp())
+ {
+ Type tb = t.toBasetype();
+ Type tx = (tb.ty == Tsarray)
+ ? tb.nextOf().sarrayOf(ale.elements ? ale.elements.dim : 0)
+ : tb.nextOf().arrayOf();
+ se.e1 = ale.implicitCastTo(sc, tx);
+ }
+ }
+ }
+
+ scope ImplicitCastTo v = new ImplicitCastTo(sc, t);
+ e.accept(v);
+ return v.result;
+}
+
+/**
+ * Checks whether or not an expression can be implicitly converted
+ * to type `t`.
+ *
+ * Unlike `implicitCastTo`, this routine does not perform the actual cast,
+ * but only checks up to what `MATCH` level the conversion would be possible.
+ *
+ * Params:
+ * e = Expression that is to be casted
+ * t = Expected resulting type
+ *
+ * Returns:
+ * The `MATCH` level between `e.type` and `t`.
+ */
+MATCH implicitConvTo(Expression e, Type t)
+{
+ extern (C++) final class ImplicitConvTo : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Type t;
+ MATCH result;
+
+ extern (D) this(Type t)
+ {
+ this.t = t;
+ result = MATCH.nomatch;
+ }
+
+ override void visit(Expression e)
+ {
+ version (none)
+ {
+ printf("Expression::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ //static int nest; if (++nest == 10) assert(0);
+ if (t == Type.terror)
+ return;
+ if (!e.type)
+ {
+ e.error("`%s` is not an expression", e.toChars());
+ e.type = Type.terror;
+ }
+
+ Expression ex = e.optimize(WANTvalue);
+ if (ex.type.equals(t))
+ {
+ result = MATCH.exact;
+ return;
+ }
+ if (ex != e)
+ {
+ //printf("\toptimized to %s of type %s\n", e.toChars(), e.type.toChars());
+ result = ex.implicitConvTo(t);
+ return;
+ }
+
+ MATCH match = e.type.implicitConvTo(t);
+ if (match != MATCH.nomatch)
+ {
+ result = match;
+ return;
+ }
+
+ /* See if we can do integral narrowing conversions
+ */
+ if (e.type.isintegral() && t.isintegral() && e.type.isTypeBasic() && t.isTypeBasic())
+ {
+ IntRange src = getIntRange(e);
+ IntRange target = IntRange.fromType(t);
+ if (target.contains(src))
+ {
+ result = MATCH.convert;
+ return;
+ }
+ }
+ }
+
+ /******
+ * Given expression e of type t, see if we can implicitly convert e
+ * to type tprime, where tprime is type t with mod bits added.
+ * Returns:
+ * match level
+ */
+ static MATCH implicitMod(Expression e, Type t, MOD mod)
+ {
+ Type tprime;
+ if (t.ty == Tpointer)
+ tprime = t.nextOf().castMod(mod).pointerTo();
+ else if (t.ty == Tarray)
+ tprime = t.nextOf().castMod(mod).arrayOf();
+ else if (t.ty == Tsarray)
+ tprime = t.nextOf().castMod(mod).sarrayOf(t.size() / t.nextOf().size());
+ else
+ tprime = t.castMod(mod);
+
+ return e.implicitConvTo(tprime);
+ }
+
+ static MATCH implicitConvToAddMin(BinExp e, Type t)
+ {
+ /* Is this (ptr +- offset)? If so, then ask ptr
+ * if the conversion can be done.
+ * This is to support doing things like implicitly converting a mutable unique
+ * pointer to an immutable pointer.
+ */
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (typeb.ty != Tpointer || tb.ty != Tpointer)
+ return MATCH.nomatch;
+
+ Type t1b = e.e1.type.toBasetype();
+ Type t2b = e.e2.type.toBasetype();
+ if (t1b.ty == Tpointer && t2b.isintegral() && t1b.equivalent(tb))
+ {
+ // ptr + offset
+ // ptr - offset
+ MATCH m = e.e1.implicitConvTo(t);
+ return (m > MATCH.constant) ? MATCH.constant : m;
+ }
+ if (t2b.ty == Tpointer && t1b.isintegral() && t2b.equivalent(tb))
+ {
+ // offset + ptr
+ MATCH m = e.e2.implicitConvTo(t);
+ return (m > MATCH.constant) ? MATCH.constant : m;
+ }
+
+ return MATCH.nomatch;
+ }
+
+ override void visit(AddExp e)
+ {
+ version (none)
+ {
+ printf("AddExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ visit(cast(Expression)e);
+ if (result == MATCH.nomatch)
+ result = implicitConvToAddMin(e, t);
+ }
+
+ override void visit(MinExp e)
+ {
+ version (none)
+ {
+ printf("MinExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ visit(cast(Expression)e);
+ if (result == MATCH.nomatch)
+ result = implicitConvToAddMin(e, t);
+ }
+
+ override void visit(IntegerExp e)
+ {
+ version (none)
+ {
+ printf("IntegerExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ MATCH m = e.type.implicitConvTo(t);
+ if (m >= MATCH.constant)
+ {
+ result = m;
+ return;
+ }
+
+ TY ty = e.type.toBasetype().ty;
+ TY toty = t.toBasetype().ty;
+ TY oldty = ty;
+
+ if (m == MATCH.nomatch && t.ty == Tenum)
+ return;
+
+ if (auto tv = t.isTypeVector())
+ {
+ TypeBasic tb = tv.elementType();
+ if (tb.ty == Tvoid)
+ return;
+ toty = tb.ty;
+ }
+
+ switch (ty)
+ {
+ case Tbool:
+ case Tint8:
+ case Tchar:
+ case Tuns8:
+ case Tint16:
+ case Tuns16:
+ case Twchar:
+ ty = Tint32;
+ break;
+
+ case Tdchar:
+ ty = Tuns32;
+ break;
+
+ default:
+ break;
+ }
+
+ // Only allow conversion if no change in value
+ immutable dinteger_t value = e.toInteger();
+
+ bool isLosslesslyConvertibleToFP(T)()
+ {
+ if (e.type.isunsigned())
+ {
+ const f = cast(T) value;
+ return cast(dinteger_t) f == value;
+ }
+
+ const f = cast(T) cast(sinteger_t) value;
+ return cast(sinteger_t) f == cast(sinteger_t) value;
+ }
+
+ switch (toty)
+ {
+ case Tbool:
+ if ((value & 1) != value)
+ return;
+ break;
+
+ case Tint8:
+ if (ty == Tuns64 && value & ~0x7FU)
+ return;
+ else if (cast(byte)value != value)
+ return;
+ break;
+
+ case Tchar:
+ if ((oldty == Twchar || oldty == Tdchar) && value > 0x7F)
+ return;
+ goto case Tuns8;
+ case Tuns8:
+ //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value);
+ if (cast(ubyte)value != value)
+ return;
+ break;
+
+ case Tint16:
+ if (ty == Tuns64 && value & ~0x7FFFU)
+ return;
+ else if (cast(short)value != value)
+ return;
+ break;
+
+ case Twchar:
+ if (oldty == Tdchar && value > 0xD7FF && value < 0xE000)
+ return;
+ goto case Tuns16;
+ case Tuns16:
+ if (cast(ushort)value != value)
+ return;
+ break;
+
+ case Tint32:
+ if (ty == Tuns32)
+ {
+ }
+ else if (ty == Tuns64 && value & ~0x7FFFFFFFU)
+ return;
+ else if (cast(int)value != value)
+ return;
+ break;
+
+ case Tuns32:
+ if (ty == Tint32)
+ {
+ }
+ else if (cast(uint)value != value)
+ return;
+ break;
+
+ case Tdchar:
+ if (value > 0x10FFFFU)
+ return;
+ break;
+
+ case Tfloat32:
+ if (!isLosslesslyConvertibleToFP!float)
+ return;
+ break;
+
+ case Tfloat64:
+ if (!isLosslesslyConvertibleToFP!double)
+ return;
+ break;
+
+ case Tfloat80:
+ if (!isLosslesslyConvertibleToFP!real_t)
+ return;
+ break;
+
+ case Tpointer:
+ //printf("type = %s\n", type.toBasetype()->toChars());
+ //printf("t = %s\n", t.toBasetype()->toChars());
+ if (ty == Tpointer && e.type.toBasetype().nextOf().ty == t.toBasetype().nextOf().ty)
+ {
+ /* Allow things like:
+ * const char* P = cast(char *)3;
+ * char* q = P;
+ */
+ break;
+ }
+ goto default;
+
+ default:
+ visit(cast(Expression)e);
+ return;
+ }
+
+ //printf("MATCH.convert\n");
+ result = MATCH.convert;
+ }
+
+ override void visit(ErrorExp e)
+ {
+ // no match
+ }
+
+ override void visit(NullExp e)
+ {
+ version (none)
+ {
+ printf("NullExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ if (e.type.equals(t))
+ {
+ result = MATCH.exact;
+ return;
+ }
+
+ /* Allow implicit conversions from immutable to mutable|const,
+ * and mutable to immutable. It works because, after all, a null
+ * doesn't actually point to anything.
+ */
+ if (t.equivalent(e.type))
+ {
+ result = MATCH.constant;
+ return;
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ version (none)
+ {
+ printf("StructLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+ if (e.type.ty == t.ty && e.type.isTypeStruct() && e.type.isTypeStruct().sym == t.isTypeStruct().sym)
+ {
+ result = MATCH.constant;
+ foreach (i, el; (*e.elements)[])
+ {
+ if (!el)
+ continue;
+ Type te = e.sd.fields[i].type.addMod(t.mod);
+ MATCH m2 = el.implicitConvTo(te);
+ //printf("\t%s => %s, match = %d\n", el.toChars(), te.toChars(), m2);
+ if (m2 < result)
+ result = m2;
+ }
+ }
+ }
+
+ override void visit(StringExp e)
+ {
+ version (none)
+ {
+ printf("StringExp::implicitConvTo(this=%s, committed=%d, type=%s, t=%s)\n", e.toChars(), e.committed, e.type.toChars(), t.toChars());
+ }
+ if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid)
+ return;
+
+ if (!(e.type.ty == Tsarray || e.type.ty == Tarray || e.type.ty == Tpointer))
+ return visit(cast(Expression)e);
+
+ TY tyn = e.type.nextOf().ty;
+
+ if (!tyn.isSomeChar)
+ return visit(cast(Expression)e);
+
+ switch (t.ty)
+ {
+ case Tsarray:
+ if (e.type.ty == Tsarray)
+ {
+ TY tynto = t.nextOf().ty;
+ if (tynto == tyn)
+ {
+ if (e.type.isTypeSArray().dim.toInteger() == t.isTypeSArray().dim.toInteger())
+ {
+ result = MATCH.exact;
+ }
+ return;
+ }
+ if (tynto.isSomeChar)
+ {
+ if (e.committed && tynto != tyn)
+ return;
+ size_t fromlen = e.numberOfCodeUnits(tynto);
+ size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger();
+ if (tolen < fromlen)
+ return;
+ if (tolen != fromlen)
+ {
+ // implicit length extending
+ result = MATCH.convert;
+ return;
+ }
+ }
+ if (!e.committed && tynto.isSomeChar)
+ {
+ result = MATCH.exact;
+ return;
+ }
+ }
+ else if (e.type.ty == Tarray)
+ {
+ TY tynto = t.nextOf().ty;
+ if (tynto.isSomeChar)
+ {
+ if (e.committed && tynto != tyn)
+ return;
+ size_t fromlen = e.numberOfCodeUnits(tynto);
+ size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger();
+ if (tolen < fromlen)
+ return;
+ if (tolen != fromlen)
+ {
+ // implicit length extending
+ result = MATCH.convert;
+ return;
+ }
+ }
+ if (tynto == tyn)
+ {
+ result = MATCH.exact;
+ return;
+ }
+ if (!e.committed && tynto.isSomeChar)
+ {
+ result = MATCH.exact;
+ return;
+ }
+ }
+ goto case; /+ fall through +/
+ case Tarray:
+ case Tpointer:
+ Type tn = t.nextOf();
+ MATCH m = MATCH.exact;
+ if (e.type.nextOf().mod != tn.mod)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=16183
+ if (!tn.isConst() && !tn.isImmutable())
+ return;
+ m = MATCH.constant;
+ }
+ if (!e.committed)
+ {
+ switch (tn.ty)
+ {
+ case Tchar:
+ if (e.postfix == 'w' || e.postfix == 'd')
+ m = MATCH.convert;
+ result = m;
+ return;
+ case Twchar:
+ if (e.postfix != 'w')
+ m = MATCH.convert;
+ result = m;
+ return;
+ case Tdchar:
+ if (e.postfix != 'd')
+ m = MATCH.convert;
+ result = m;
+ return;
+ case Tenum:
+ if (tn.isTypeEnum().sym.isSpecial())
+ {
+ /* Allow string literal -> const(wchar_t)[]
+ */
+ if (TypeBasic tob = tn.toBasetype().isTypeBasic())
+ result = tn.implicitConvTo(tob);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ version (none)
+ {
+ printf("ArrayLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if ((tb.ty == Tarray || tb.ty == Tsarray) &&
+ (typeb.ty == Tarray || typeb.ty == Tsarray))
+ {
+ result = MATCH.exact;
+ Type typen = typeb.nextOf().toBasetype();
+
+ if (auto tsa = tb.isTypeSArray())
+ {
+ if (e.elements.dim != tsa.dim.toInteger())
+ result = MATCH.nomatch;
+ }
+
+ Type telement = tb.nextOf();
+ if (!e.elements.dim)
+ {
+ if (typen.ty != Tvoid)
+ result = typen.implicitConvTo(telement);
+ }
+ else
+ {
+ if (e.basis)
+ {
+ MATCH m = e.basis.implicitConvTo(telement);
+ if (m < result)
+ result = m;
+ }
+ for (size_t i = 0; i < e.elements.dim; i++)
+ {
+ Expression el = (*e.elements)[i];
+ if (result == MATCH.nomatch)
+ break;
+ if (!el)
+ continue;
+ MATCH m = el.implicitConvTo(telement);
+ if (m < result)
+ result = m; // remember worst match
+ }
+ }
+
+ if (!result)
+ result = e.type.implicitConvTo(t);
+
+ return;
+ }
+ else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray))
+ {
+ result = MATCH.exact;
+ // Convert array literal to vector type
+ TypeVector tv = tb.isTypeVector();
+ TypeSArray tbase = tv.basetype.isTypeSArray();
+ assert(tbase);
+ const edim = e.elements.dim;
+ const tbasedim = tbase.dim.toInteger();
+ if (edim > tbasedim)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ Type telement = tv.elementType();
+ if (edim < tbasedim)
+ {
+ Expression el = typeb.nextOf.defaultInitLiteral(e.loc);
+ MATCH m = el.implicitConvTo(telement);
+ if (m < result)
+ result = m; // remember worst match
+ }
+ foreach (el; (*e.elements)[])
+ {
+ MATCH m = el.implicitConvTo(telement);
+ if (m < result)
+ result = m; // remember worst match
+ if (result == MATCH.nomatch)
+ break; // no need to check for worse
+ }
+ return;
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ auto taa = t.toBasetype().isTypeAArray();
+ Type typeb = e.type.toBasetype();
+
+ if (!(taa && typeb.ty == Taarray))
+ return visit(cast(Expression)e);
+
+ result = MATCH.exact;
+ foreach (i, el; (*e.keys)[])
+ {
+ MATCH m = el.implicitConvTo(taa.index);
+ if (m < result)
+ result = m; // remember worst match
+ if (result == MATCH.nomatch)
+ break; // no need to check for worse
+ el = (*e.values)[i];
+ m = el.implicitConvTo(taa.nextOf());
+ if (m < result)
+ result = m; // remember worst match
+ if (result == MATCH.nomatch)
+ break; // no need to check for worse
+ }
+ }
+
+ override void visit(CallExp e)
+ {
+ enum LOG = false;
+ static if (LOG)
+ {
+ printf("CallExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ /* Allow the result of strongly pure functions to
+ * convert to immutable
+ */
+ if (e.f &&
+ (global.params.useDIP1000 != FeatureState.enabled || // lots of legacy code breaks with the following purity check
+ e.f.isPure() >= PURE.strong ||
+ // Special case exemption for Object.dup() which we assume is implemented correctly
+ e.f.ident == Id.dup &&
+ e.f.toParent2() == ClassDeclaration.object.toParent()) &&
+ e.f.isReturnIsolated() // check isReturnIsolated last, because it is potentially expensive.
+ )
+ {
+ result = e.type.immutableOf().implicitConvTo(t);
+ if (result > MATCH.constant) // Match level is MATCH.constant at best.
+ result = MATCH.constant;
+ return;
+ }
+
+ /* Conversion is 'const' conversion if:
+ * 1. function is pure (weakly pure is ok)
+ * 2. implicit conversion only fails because of mod bits
+ * 3. each function parameter can be implicitly converted to the mod bits
+ */
+ auto tf = (e.f ? e.f.type : e.e1.type).toBasetype().isTypeFunction();
+ if (!tf)
+ return;
+
+ if (tf.purity == PURE.impure)
+ return;
+ if (e.f && e.f.isNested())
+ return;
+
+ /* See if fail only because of mod bits.
+ *
+ * https://issues.dlang.org/show_bug.cgi?id=14155
+ * All pure functions can access global immutable data.
+ * So the returned pointer may refer an immutable global data,
+ * and then the returned pointer that points non-mutable object
+ * cannot be unique pointer.
+ *
+ * Example:
+ * immutable g;
+ * static this() { g = 1; }
+ * const(int*) foo() pure { return &g; }
+ * void test() {
+ * immutable(int*) ip = foo(); // OK
+ * int* mp = foo(); // should be disallowed
+ * }
+ */
+ if (e.type.immutableOf().implicitConvTo(t) < MATCH.constant && e.type.addMod(MODFlags.shared_).implicitConvTo(t) < MATCH.constant && e.type.implicitConvTo(t.addMod(MODFlags.shared_)) < MATCH.constant)
+ {
+ return;
+ }
+ // Allow a conversion to immutable type, or
+ // conversions of mutable types between thread-local and shared.
+
+ /* Get mod bits of what we're converting to
+ */
+ Type tb = t.toBasetype();
+ MOD mod = tb.mod;
+ if (tf.isref)
+ {
+ }
+ else
+ {
+ if (Type ti = getIndirection(t))
+ mod = ti.mod;
+ }
+ static if (LOG)
+ {
+ printf("mod = x%x\n", mod);
+ }
+ if (mod & MODFlags.wild)
+ return; // not sure what to do with this
+
+ /* Apply mod bits to each function parameter,
+ * and see if we can convert the function argument to the modded type
+ */
+
+ size_t nparams = tf.parameterList.length;
+ size_t j = tf.isDstyleVariadic(); // if TypeInfoArray was prepended
+ if (auto dve = e.e1.isDotVarExp())
+ {
+ /* Treat 'this' as just another function argument
+ */
+ Type targ = dve.e1.type;
+ if (targ.constConv(targ.castMod(mod)) == MATCH.nomatch)
+ return;
+ }
+ foreach (const i; j .. e.arguments.dim)
+ {
+ Expression earg = (*e.arguments)[i];
+ Type targ = earg.type.toBasetype();
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targ: %s\n", cast(int)i, earg.toChars(), targ.toChars());
+ }
+ if (i - j < nparams)
+ {
+ Parameter fparam = tf.parameterList[i - j];
+ if (fparam.storageClass & STC.lazy_)
+ return; // not sure what to do with this
+ Type tparam = fparam.type;
+ if (!tparam)
+ continue;
+ if (fparam.isReference())
+ {
+ if (targ.constConv(tparam.castMod(mod)) == MATCH.nomatch)
+ return;
+ continue;
+ }
+ }
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targm: %s\n", cast(int)i, earg.toChars(), targ.addMod(mod).toChars());
+ }
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return;
+ }
+
+ /* Success
+ */
+ result = MATCH.constant;
+ }
+
+ override void visit(AddrExp e)
+ {
+ version (none)
+ {
+ printf("AddrExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e.type.implicitConvTo(t);
+ //printf("\tresult = %d\n", result);
+
+ if (result != MATCH.nomatch)
+ return;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (e.e1.op == TOK.overloadSet &&
+ (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
+ {
+ OverExp eo = e.e1.isOverExp();
+ FuncDeclaration f = null;
+ foreach (s; eo.vars.a[])
+ {
+ FuncDeclaration f2 = s.isFuncDeclaration();
+ assert(f2);
+ if (f2.overloadExactMatch(tb.nextOf()))
+ {
+ if (f)
+ {
+ /* Error if match in more than one overload set,
+ * even if one is a 'better' match than the other.
+ */
+ ScopeDsymbol.multiplyDefined(e.loc, f, f2);
+ }
+ else
+ f = f2;
+ result = MATCH.exact;
+ }
+ }
+ }
+
+ if (e.e1.op == TOK.variable &&
+ typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
+ tb.ty == Tpointer && tb.nextOf().ty == Tfunction)
+ {
+ /* I don't think this can ever happen -
+ * it should have been
+ * converted to a SymOffExp.
+ */
+ assert(0);
+ }
+
+ //printf("\tresult = %d\n", result);
+ }
+
+ override void visit(SymOffExp e)
+ {
+ version (none)
+ {
+ printf("SymOffExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e.type.implicitConvTo(t);
+ //printf("\tresult = %d\n", result);
+ if (result != MATCH.nomatch)
+ return;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
+ (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
+ {
+ if (FuncDeclaration f = e.var.isFuncDeclaration())
+ {
+ f = f.overloadExactMatch(tb.nextOf());
+ if (f)
+ {
+ if ((tb.ty == Tdelegate && (f.needThis() || f.isNested())) ||
+ (tb.ty == Tpointer && !(f.needThis() || f.isNested())))
+ {
+ result = MATCH.exact;
+ }
+ }
+ }
+ }
+ //printf("\tresult = %d\n", result);
+ }
+
+ override void visit(DelegateExp e)
+ {
+ version (none)
+ {
+ printf("DelegateExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e.type.implicitConvTo(t);
+ if (result != MATCH.nomatch)
+ return;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (typeb.ty == Tdelegate && tb.ty == Tdelegate)
+ {
+ if (e.func && e.func.overloadExactMatch(tb.nextOf()))
+ result = MATCH.exact;
+ }
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp::implicitConvTo type = %p %s, t = %s\n", e.type, e.type ? e.type.toChars() : NULL, t.toChars());
+ MATCH m = e.matchType(t, null, null, 1);
+ if (m > MATCH.nomatch)
+ {
+ result = m;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(AndExp e)
+ {
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ MATCH m1 = e.e1.implicitConvTo(t);
+ MATCH m2 = e.e2.implicitConvTo(t);
+
+ // Pick the worst match
+ result = (m1 < m2) ? m1 : m2;
+ }
+
+ override void visit(OrExp e)
+ {
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ MATCH m1 = e.e1.implicitConvTo(t);
+ MATCH m2 = e.e2.implicitConvTo(t);
+
+ // Pick the worst match
+ result = (m1 < m2) ? m1 : m2;
+ }
+
+ override void visit(XorExp e)
+ {
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ MATCH m1 = e.e1.implicitConvTo(t);
+ MATCH m2 = e.e2.implicitConvTo(t);
+
+ // Pick the worst match
+ result = (m1 < m2) ? m1 : m2;
+ }
+
+ override void visit(CondExp e)
+ {
+ MATCH m1 = e.e1.implicitConvTo(t);
+ MATCH m2 = e.e2.implicitConvTo(t);
+ //printf("CondExp: m1 %d m2 %d\n", m1, m2);
+
+ // Pick the worst match
+ result = (m1 < m2) ? m1 : m2;
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+
+ override void visit(CastExp e)
+ {
+ version (none)
+ {
+ printf("CastExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e.type.implicitConvTo(t);
+ if (result != MATCH.nomatch)
+ return;
+
+ if (t.isintegral() && e.e1.type.isintegral() && e.e1.implicitConvTo(t) != MATCH.nomatch)
+ result = MATCH.convert;
+ else
+ visit(cast(Expression)e);
+ }
+
+ override void visit(NewExp e)
+ {
+ version (none)
+ {
+ printf("NewExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ /* Calling new() is like calling a pure function. We can implicitly convert the
+ * return from new() to t using the same algorithm as in CallExp, with the function
+ * 'arguments' being:
+ * thisexp
+ * newargs
+ * arguments
+ * .init
+ * 'member' need to be pure.
+ */
+
+ /* See if fail only because of mod bits
+ */
+ if (e.type.immutableOf().implicitConvTo(t.immutableOf()) == MATCH.nomatch)
+ return;
+
+ /* Get mod bits of what we're converting to
+ */
+ Type tb = t.toBasetype();
+ MOD mod = tb.mod;
+ if (Type ti = getIndirection(t))
+ mod = ti.mod;
+ static if (LOG)
+ {
+ printf("mod = x%x\n", mod);
+ }
+ if (mod & MODFlags.wild)
+ return; // not sure what to do with this
+
+ /* Apply mod bits to each argument,
+ * and see if we can convert the argument to the modded type
+ */
+
+ if (e.thisexp)
+ {
+ /* Treat 'this' as just another function argument
+ */
+ Type targ = e.thisexp.type;
+ if (targ.constConv(targ.castMod(mod)) == MATCH.nomatch)
+ return;
+ }
+
+ /* Check call to 'member'
+ */
+ if (e.member)
+ {
+ FuncDeclaration fd = e.member;
+ if (fd.errors || fd.type.ty != Tfunction)
+ return; // error
+ TypeFunction tf = fd.type.isTypeFunction();
+ if (tf.purity == PURE.impure)
+ return; // impure
+
+ if (e.type.immutableOf().implicitConvTo(t) < MATCH.constant && e.type.addMod(MODFlags.shared_).implicitConvTo(t) < MATCH.constant && e.type.implicitConvTo(t.addMod(MODFlags.shared_)) < MATCH.constant)
+ {
+ return;
+ }
+ // Allow a conversion to immutable type, or
+ // conversions of mutable types between thread-local and shared.
+
+ Expressions* args = e.arguments;
+
+ size_t nparams = tf.parameterList.length;
+ // if TypeInfoArray was prepended
+ size_t j = tf.isDstyleVariadic();
+ for (size_t i = j; i < e.arguments.dim; ++i)
+ {
+ Expression earg = (*args)[i];
+ Type targ = earg.type.toBasetype();
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targ: %s\n", cast(int)i, earg.toChars(), targ.toChars());
+ }
+ if (i - j < nparams)
+ {
+ Parameter fparam = tf.parameterList[i - j];
+ if (fparam.storageClass & STC.lazy_)
+ return; // not sure what to do with this
+ Type tparam = fparam.type;
+ if (!tparam)
+ continue;
+ if (fparam.isReference())
+ {
+ if (targ.constConv(tparam.castMod(mod)) == MATCH.nomatch)
+ return;
+ continue;
+ }
+ }
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targm: %s\n", cast(int)i, earg.toChars(), targ.addMod(mod).toChars());
+ }
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return;
+ }
+ }
+
+ /* If no 'member', then construction is by simple assignment,
+ * and just straight check 'arguments'
+ */
+ if (!e.member && e.arguments)
+ {
+ for (size_t i = 0; i < e.arguments.dim; ++i)
+ {
+ Expression earg = (*e.arguments)[i];
+ if (!earg) // https://issues.dlang.org/show_bug.cgi?id=14853
+ // if it's on overlapped field
+ continue;
+ Type targ = earg.type.toBasetype();
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targ: %s\n", cast(int)i, earg.toChars(), targ.toChars());
+ printf("[%d] earg: %s, targm: %s\n", cast(int)i, earg.toChars(), targ.addMod(mod).toChars());
+ }
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return;
+ }
+ }
+
+ /* Consider the .init expression as an argument
+ */
+ Type ntb = e.newtype.toBasetype();
+ if (ntb.ty == Tarray)
+ ntb = ntb.nextOf().toBasetype();
+ if (auto ts = ntb.isTypeStruct())
+ {
+ // Don't allow nested structs - uplevel reference may not be convertible
+ StructDeclaration sd = ts.sym;
+ sd.size(e.loc); // resolve any forward references
+ if (sd.isNested())
+ return;
+ }
+ if (ntb.isZeroInit(e.loc))
+ {
+ /* Zeros are implicitly convertible, except for special cases.
+ */
+ if (auto tc = ntb.isTypeClass())
+ {
+ /* With new() must look at the class instance initializer.
+ */
+ ClassDeclaration cd = tc.sym;
+
+ cd.size(e.loc); // resolve any forward references
+
+ if (cd.isNested())
+ return; // uplevel reference may not be convertible
+
+ assert(!cd.isInterfaceDeclaration());
+
+ struct ClassCheck
+ {
+ extern (C++) static bool convertible(Expression e, ClassDeclaration cd, MOD mod)
+ {
+ for (size_t i = 0; i < cd.fields.dim; i++)
+ {
+ VarDeclaration v = cd.fields[i];
+ Initializer _init = v._init;
+ if (_init)
+ {
+ if (_init.isVoidInitializer())
+ {
+ }
+ else if (ExpInitializer ei = _init.isExpInitializer())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=21319
+ // This is to prevent re-analyzing the same expression
+ // over and over again.
+ if (ei.exp == e)
+ return false;
+ Type tb = v.type.toBasetype();
+ if (implicitMod(ei.exp, tb, mod) == MATCH.nomatch)
+ return false;
+ }
+ else
+ {
+ /* Enhancement: handle StructInitializer and ArrayInitializer
+ */
+ return false;
+ }
+ }
+ else if (!v.type.isZeroInit(e.loc))
+ return false;
+ }
+ return cd.baseClass ? convertible(e, cd.baseClass, mod) : true;
+ }
+ }
+
+ if (!ClassCheck.convertible(e, cd, mod))
+ return;
+ }
+ }
+ else
+ {
+ Expression earg = e.newtype.defaultInitLiteral(e.loc);
+ Type targ = e.newtype.toBasetype();
+
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return;
+ }
+
+ /* Success
+ */
+ result = MATCH.constant;
+ }
+
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp::implicitConvTo e = %s, type = %s\n", e.toChars(), e.type.toChars());
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.ty == Tsarray && typeb.ty == Tarray)
+ {
+ typeb = toStaticArrayType(e);
+ if (typeb)
+ {
+ // Try: T[] -> T[dim]
+ // (Slice with compile-time known boundaries to static array)
+ result = typeb.implicitConvTo(t);
+ if (result > MATCH.convert)
+ result = MATCH.convert; // match with implicit conversion at most
+ }
+ return;
+ }
+
+ /* If the only reason it won't convert is because of the mod bits,
+ * then test for conversion by seeing if e1 can be converted with those
+ * same mod bits.
+ */
+ Type t1b = e.e1.type.toBasetype();
+ if (tb.ty == Tarray && typeb.equivalent(tb))
+ {
+ Type tbn = tb.nextOf();
+ Type tx = null;
+
+ /* If e.e1 is dynamic array or pointer, the uniqueness of e.e1
+ * is equivalent with the uniqueness of the referred data. And in here
+ * we can have arbitrary typed reference for that.
+ */
+ if (t1b.ty == Tarray)
+ tx = tbn.arrayOf();
+ if (t1b.ty == Tpointer)
+ tx = tbn.pointerTo();
+
+ /* If e.e1 is static array, at least it should be an rvalue.
+ * If not, e.e1 is a reference, and its uniqueness does not link
+ * to the uniqueness of the referred data.
+ */
+ if (t1b.ty == Tsarray && !e.e1.isLvalue())
+ tx = tbn.sarrayOf(t1b.size() / tbn.size());
+
+ if (tx)
+ {
+ result = e.e1.implicitConvTo(tx);
+ if (result > MATCH.constant) // Match level is MATCH.constant at best.
+ result = MATCH.constant;
+ }
+ }
+
+ // Enhancement 10724
+ if (tb.ty == Tpointer && e.e1.op == TOK.string_)
+ e.e1.accept(this);
+ }
+ }
+
+ scope ImplicitConvTo v = new ImplicitConvTo(t);
+ e.accept(v);
+ return v.result;
+}
+
+/**
+ * Same as implicitConvTo(); except follow C11 rules, which are quite a bit
+ * more permissive than D.
+ * C11 6.3 and 6.5.16.1
+ * Params:
+ * e = Expression that is to be casted
+ * t = Expected resulting type
+ * Returns:
+ * The `MATCH` level between `e.type` and `t`.
+ */
+MATCH cimplicitConvTo(Expression e, Type t)
+{
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.equals(typeb))
+ return MATCH.exact;
+ if ((typeb.isintegral() || typeb.isfloating()) &&
+ (tb.isintegral() || tb.isfloating()))
+ return MATCH.convert;
+ if (tb.ty == Tpointer && typeb.isintegral()) // C11 6.3.2.3-5
+ return MATCH.convert;
+ if (tb.isintegral() && typeb.ty == Tpointer) // C11 6.3.2.3-6
+ return MATCH.convert;
+ if (tb.ty == Tpointer && typeb.ty == Tpointer)
+ {
+ if (tb.isTypePointer().next.ty == Tvoid ||
+ typeb.isTypePointer().next.ty == Tvoid)
+ return MATCH.convert; // convert to/from void* C11 6.3.2.3-1
+ }
+
+ return implicitConvTo(e, t);
+}
+
+/*****************************************
+ */
+Type toStaticArrayType(SliceExp e)
+{
+ if (e.lwr && e.upr)
+ {
+ // For the following code to work, e should be optimized beforehand.
+ // (eg. $ in lwr and upr should be already resolved, if possible)
+ Expression lwr = e.lwr.optimize(WANTvalue);
+ Expression upr = e.upr.optimize(WANTvalue);
+ if (lwr.isConst() && upr.isConst())
+ {
+ size_t len = cast(size_t)(upr.toUInteger() - lwr.toUInteger());
+ return e.type.toBasetype().nextOf().sarrayOf(len);
+ }
+ }
+ else
+ {
+ Type t1b = e.e1.type.toBasetype();
+ if (t1b.ty == Tsarray)
+ return t1b;
+ }
+ return null;
+}
+
+/**************************************
+ * Do an explicit cast.
+ * Assume that the expression `e` does not have any indirections.
+ * (Parameter 'att' is used to stop 'alias this' recursion)
+ */
+Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
+{
+ extern (C++) final class CastTo : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Type t;
+ Scope* sc;
+ Expression result;
+
+ extern (D) this(Scope* sc, Type t)
+ {
+ this.sc = sc;
+ this.t = t;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression::castTo(this=%s, t=%s)\n", e.toChars(), t.toChars());
+ version (none)
+ {
+ printf("Expression::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ if (e.type.equals(t))
+ {
+ result = e;
+ return;
+ }
+ if (e.op == TOK.variable)
+ {
+ VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
+ if (v && v.storage_class & STC.manifest)
+ {
+ result = e.ctfeInterpret();
+ /* https://issues.dlang.org/show_bug.cgi?id=18236
+ *
+ * The expression returned by ctfeInterpret points
+ * to the line where the manifest constant was declared
+ * so we need to update the location before trying to cast
+ */
+ result.loc = e.loc;
+ result = result.castTo(sc, t);
+ return;
+ }
+ }
+
+ Type tob = t.toBasetype();
+ Type t1b = e.type.toBasetype();
+ if (tob.equals(t1b))
+ {
+ result = e.copy(); // because of COW for assignment to e.type
+ result.type = t;
+ return;
+ }
+
+ /* Make semantic error against invalid cast between concrete types.
+ * Assume that 'e' is never be any placeholder expressions.
+ * The result of these checks should be consistent with CastExp::toElem().
+ */
+
+ // Fat Value types
+ const(bool) tob_isFV = (tob.ty == Tstruct || tob.ty == Tsarray || tob.ty == Tvector);
+ const(bool) t1b_isFV = (t1b.ty == Tstruct || t1b.ty == Tsarray || t1b.ty == Tvector);
+
+ // Fat Reference types
+ const(bool) tob_isFR = (tob.ty == Tarray || tob.ty == Tdelegate);
+ const(bool) t1b_isFR = (t1b.ty == Tarray || t1b.ty == Tdelegate);
+
+ // Reference types
+ const(bool) tob_isR = (tob_isFR || tob.ty == Tpointer || tob.ty == Taarray || tob.ty == Tclass);
+ const(bool) t1b_isR = (t1b_isFR || t1b.ty == Tpointer || t1b.ty == Taarray || t1b.ty == Tclass);
+
+ // Arithmetic types (== valueable basic types)
+ const(bool) tob_isA = ((tob.isintegral() || tob.isfloating()) && tob.ty != Tvector);
+ const(bool) t1b_isA = ((t1b.isintegral() || t1b.isfloating()) && t1b.ty != Tvector);
+
+ // Try casting the alias this member.
+ // Return the expression if it succeeds, null otherwise.
+ Expression tryAliasThisCast()
+ {
+ if (isRecursiveAliasThis(att, t1b))
+ return null;
+
+ /* Forward the cast to our alias this member, rewrite to:
+ * cast(to)e1.aliasthis
+ */
+ auto exp = resolveAliasThis(sc, e);
+ const errors = global.startGagging();
+ exp = castTo(exp, sc, t, att);
+ return global.endGagging(errors) ? null : exp;
+ }
+
+ bool hasAliasThis;
+ if (AggregateDeclaration t1ad = isAggregate(t1b))
+ {
+ AggregateDeclaration toad = isAggregate(tob);
+ if (t1ad != toad && t1ad.aliasthis)
+ {
+ if (t1b.ty == Tclass && tob.ty == Tclass)
+ {
+ ClassDeclaration t1cd = t1b.isClassHandle();
+ ClassDeclaration tocd = tob.isClassHandle();
+ int offset;
+ if (tocd.isBaseOf(t1cd, &offset))
+ goto Lok;
+ }
+ hasAliasThis = true;
+ }
+ }
+ else if (tob.ty == Tvector && t1b.ty != Tvector)
+ {
+ //printf("test1 e = %s, e.type = %s, tob = %s\n", e.toChars(), e.type.toChars(), tob.toChars());
+ TypeVector tv = tob.isTypeVector();
+ result = new CastExp(e.loc, e, tv.elementType());
+ result = new VectorExp(e.loc, result, tob);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ else if (tob.ty != Tvector && t1b.ty == Tvector)
+ {
+ // T[n] <-- __vector(U[m])
+ if (tob.ty == Tsarray)
+ {
+ if (t1b.size(e.loc) == tob.size(e.loc))
+ goto Lok;
+ }
+ goto Lfail;
+ }
+ else if (t1b.implicitConvTo(tob) == MATCH.constant && t.equals(e.type.constOf()))
+ {
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+
+ // arithmetic values vs. other arithmetic values
+ // arithmetic values vs. T*
+ if (tob_isA && (t1b_isA || t1b.ty == Tpointer) || t1b_isA && (tob_isA || tob.ty == Tpointer))
+ {
+ goto Lok;
+ }
+
+ // arithmetic values vs. references or fat values
+ if (tob_isA && (t1b_isR || t1b_isFV) || t1b_isA && (tob_isR || tob_isFV))
+ {
+ goto Lfail;
+ }
+
+ // Bugzlla 3133: A cast between fat values is possible only when the sizes match.
+ if (tob_isFV && t1b_isFV)
+ {
+ if (hasAliasThis)
+ {
+ result = tryAliasThisCast();
+ if (result)
+ return;
+ }
+
+ if (t1b.size(e.loc) == tob.size(e.loc))
+ goto Lok;
+
+ auto ts = toAutoQualChars(e.type, t);
+ e.error("cannot cast expression `%s` of type `%s` to `%s` because of different sizes",
+ e.toChars(), ts[0], ts[1]);
+ result = ErrorExp.get();
+ return;
+ }
+
+ // Fat values vs. null or references
+ if (tob_isFV && (t1b.ty == Tnull || t1b_isR) || t1b_isFV && (tob.ty == Tnull || tob_isR))
+ {
+ if (tob.ty == Tpointer && t1b.ty == Tsarray)
+ {
+ // T[n] sa;
+ // cast(U*)sa; // ==> cast(U*)sa.ptr;
+ result = new AddrExp(e.loc, e, t);
+ return;
+ }
+ if (tob.ty == Tarray && t1b.ty == Tsarray)
+ {
+ // T[n] sa;
+ // cast(U[])sa; // ==> cast(U[])sa[];
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (auto v = expToVariable(e))
+ {
+ if (e.type.hasPointers() && !checkAddressVar(sc, e, v))
+ goto Lfail;
+ }
+ }
+ const fsize = t1b.nextOf().size();
+ const tsize = tob.nextOf().size();
+ if (fsize != tsize)
+ {
+ const dim = t1b.isTypeSArray().dim.toInteger();
+ if (tsize == 0 || (dim * fsize) % tsize != 0)
+ {
+ e.error("cannot cast expression `%s` of type `%s` to `%s` since sizes don't line up",
+ e.toChars(), e.type.toChars(), t.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ goto Lok;
+ }
+ goto Lfail;
+ }
+
+ /* For references, any reinterpret casts are allowed to same 'ty' type.
+ * T* to U*
+ * R1 function(P1) to R2 function(P2)
+ * R1 delegate(P1) to R2 delegate(P2)
+ * T[] to U[]
+ * V1[K1] to V2[K2]
+ * class/interface A to B (will be a dynamic cast if possible)
+ */
+ if (tob.ty == t1b.ty && tob_isR && t1b_isR)
+ goto Lok;
+
+ // typeof(null) <-- non-null references or values
+ if (tob.ty == Tnull && t1b.ty != Tnull)
+ goto Lfail; // https://issues.dlang.org/show_bug.cgi?id=14629
+ // typeof(null) --> non-null references or arithmetic values
+ if (t1b.ty == Tnull && tob.ty != Tnull)
+ goto Lok;
+
+ // Check size mismatch of references.
+ // Tarray and Tdelegate are (void*).sizeof*2, but others have (void*).sizeof.
+ if (tob_isFR && t1b_isR || t1b_isFR && tob_isR)
+ {
+ if (tob.ty == Tpointer && t1b.ty == Tarray)
+ {
+ // T[] da;
+ // cast(U*)da; // ==> cast(U*)da.ptr;
+ goto Lok;
+ }
+ if (tob.ty == Tpointer && t1b.ty == Tdelegate)
+ {
+ // void delegate() dg;
+ // cast(U*)dg; // ==> cast(U*)dg.ptr;
+ // Note that it happens even when U is a Tfunction!
+ e.deprecation("casting from %s to %s is deprecated", e.type.toChars(), t.toChars());
+ goto Lok;
+ }
+ goto Lfail;
+ }
+
+ if (t1b.ty == Tvoid && tob.ty != Tvoid)
+ {
+ Lfail:
+ /* if the cast cannot be performed, maybe there is an alias
+ * this that can be used for casting.
+ */
+ if (hasAliasThis)
+ {
+ result = tryAliasThisCast();
+ if (result)
+ return;
+ }
+ e.error("cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+
+ Lok:
+ result = new CastExp(e.loc, e, t);
+ result.type = t; // Don't call semantic()
+ //printf("Returning: %s\n", result.toChars());
+ }
+
+ override void visit(ErrorExp e)
+ {
+ result = e;
+ }
+
+ override void visit(RealExp e)
+ {
+ if (!e.type.equals(t))
+ {
+ if ((e.type.isreal() && t.isreal()) || (e.type.isimaginary() && t.isimaginary()))
+ {
+ result = e.copy();
+ result.type = t;
+ }
+ else
+ visit(cast(Expression)e);
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(ComplexExp e)
+ {
+ if (!e.type.equals(t))
+ {
+ if (e.type.iscomplex() && t.iscomplex())
+ {
+ result = e.copy();
+ result.type = t;
+ }
+ else
+ visit(cast(Expression)e);
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ visit(cast(Expression)e);
+ if (result.op == TOK.structLiteral)
+ (cast(StructLiteralExp)result).stype = t; // commit type
+ }
+
+ override void visit(StringExp e)
+ {
+ /* This follows copy-on-write; any changes to 'this'
+ * will result in a copy.
+ * The this.string member is considered immutable.
+ */
+ int copied = 0;
+
+ //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t.toChars(), e.toChars(), e.committed);
+
+ if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid)
+ {
+ e.error("cannot convert string literal to `void*`");
+ result = ErrorExp.get();
+ return;
+ }
+
+ StringExp se = e;
+ if (!e.committed)
+ {
+ se = cast(StringExp)e.copy();
+ se.committed = 1;
+ copied = 1;
+ }
+
+ if (e.type.equals(t))
+ {
+ result = se;
+ return;
+ }
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ //printf("\ttype = %s\n", e.type.toChars());
+ if (tb.ty == Tdelegate && typeb.ty != Tdelegate)
+ {
+ visit(cast(Expression)e);
+ return;
+ }
+
+ if (typeb.equals(tb))
+ {
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+ se.type = t;
+ result = se;
+ return;
+ }
+
+ /* Handle reinterpret casts:
+ * cast(wchar[3])"abcd"c --> [\u6261, \u6463, \u0000]
+ * cast(wchar[2])"abcd"c --> [\u6261, \u6463]
+ * cast(wchar[1])"abcd"c --> [\u6261]
+ * cast(char[4])"a" --> ['a', 0, 0, 0]
+ */
+ if (e.committed && tb.ty == Tsarray && typeb.ty == Tarray)
+ {
+ se = cast(StringExp)e.copy();
+ d_uns64 szx = tb.nextOf().size();
+ assert(szx <= 255);
+ se.sz = cast(ubyte)szx;
+ se.len = cast(size_t)tb.isTypeSArray().dim.toInteger();
+ se.committed = 1;
+ se.type = t;
+
+ /* If larger than source, pad with zeros.
+ */
+ const fullSize = (se.len + 1) * se.sz; // incl. terminating 0
+ if (fullSize > (e.len + 1) * e.sz)
+ {
+ void* s = mem.xmalloc(fullSize);
+ const srcSize = e.len * e.sz;
+ const data = se.peekData();
+ memcpy(s, data.ptr, srcSize);
+ memset(s + srcSize, 0, fullSize - srcSize);
+ se.setData(s, se.len, se.sz);
+ }
+ result = se;
+ return;
+ }
+
+ if (tb.ty != Tsarray && tb.ty != Tarray && tb.ty != Tpointer)
+ {
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+ goto Lcast;
+ }
+ if (typeb.ty != Tsarray && typeb.ty != Tarray && typeb.ty != Tpointer)
+ {
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+ goto Lcast;
+ }
+
+ if (typeb.nextOf().size() == tb.nextOf().size())
+ {
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+ if (tb.ty == Tsarray)
+ goto L2; // handle possible change in static array dimension
+ se.type = t;
+ result = se;
+ return;
+ }
+
+ if (e.committed)
+ goto Lcast;
+
+ auto X(T, U)(T tf, U tt)
+ {
+ return (cast(int)tf * 256 + cast(int)tt);
+ }
+
+ {
+ OutBuffer buffer;
+ size_t newlen = 0;
+ int tfty = typeb.nextOf().toBasetype().ty;
+ int ttty = tb.nextOf().toBasetype().ty;
+ switch (X(tfty, ttty))
+ {
+ case X(Tchar, Tchar):
+ case X(Twchar, Twchar):
+ case X(Tdchar, Tdchar):
+ break;
+
+ case X(Tchar, Twchar):
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeChar(se.peekString(), u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ else
+ buffer.writeUTF16(c);
+ }
+ newlen = buffer.length / 2;
+ buffer.writeUTF16(0);
+ goto L1;
+
+ case X(Tchar, Tdchar):
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeChar(se.peekString(), u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ buffer.write4(c);
+ newlen++;
+ }
+ buffer.write4(0);
+ goto L1;
+
+ case X(Twchar, Tchar):
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeWchar(se.peekWstring(), u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ else
+ buffer.writeUTF8(c);
+ }
+ newlen = buffer.length;
+ buffer.writeUTF8(0);
+ goto L1;
+
+ case X(Twchar, Tdchar):
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeWchar(se.peekWstring(), u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ buffer.write4(c);
+ newlen++;
+ }
+ buffer.write4(0);
+ goto L1;
+
+ case X(Tdchar, Tchar):
+ for (size_t u = 0; u < e.len; u++)
+ {
+ uint c = se.peekDstring()[u];
+ if (!utf_isValidDchar(c))
+ e.error("invalid UCS-32 char \\U%08x", c);
+ else
+ buffer.writeUTF8(c);
+ newlen++;
+ }
+ newlen = buffer.length;
+ buffer.writeUTF8(0);
+ goto L1;
+
+ case X(Tdchar, Twchar):
+ for (size_t u = 0; u < e.len; u++)
+ {
+ uint c = se.peekDstring()[u];
+ if (!utf_isValidDchar(c))
+ e.error("invalid UCS-32 char \\U%08x", c);
+ else
+ buffer.writeUTF16(c);
+ newlen++;
+ }
+ newlen = buffer.length / 2;
+ buffer.writeUTF16(0);
+ goto L1;
+
+ L1:
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+
+ {
+ d_uns64 szx = tb.nextOf().size();
+ assert(szx <= 255);
+ se.setData(buffer.extractSlice().ptr, newlen, cast(ubyte)szx);
+ }
+ break;
+
+ default:
+ assert(typeb.nextOf().size() != tb.nextOf().size());
+ goto Lcast;
+ }
+ }
+ L2:
+ assert(copied);
+
+ // See if need to truncate or extend the literal
+ if (auto tsa = tb.isTypeSArray())
+ {
+ size_t dim2 = cast(size_t)tsa.dim.toInteger();
+ //printf("dim from = %d, to = %d\n", (int)se.len, (int)dim2);
+
+ // Changing dimensions
+ if (dim2 != se.len)
+ {
+ // Copy when changing the string literal
+ const newsz = se.sz;
+ const d = (dim2 < se.len) ? dim2 : se.len;
+ void* s = mem.xmalloc((dim2 + 1) * newsz);
+ memcpy(s, se.peekData().ptr, d * newsz);
+ // Extend with 0, add terminating 0
+ memset(s + d * newsz, 0, (dim2 + 1 - d) * newsz);
+ se.setData(s, dim2, newsz);
+ }
+ }
+ se.type = t;
+ result = se;
+ return;
+
+ Lcast:
+ result = new CastExp(e.loc, se, t);
+ result.type = t; // so semantic() won't be run on e
+ }
+
+ override void visit(AddrExp e)
+ {
+ version (none)
+ {
+ printf("AddrExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.equals(typeb))
+ {
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (e.e1.op == TOK.overloadSet &&
+ (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
+ {
+ OverExp eo = cast(OverExp)e.e1;
+ FuncDeclaration f = null;
+ for (size_t i = 0; i < eo.vars.a.dim; i++)
+ {
+ auto s = eo.vars.a[i];
+ auto f2 = s.isFuncDeclaration();
+ assert(f2);
+ if (f2.overloadExactMatch(tb.nextOf()))
+ {
+ if (f)
+ {
+ /* Error if match in more than one overload set,
+ * even if one is a 'better' match than the other.
+ */
+ ScopeDsymbol.multiplyDefined(e.loc, f, f2);
+ }
+ else
+ f = f2;
+ }
+ }
+ if (f)
+ {
+ f.tookAddressOf++;
+ auto se = new SymOffExp(e.loc, f, 0, false);
+ auto se2 = se.expressionSemantic(sc);
+ // Let SymOffExp::castTo() do the heavy lifting
+ visit(se2);
+ return;
+ }
+ }
+
+ if (e.e1.op == TOK.variable &&
+ typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
+ tb.ty == Tpointer && tb.nextOf().ty == Tfunction)
+ {
+ auto ve = cast(VarExp)e.e1;
+ auto f = ve.var.isFuncDeclaration();
+ if (f)
+ {
+ assert(f.isImportedSymbol());
+ f = f.overloadExactMatch(tb.nextOf());
+ if (f)
+ {
+ result = new VarExp(e.loc, f, false);
+ result.type = f.type;
+ result = new AddrExp(e.loc, result, t);
+ return;
+ }
+ }
+ }
+
+ if (auto f = isFuncAddress(e))
+ {
+ if (f.checkForwardRef(e.loc))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(TupleExp e)
+ {
+ if (e.type.equals(t))
+ {
+ result = e;
+ return;
+ }
+
+ TupleExp te = e.copy().isTupleExp();
+ te.e0 = e.e0 ? e.e0.copy() : null;
+ te.exps = e.exps.copy();
+ for (size_t i = 0; i < te.exps.dim; i++)
+ {
+ Expression ex = (*te.exps)[i];
+ ex = ex.castTo(sc, t);
+ (*te.exps)[i] = ex;
+ }
+ result = te;
+
+ /* Questionable behavior: In here, result.type is not set to t.
+ * Therefoe:
+ * TypeTuple!(int, int) values;
+ * auto values2 = cast(long)values;
+ * // typeof(values2) == TypeTuple!(int, int) !!
+ *
+ * Only when the casted tuple is immediately expanded, it would work.
+ * auto arr = [cast(long)values];
+ * // typeof(arr) == long[]
+ */
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ version (none)
+ {
+ printf("ArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+
+ ArrayLiteralExp ae = e;
+
+ Type tb = t.toBasetype();
+ if (tb.ty == Tarray && global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (checkArrayLiteralEscape(sc, ae, false))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ if (e.type == t)
+ {
+ result = e;
+ return;
+ }
+ Type typeb = e.type.toBasetype();
+
+ if ((tb.ty == Tarray || tb.ty == Tsarray) &&
+ (typeb.ty == Tarray || typeb.ty == Tsarray))
+ {
+ if (tb.nextOf().toBasetype().ty == Tvoid && typeb.nextOf().toBasetype().ty != Tvoid)
+ {
+ // Don't do anything to cast non-void[] to void[]
+ }
+ else if (typeb.ty == Tsarray && typeb.nextOf().toBasetype().ty == Tvoid)
+ {
+ // Don't do anything for casting void[n] to others
+ }
+ else
+ {
+ if (auto tsa = tb.isTypeSArray())
+ {
+ if (e.elements.dim != tsa.dim.toInteger())
+ goto L1;
+ }
+
+ ae = cast(ArrayLiteralExp)e.copy();
+ if (e.basis)
+ ae.basis = e.basis.castTo(sc, tb.nextOf());
+ ae.elements = e.elements.copy();
+ for (size_t i = 0; i < e.elements.dim; i++)
+ {
+ Expression ex = (*e.elements)[i];
+ if (!ex)
+ continue;
+ ex = ex.castTo(sc, tb.nextOf());
+ (*ae.elements)[i] = ex;
+ }
+ ae.type = t;
+ result = ae;
+ return;
+ }
+ }
+ else if (tb.ty == Tpointer && typeb.ty == Tsarray)
+ {
+ Type tp = typeb.nextOf().pointerTo();
+ if (!tp.equals(ae.type))
+ {
+ ae = cast(ArrayLiteralExp)e.copy();
+ ae.type = tp;
+ }
+ }
+ else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray))
+ {
+ // Convert array literal to vector type
+ TypeVector tv = tb.isTypeVector();
+ TypeSArray tbase = tv.basetype.isTypeSArray();
+ assert(tbase.ty == Tsarray);
+ const edim = e.elements.dim;
+ const tbasedim = tbase.dim.toInteger();
+ if (edim > tbasedim)
+ goto L1;
+
+ ae = e.copy().isArrayLiteralExp();
+ ae.type = tbase; // https://issues.dlang.org/show_bug.cgi?id=12642
+ ae.elements = e.elements.copy();
+ Type telement = tv.elementType();
+ foreach (i; 0 .. edim)
+ {
+ Expression ex = (*e.elements)[i];
+ ex = ex.castTo(sc, telement);
+ (*ae.elements)[i] = ex;
+ }
+ // Fill in the rest with the default initializer
+ ae.elements.setDim(cast(size_t)tbasedim);
+ foreach (i; edim .. cast(size_t)tbasedim)
+ {
+ Expression ex = typeb.nextOf.defaultInitLiteral(e.loc);
+ ex = ex.castTo(sc, telement);
+ (*ae.elements)[i] = ex;
+ }
+ Expression ev = new VectorExp(e.loc, ae, tb);
+ ev = ev.expressionSemantic(sc);
+ result = ev;
+ return;
+ }
+ L1:
+ visit(cast(Expression)ae);
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ //printf("AssocArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ if (e.type == t)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.ty == Taarray && typeb.ty == Taarray &&
+ tb.nextOf().toBasetype().ty != Tvoid)
+ {
+ AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e.copy();
+ ae.keys = e.keys.copy();
+ ae.values = e.values.copy();
+ assert(e.keys.dim == e.values.dim);
+ for (size_t i = 0; i < e.keys.dim; i++)
+ {
+ Expression ex = (*e.values)[i];
+ ex = ex.castTo(sc, tb.nextOf());
+ (*ae.values)[i] = ex;
+
+ ex = (*e.keys)[i];
+ ex = ex.castTo(sc, tb.isTypeAArray().index);
+ (*ae.keys)[i] = ex;
+ }
+ ae.type = t;
+ result = ae;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(SymOffExp e)
+ {
+ version (none)
+ {
+ printf("SymOffExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ if (e.type == t && !e.hasOverloads)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.equals(typeb))
+ {
+ result = e.copy();
+ result.type = t;
+ (cast(SymOffExp)result).hasOverloads = false;
+ return;
+ }
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (e.hasOverloads &&
+ typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
+ (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
+ {
+ FuncDeclaration f = e.var.isFuncDeclaration();
+ f = f ? f.overloadExactMatch(tb.nextOf()) : null;
+ if (f)
+ {
+ if (tb.ty == Tdelegate)
+ {
+ if (f.needThis() && hasThis(sc))
+ {
+ result = new DelegateExp(e.loc, new ThisExp(e.loc), f, false);
+ result = result.expressionSemantic(sc);
+ }
+ else if (f.needThis())
+ {
+ e.error("no `this` to create delegate for `%s`", f.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ else if (f.isNested())
+ {
+ result = new DelegateExp(e.loc, IntegerExp.literal!0, f, false);
+ result = result.expressionSemantic(sc);
+ }
+ else
+ {
+ e.error("cannot cast from function pointer to delegate");
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ else
+ {
+ result = new SymOffExp(e.loc, f, 0, false);
+ result.type = t;
+ }
+ f.tookAddressOf++;
+ return;
+ }
+ }
+
+ if (auto f = isFuncAddress(e))
+ {
+ if (f.checkForwardRef(e.loc))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(DelegateExp e)
+ {
+ version (none)
+ {
+ printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ __gshared const(char)* msg = "cannot form delegate due to covariant return type";
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.equals(typeb) && !e.hasOverloads)
+ {
+ int offset;
+ e.func.tookAddressOf++;
+ if (e.func.tintro && e.func.tintro.nextOf().isBaseOf(e.func.type.nextOf(), &offset) && offset)
+ e.error("%s", msg);
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+
+ // Look for delegates to functions where the functions are overloaded.
+ if (typeb.ty == Tdelegate && tb.ty == Tdelegate)
+ {
+ if (e.func)
+ {
+ auto f = e.func.overloadExactMatch(tb.nextOf());
+ if (f)
+ {
+ int offset;
+ if (f.tintro && f.tintro.nextOf().isBaseOf(f.type.nextOf(), &offset) && offset)
+ e.error("%s", msg);
+ if (f != e.func) // if address not already marked as taken
+ f.tookAddressOf++;
+ result = new DelegateExp(e.loc, e.e1, f, false, e.vthis2);
+ result.type = t;
+ return;
+ }
+ if (e.func.tintro)
+ e.error("%s", msg);
+ }
+ }
+
+ if (auto f = isFuncAddress(e))
+ {
+ if (f.checkForwardRef(e.loc))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp::castTo type = %s, t = %s\n", e.type.toChars(), t.toChars());
+ FuncExp fe;
+ if (e.matchType(t, sc, &fe, 1) > MATCH.nomatch)
+ {
+ result = fe;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(CondExp e)
+ {
+ if (!e.type.equals(t))
+ {
+ result = new CondExp(e.loc, e.econd, e.e1.castTo(sc, t), e.e2.castTo(sc, t));
+ result.type = t;
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(CommaExp e)
+ {
+ Expression e2c = e.e2.castTo(sc, t);
+
+ if (e2c != e.e2)
+ {
+ result = new CommaExp(e.loc, e.e1, e2c);
+ result.type = e2c.type;
+ }
+ else
+ {
+ result = e;
+ result.type = e.e2.type;
+ }
+ }
+
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp::castTo e = %s, type = %s, t = %s\n", e.toChars(), e.type.toChars(), t.toChars());
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (e.type.equals(t) || typeb.ty != Tarray ||
+ (tb.ty != Tarray && tb.ty != Tsarray))
+ {
+ visit(cast(Expression)e);
+ return;
+ }
+
+ if (tb.ty == Tarray)
+ {
+ if (typeb.nextOf().equivalent(tb.nextOf()))
+ {
+ // T[] to const(T)[]
+ result = e.copy();
+ result.type = t;
+ }
+ else
+ {
+ visit(cast(Expression)e);
+ }
+ return;
+ }
+
+ // Handle the cast from Tarray to Tsarray with CT-known slicing
+
+ TypeSArray tsa = toStaticArrayType(e).isTypeSArray();
+ if (tsa && tsa.size(e.loc) == tb.size(e.loc))
+ {
+ /* Match if the sarray sizes are equal:
+ * T[a .. b] to const(T)[b-a]
+ * T[a .. b] to U[dim] if (T.sizeof*(b-a) == U.sizeof*dim)
+ *
+ * If a SliceExp has Tsarray, it will become lvalue.
+ * That's handled in SliceExp::isLvalue and toLvalue
+ */
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+ if (tsa && tsa.dim.equals(tb.isTypeSArray().dim))
+ {
+ /* Match if the dimensions are equal
+ * with the implicit conversion of e.e1:
+ * cast(float[2]) [2.0, 1.0, 0.0][0..2];
+ */
+ Type t1b = e.e1.type.toBasetype();
+ if (t1b.ty == Tsarray)
+ t1b = tb.nextOf().sarrayOf(t1b.isTypeSArray().dim.toInteger());
+ else if (t1b.ty == Tarray)
+ t1b = tb.nextOf().arrayOf();
+ else if (t1b.ty == Tpointer)
+ t1b = tb.nextOf().pointerTo();
+ else
+ assert(0);
+ if (e.e1.implicitConvTo(t1b) > MATCH.nomatch)
+ {
+ Expression e1x = e.e1.implicitCastTo(sc, t1b);
+ assert(e1x.op != TOK.error);
+ e = cast(SliceExp)e.copy();
+ e.e1 = e1x;
+ e.type = t;
+ result = e;
+ return;
+ }
+ }
+ auto ts = toAutoQualChars(tsa ? tsa : e.type, t);
+ e.error("cannot cast expression `%s` of type `%s` to `%s`",
+ e.toChars(), ts[0], ts[1]);
+ result = ErrorExp.get();
+ }
+ }
+
+ // Casting to noreturn isn't an actual cast
+ // Rewrite cast(<qual> noreturn) <exp>
+ // as <exp>, assert(false)
+ if (t.isTypeNoreturn())
+ {
+ // Don't generate an unreachable assert(false) if e will abort
+ if (e.type.isTypeNoreturn())
+ {
+ // Paint e to accomodate for different type qualifiers
+ e.type = t;
+ return e;
+ }
+
+ auto ini = t.defaultInitLiteral(e.loc);
+ return Expression.combine(e, ini);
+ }
+
+ scope CastTo v = new CastTo(sc, t);
+ e.accept(v);
+ return v.result;
+}
+
+/****************************************
+ * Set type inference target
+ * t Target type
+ * flag 1: don't put an error when inference fails
+ */
+Expression inferType(Expression e, Type t, int flag = 0)
+{
+ Expression visitAle(ArrayLiteralExp ale)
+ {
+ Type tb = t.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ Type tn = tb.nextOf();
+ if (ale.basis)
+ ale.basis = inferType(ale.basis, tn, flag);
+ for (size_t i = 0; i < ale.elements.dim; i++)
+ {
+ if (Expression e = (*ale.elements)[i])
+ {
+ e = inferType(e, tn, flag);
+ (*ale.elements)[i] = e;
+ }
+ }
+ }
+ return ale;
+ }
+
+ Expression visitAar(AssocArrayLiteralExp aale)
+ {
+ Type tb = t.toBasetype();
+ if (auto taa = tb.isTypeAArray())
+ {
+ Type ti = taa.index;
+ Type tv = taa.nextOf();
+ for (size_t i = 0; i < aale.keys.dim; i++)
+ {
+ if (Expression e = (*aale.keys)[i])
+ {
+ e = inferType(e, ti, flag);
+ (*aale.keys)[i] = e;
+ }
+ }
+ for (size_t i = 0; i < aale.values.dim; i++)
+ {
+ if (Expression e = (*aale.values)[i])
+ {
+ e = inferType(e, tv, flag);
+ (*aale.values)[i] = e;
+ }
+ }
+ }
+ return aale;
+ }
+
+ Expression visitFun(FuncExp fe)
+ {
+ //printf("FuncExp::inferType('%s'), to=%s\n", fe.type ? fe.type.toChars() : "null", t.toChars());
+ if (t.ty == Tdelegate || t.ty == Tpointer && t.nextOf().ty == Tfunction)
+ {
+ fe.fd.treq = t;
+ }
+ return fe;
+ }
+
+ Expression visitTer(CondExp ce)
+ {
+ Type tb = t.toBasetype();
+ ce.e1 = inferType(ce.e1, tb, flag);
+ ce.e2 = inferType(ce.e2, tb, flag);
+ return ce;
+ }
+
+ if (t) switch (e.op)
+ {
+ case TOK.arrayLiteral: return visitAle(cast(ArrayLiteralExp) e);
+ case TOK.assocArrayLiteral: return visitAar(cast(AssocArrayLiteralExp) e);
+ case TOK.function_: return visitFun(cast(FuncExp) e);
+ case TOK.question: return visitTer(cast(CondExp) e);
+ default:
+ }
+ return e;
+}
+
+/****************************************
+ * Scale addition/subtraction to/from pointer.
+ */
+Expression scaleFactor(BinExp be, Scope* sc)
+{
+ Type t1b = be.e1.type.toBasetype();
+ Type t2b = be.e2.type.toBasetype();
+ Expression eoff;
+
+ if (t1b.ty == Tpointer && t2b.isintegral())
+ {
+ // Need to adjust operator by the stride
+ // Replace (ptr + int) with (ptr + (int * stride))
+ Type t = Type.tptrdiff_t;
+
+ d_uns64 stride = t1b.nextOf().size(be.loc);
+ if (!t.equals(t2b))
+ be.e2 = be.e2.castTo(sc, t);
+ eoff = be.e2;
+ be.e2 = new MulExp(be.loc, be.e2, new IntegerExp(Loc.initial, stride, t));
+ be.e2.type = t;
+ be.type = be.e1.type;
+ }
+ else if (t2b.ty == Tpointer && t1b.isintegral())
+ {
+ // Need to adjust operator by the stride
+ // Replace (int + ptr) with (ptr + (int * stride))
+ Type t = Type.tptrdiff_t;
+ Expression e;
+
+ d_uns64 stride = t2b.nextOf().size(be.loc);
+ if (!t.equals(t1b))
+ e = be.e1.castTo(sc, t);
+ else
+ e = be.e1;
+ eoff = e;
+ e = new MulExp(be.loc, e, new IntegerExp(Loc.initial, stride, t));
+ e.type = t;
+ be.type = be.e2.type;
+ be.e1 = be.e2;
+ be.e2 = e;
+ }
+ else
+ assert(0);
+
+ if (sc.func && !sc.intypeof)
+ {
+ eoff = eoff.optimize(WANTvalue);
+ if (eoff.op == TOK.int64 && eoff.toInteger() == 0)
+ {
+ }
+ else if (sc.func.setUnsafe())
+ {
+ be.error("pointer arithmetic not allowed in @safe functions");
+ return ErrorExp.get();
+ }
+ }
+
+ return be;
+}
+
+/**************************************
+ * Return true if e is an empty array literal with dimensionality
+ * equal to or less than type of other array.
+ * [], [[]], [[[]]], etc.
+ * I.e., make sure that [1,2] is compatible with [],
+ * [[1,2]] is compatible with [[]], etc.
+ */
+private bool isVoidArrayLiteral(Expression e, Type other)
+{
+ while (e.op == TOK.arrayLiteral && e.type.ty == Tarray && ((cast(ArrayLiteralExp)e).elements.dim == 1))
+ {
+ auto ale = cast(ArrayLiteralExp)e;
+ e = ale[0];
+ if (other.ty == Tsarray || other.ty == Tarray)
+ other = other.nextOf();
+ else
+ return false;
+ }
+ if (other.ty != Tsarray && other.ty != Tarray)
+ return false;
+ Type t = e.type;
+ return (e.op == TOK.arrayLiteral && t.ty == Tarray && t.nextOf().ty == Tvoid && (cast(ArrayLiteralExp)e).elements.dim == 0);
+}
+
+/**
+ * Merge types of `e1` and `e2` into a common subset
+ *
+ * Parameters `e1` and `e2` will be rewritten in place as needed.
+ *
+ * Params:
+ * sc = Current scope
+ * op = Operator such as `e1 op e2`. In practice, either TOK.question
+ * or one of the binary operator.
+ * pe1 = The LHS of the operation, will be rewritten
+ * pe2 = The RHS of the operation, will be rewritten
+ *
+ * Returns:
+ * The resulting type in case of success, `null` in case of error
+ */
+Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
+{
+ //printf("typeMerge() %s op %s\n", e1.toChars(), e2.toChars());
+
+ Expression e1 = pe1;
+ Expression e2 = pe2;
+
+ Type Lret(Type result)
+ {
+ pe1 = e1;
+ pe2 = e2;
+
+ version (none)
+ {
+ printf("-typeMerge() %s op %s\n", e1.toChars(), e2.toChars());
+ if (e1.type)
+ printf("\tt1 = %s\n", e1.type.toChars());
+ if (e2.type)
+ printf("\tt2 = %s\n", e2.type.toChars());
+ printf("\ttype = %s\n", result.toChars());
+ }
+ return result;
+ }
+
+ /// Converts one of the expression too the other
+ Type convert(ref Expression from, Type to)
+ {
+ from = from.castTo(sc, to);
+ return Lret(to);
+ }
+
+ /// Converts both expression to a third type
+ Type coerce(Type towards)
+ {
+ e1 = e1.castTo(sc, towards);
+ e2 = e2.castTo(sc, towards);
+ return Lret(towards);
+ }
+
+ Type t1b = e1.type.toBasetype();
+ Type t2b = e2.type.toBasetype();
+
+ if (op != TOK.question || t1b.ty != t2b.ty && (t1b.isTypeBasic() && t2b.isTypeBasic()))
+ {
+ if (op == TOK.question && t1b.ty.isSomeChar() && t2b.ty.isSomeChar())
+ {
+ e1 = e1.castTo(sc, Type.tdchar);
+ e2 = e2.castTo(sc, Type.tdchar);
+ }
+ else
+ {
+ e1 = integralPromotions(e1, sc);
+ e2 = integralPromotions(e2, sc);
+ }
+ }
+
+ MATCH m;
+ Type t1 = e1.type;
+ Type t2 = e2.type;
+ assert(t1);
+ Type t = t1;
+
+ /* The start type of alias this type recursion.
+ * In following case, we should save A, and stop recursion
+ * if it appears again.
+ * X -> Y -> [A] -> B -> A -> B -> ...
+ */
+ Type att1 = null;
+ Type att2 = null;
+
+ if (t1.mod != t2.mod &&
+ t1.ty == Tenum && t2.ty == Tenum &&
+ t1.isTypeEnum().sym == t2.isTypeEnum().sym)
+ {
+ ubyte mod = MODmerge(t1.mod, t2.mod);
+ t1 = t1.castMod(mod);
+ t2 = t2.castMod(mod);
+ }
+
+Lagain:
+ t1b = t1.toBasetype();
+ t2b = t2.toBasetype();
+
+ const ty = implicitConvCommonTy(t1b.ty, t2b.ty);
+ if (ty != Terror)
+ {
+ const ty1 = implicitConvTy1(t1b.ty, t2b.ty);
+ const ty2 = implicitConvTy1(t2b.ty, t1b.ty);
+
+ if (t1b.ty == ty1) // if no promotions
+ {
+ if (t1.equals(t2))
+ return Lret(t1);
+
+ if (t1b.equals(t2b))
+ return Lret(t1b);
+ }
+
+ t1 = Type.basic[ty1];
+ t2 = Type.basic[ty2];
+ e1 = e1.castTo(sc, t1);
+ e2 = e2.castTo(sc, t2);
+ return Lret(Type.basic[ty]);
+ }
+
+ t1 = t1b;
+ t2 = t2b;
+
+ if (t1.ty == Ttuple || t2.ty == Ttuple)
+ return null;
+
+ if (t1.equals(t2))
+ {
+ // merging can not result in new enum type
+ if (t.ty == Tenum)
+ return Lret(t1b);
+ return Lret(t);
+ }
+
+ if ((t1.ty == Tpointer && t2.ty == Tpointer) || (t1.ty == Tdelegate && t2.ty == Tdelegate))
+ {
+ // Bring pointers to compatible type
+ Type t1n = t1.nextOf();
+ Type t2n = t2.nextOf();
+
+ if (t1n.equals(t2n))
+ return Lret(t);
+
+ if (t1n.ty == Tvoid) // pointers to void are always compatible
+ return Lret(t2);
+
+ if (t2n.ty == Tvoid)
+ return Lret(t);
+
+ if (t1.implicitConvTo(t2))
+ return convert(e1, t2);
+
+ if (t2.implicitConvTo(t1))
+ return convert(e2, t1);
+
+ if (t1n.ty == Tfunction && t2n.ty == Tfunction)
+ {
+ TypeFunction tf1 = t1n.isTypeFunction();
+ TypeFunction tf2 = t2n.isTypeFunction();
+ tf1.purityLevel();
+ tf2.purityLevel();
+
+ TypeFunction d = tf1.syntaxCopy();
+
+ if (tf1.purity != tf2.purity)
+ d.purity = PURE.impure;
+ assert(d.purity != PURE.fwdref);
+
+ d.isnothrow = (tf1.isnothrow && tf2.isnothrow);
+ d.isnogc = (tf1.isnogc && tf2.isnogc);
+
+ if (tf1.trust == tf2.trust)
+ d.trust = tf1.trust;
+ else if (tf1.trust <= TRUST.system || tf2.trust <= TRUST.system)
+ d.trust = TRUST.system;
+ else
+ d.trust = TRUST.trusted;
+
+ Type tx = (t1.ty == Tdelegate) ? new TypeDelegate(d) : d.pointerTo();
+ tx = tx.typeSemantic(e1.loc, sc);
+
+ if (t1.implicitConvTo(tx) && t2.implicitConvTo(tx))
+ return coerce(tx);
+ return null;
+ }
+
+ if (t1n.mod != t2n.mod)
+ {
+ if (!t1n.isImmutable() && !t2n.isImmutable() && t1n.isShared() != t2n.isShared())
+ return null;
+ ubyte mod = MODmerge(t1n.mod, t2n.mod);
+ t1 = t1n.castMod(mod).pointerTo();
+ t2 = t2n.castMod(mod).pointerTo();
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t1n.ty == Tclass && t2n.ty == Tclass)
+ {
+ ClassDeclaration cd1 = t1n.isClassHandle();
+ ClassDeclaration cd2 = t2n.isClassHandle();
+ int offset;
+ if (cd1.isBaseOf(cd2, &offset))
+ {
+ if (offset)
+ e2 = e2.castTo(sc, t);
+ return Lret(t);
+ }
+
+ if (cd2.isBaseOf(cd1, &offset))
+ {
+ if (offset)
+ e1 = e1.castTo(sc, t2);
+ return Lret(t2);
+ }
+
+ return null;
+ }
+
+ t1 = t1n.constOf().pointerTo();
+ t2 = t2n.constOf().pointerTo();
+ if (t1.implicitConvTo(t2))
+ return convert(e1, t2);
+ if (t2.implicitConvTo(t1))
+ return convert(e2, t1);
+ return null;
+ }
+
+ if ((t1.ty == Tsarray || t1.ty == Tarray) && (e2.op == TOK.null_ && t2.ty == Tpointer && t2.nextOf().ty == Tvoid || e2.op == TOK.arrayLiteral && t2.ty == Tsarray && t2.nextOf().ty == Tvoid && t2.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e2, t1)))
+ {
+ /* (T[n] op void*) => T[]
+ * (T[] op void*) => T[]
+ * (T[n] op void[0]) => T[]
+ * (T[] op void[0]) => T[]
+ * (T[n] op void[]) => T[]
+ * (T[] op void[]) => T[]
+ */
+ return coerce(t1.nextOf().arrayOf());
+ }
+
+ if ((t2.ty == Tsarray || t2.ty == Tarray) && (e1.op == TOK.null_ && t1.ty == Tpointer && t1.nextOf().ty == Tvoid || e1.op == TOK.arrayLiteral && t1.ty == Tsarray && t1.nextOf().ty == Tvoid && t1.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e1, t2)))
+ {
+ /* (void* op T[n]) => T[]
+ * (void* op T[]) => T[]
+ * (void[0] op T[n]) => T[]
+ * (void[0] op T[]) => T[]
+ * (void[] op T[n]) => T[]
+ * (void[] op T[]) => T[]
+ */
+ return coerce(t2.nextOf().arrayOf());
+ }
+
+ if ((t1.ty == Tsarray || t1.ty == Tarray) && (m = t1.implicitConvTo(t2)) != MATCH.nomatch)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7285
+ // Tsarray op [x, y, ...] should to be Tsarray
+ // https://issues.dlang.org/show_bug.cgi?id=14737
+ // Tsarray ~ [x, y, ...] should to be Tarray
+ if (t1.ty == Tsarray && e2.op == TOK.arrayLiteral && op != TOK.concatenate)
+ return convert(e2, t1);
+ if (m == MATCH.constant && (op == TOK.addAssign || op == TOK.minAssign || op == TOK.mulAssign || op == TOK.divAssign || op == TOK.modAssign || op == TOK.powAssign || op == TOK.andAssign || op == TOK.orAssign || op == TOK.xorAssign))
+ {
+ // Don't make the lvalue const
+ return Lret(t2);
+ }
+ return convert(e1, t2);
+ }
+
+ if ((t2.ty == Tsarray || t2.ty == Tarray) && t2.implicitConvTo(t1))
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7285
+ // https://issues.dlang.org/show_bug.cgi?id=14737
+ if (t2.ty == Tsarray && e1.op == TOK.arrayLiteral && op != TOK.concatenate)
+ return convert(e1, t2);
+ return convert(e2, t1);
+ }
+
+ if ((t1.ty == Tsarray || t1.ty == Tarray || t1.ty == Tpointer) && (t2.ty == Tsarray || t2.ty == Tarray || t2.ty == Tpointer) && t1.nextOf().mod != t2.nextOf().mod)
+ {
+ /* If one is mutable and the other immutable, then retry
+ * with both of them as const
+ */
+ Type t1n = t1.nextOf();
+ Type t2n = t2.nextOf();
+ ubyte mod;
+ if (e1.op == TOK.null_ && e2.op != TOK.null_)
+ mod = t2n.mod;
+ else if (e1.op != TOK.null_ && e2.op == TOK.null_)
+ mod = t1n.mod;
+ else if (!t1n.isImmutable() && !t2n.isImmutable() && t1n.isShared() != t2n.isShared())
+ return null;
+ else
+ mod = MODmerge(t1n.mod, t2n.mod);
+
+ if (t1.ty == Tpointer)
+ t1 = t1n.castMod(mod).pointerTo();
+ else
+ t1 = t1n.castMod(mod).arrayOf();
+
+ if (t2.ty == Tpointer)
+ t2 = t2n.castMod(mod).pointerTo();
+ else
+ t2 = t2n.castMod(mod).arrayOf();
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t1.ty == Tclass && t2.ty == Tclass)
+ {
+ if (t1.mod != t2.mod)
+ {
+ ubyte mod;
+ if (e1.op == TOK.null_ && e2.op != TOK.null_)
+ mod = t2.mod;
+ else if (e1.op != TOK.null_ && e2.op == TOK.null_)
+ mod = t1.mod;
+ else if (!t1.isImmutable() && !t2.isImmutable() && t1.isShared() != t2.isShared())
+ return null;
+ else
+ mod = MODmerge(t1.mod, t2.mod);
+ t1 = t1.castMod(mod);
+ t2 = t2.castMod(mod);
+ t = t1;
+ goto Lagain;
+ }
+ goto Lcc;
+ }
+
+ if (t1.ty == Tclass || t2.ty == Tclass)
+ {
+ Lcc:
+ while (1)
+ {
+ MATCH i1 = e2.implicitConvTo(t1);
+ MATCH i2 = e1.implicitConvTo(t2);
+
+ if (i1 && i2)
+ {
+ // We have the case of class vs. void*, so pick class
+ if (t1.ty == Tpointer)
+ i1 = MATCH.nomatch;
+ else if (t2.ty == Tpointer)
+ i2 = MATCH.nomatch;
+ }
+
+ if (i2)
+ return coerce(t2);
+ if (i1)
+ return coerce(t1);
+
+ if (t1.ty == Tclass && t2.ty == Tclass)
+ {
+ TypeClass tc1 = t1.isTypeClass();
+ TypeClass tc2 = t2.isTypeClass();
+
+ /* Pick 'tightest' type
+ */
+ ClassDeclaration cd1 = tc1.sym.baseClass;
+ ClassDeclaration cd2 = tc2.sym.baseClass;
+ if (cd1 && cd2)
+ {
+ t1 = cd1.type.castMod(t1.mod);
+ t2 = cd2.type.castMod(t2.mod);
+ }
+ else if (cd1)
+ t1 = cd1.type;
+ else if (cd2)
+ t2 = cd2.type;
+ else
+ return null;
+ }
+ else if (t1.ty == Tstruct && t1.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att1, e1.type))
+ return null;
+ //printf("att tmerge(c || c) e1 = %s\n", e1.type.toChars());
+ e1 = resolveAliasThis(sc, e1);
+ t1 = e1.type;
+ continue;
+ }
+ else if (t2.ty == Tstruct && t2.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att2, e2.type))
+ return null;
+ //printf("att tmerge(c || c) e2 = %s\n", e2.type.toChars());
+ e2 = resolveAliasThis(sc, e2);
+ t2 = e2.type;
+ continue;
+ }
+ else
+ return null;
+ }
+ }
+
+ if (t1.ty == Tstruct && t2.ty == Tstruct)
+ {
+ if (t1.mod != t2.mod)
+ {
+ if (!t1.isImmutable() && !t2.isImmutable() && t1.isShared() != t2.isShared())
+ return null;
+ ubyte mod = MODmerge(t1.mod, t2.mod);
+ t1 = t1.castMod(mod);
+ t2 = t2.castMod(mod);
+ t = t1;
+ goto Lagain;
+ }
+
+ TypeStruct ts1 = t1.isTypeStruct();
+ TypeStruct ts2 = t2.isTypeStruct();
+ if (ts1.sym != ts2.sym)
+ {
+ if (!ts1.sym.aliasthis && !ts2.sym.aliasthis)
+ return null;
+
+ MATCH i1 = MATCH.nomatch;
+ MATCH i2 = MATCH.nomatch;
+
+ Expression e1b = null;
+ Expression e2b = null;
+ if (ts2.sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att2, e2.type))
+ return null;
+ //printf("att tmerge(s && s) e2 = %s\n", e2.type.toChars());
+ e2b = resolveAliasThis(sc, e2);
+ i1 = e2b.implicitConvTo(t1);
+ }
+ if (ts1.sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att1, e1.type))
+ return null;
+ //printf("att tmerge(s && s) e1 = %s\n", e1.type.toChars());
+ e1b = resolveAliasThis(sc, e1);
+ i2 = e1b.implicitConvTo(t2);
+ }
+ if (i1 && i2)
+ return null;
+
+ if (i1)
+ return convert(e2, t1);
+ if (i2)
+ return convert(e1, t2);
+
+ if (e1b)
+ {
+ e1 = e1b;
+ t1 = e1b.type.toBasetype();
+ }
+ if (e2b)
+ {
+ e2 = e2b;
+ t2 = e2b.type.toBasetype();
+ }
+ t = t1;
+ goto Lagain;
+ }
+ }
+
+ if (t1.ty == Tstruct || t2.ty == Tstruct)
+ {
+ if (t1.ty == Tstruct && t1.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att1, e1.type))
+ return null;
+ //printf("att tmerge(s || s) e1 = %s\n", e1.type.toChars());
+ e1 = resolveAliasThis(sc, e1);
+ t1 = e1.type;
+ t = t1;
+ goto Lagain;
+ }
+ if (t2.ty == Tstruct && t2.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att2, e2.type))
+ return null;
+ //printf("att tmerge(s || s) e2 = %s\n", e2.type.toChars());
+ e2 = resolveAliasThis(sc, e2);
+ t2 = e2.type;
+ t = t2;
+ goto Lagain;
+ }
+ return null;
+ }
+
+ if ((e1.op == TOK.string_ || e1.op == TOK.null_) && e1.implicitConvTo(t2))
+ return convert(e1, t2);
+ if ((e2.op == TOK.string_ || e2.op == TOK.null_) && e2.implicitConvTo(t1))
+ return convert(e2, t1);
+ if (t1.ty == Tsarray && t2.ty == Tsarray && e2.implicitConvTo(t1.nextOf().arrayOf()))
+ return coerce(t1.nextOf().arrayOf());
+ if (t1.ty == Tsarray && t2.ty == Tsarray && e1.implicitConvTo(t2.nextOf().arrayOf()))
+ return coerce(t2.nextOf().arrayOf());
+
+ if (t1.ty == Tvector && t2.ty == Tvector)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13841
+ // all vector types should have no common types between
+ // different vectors, even though their sizes are same.
+ auto tv1 = t1.isTypeVector();
+ auto tv2 = t2.isTypeVector();
+ if (!tv1.basetype.equals(tv2.basetype))
+ return null;
+
+ goto LmodCompare;
+ }
+
+ if (t1.ty == Tvector && t2.ty != Tvector && e2.implicitConvTo(t1))
+ {
+ e2 = e2.castTo(sc, t1);
+ t2 = t1;
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t2.ty == Tvector && t1.ty != Tvector && e1.implicitConvTo(t2))
+ {
+ e1 = e1.castTo(sc, t2);
+ t1 = t2;
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t1.isintegral() && t2.isintegral())
+ {
+ if (t1.ty != t2.ty)
+ {
+ if (t1.ty == Tvector || t2.ty == Tvector)
+ return null;
+ e1 = integralPromotions(e1, sc);
+ e2 = integralPromotions(e2, sc);
+ t1 = e1.type;
+ t2 = e2.type;
+ goto Lagain;
+ }
+ assert(t1.ty == t2.ty);
+LmodCompare:
+ if (!t1.isImmutable() && !t2.isImmutable() && t1.isShared() != t2.isShared())
+ return null;
+ ubyte mod = MODmerge(t1.mod, t2.mod);
+
+ t1 = t1.castMod(mod);
+ t2 = t2.castMod(mod);
+ t = t1;
+ e1 = e1.castTo(sc, t);
+ e2 = e2.castTo(sc, t);
+ goto Lagain;
+ }
+
+ if (t1.ty == Tnull && t2.ty == Tnull)
+ {
+ ubyte mod = MODmerge(t1.mod, t2.mod);
+ return coerce(t1.castMod(mod));
+ }
+
+ if (t2.ty == Tnull && (t1.ty == Tpointer || t1.ty == Taarray || t1.ty == Tarray))
+ return convert(e2, t1);
+ if (t1.ty == Tnull && (t2.ty == Tpointer || t2.ty == Taarray || t2.ty == Tarray))
+ return convert(e1, t2);
+
+ if (t1.ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e1))
+ {
+ if (e2.implicitConvTo(t1.nextOf()))
+ {
+ // T[] op T
+ // T[] op cast(T)U
+ e2 = e2.castTo(sc, t1.nextOf());
+ return Lret(t1.nextOf().arrayOf());
+ }
+ if (t1.nextOf().implicitConvTo(e2.type))
+ {
+ // (cast(T)U)[] op T (https://issues.dlang.org/show_bug.cgi?id=12780)
+ // e1 is left as U[], it will be handled in arrayOp() later.
+ return Lret(e2.type.arrayOf());
+ }
+ if (t2.ty == Tarray && isArrayOpOperand(e2))
+ {
+ if (t1.nextOf().implicitConvTo(t2.nextOf()))
+ {
+ // (cast(T)U)[] op T[] (https://issues.dlang.org/show_bug.cgi?id=12780)
+ t = t2.nextOf().arrayOf();
+ // if cast won't be handled in arrayOp() later
+ if (!isArrayOpImplicitCast(t1.isTypeDArray(), t2.isTypeDArray()))
+ e1 = e1.castTo(sc, t);
+ return Lret(t);
+ }
+ if (t2.nextOf().implicitConvTo(t1.nextOf()))
+ {
+ // T[] op (cast(T)U)[] (https://issues.dlang.org/show_bug.cgi?id=12780)
+ // e2 is left as U[], it will be handled in arrayOp() later.
+ t = t1.nextOf().arrayOf();
+ // if cast won't be handled in arrayOp() later
+ if (!isArrayOpImplicitCast(t2.isTypeDArray(), t1.isTypeDArray()))
+ e2 = e2.castTo(sc, t);
+ return Lret(t);
+ }
+ return null;
+ }
+ return null;
+ }
+ else if (t2.ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e2))
+ {
+ if (e1.implicitConvTo(t2.nextOf()))
+ {
+ // T op T[]
+ // cast(T)U op T[]
+ e1 = e1.castTo(sc, t2.nextOf());
+ t = t2.nextOf().arrayOf();
+ }
+ else if (t2.nextOf().implicitConvTo(e1.type))
+ {
+ // T op (cast(T)U)[] (https://issues.dlang.org/show_bug.cgi?id=12780)
+ // e2 is left as U[], it will be handled in arrayOp() later.
+ t = e1.type.arrayOf();
+ }
+ else
+ return null;
+
+ //printf("test %s\n", Token::toChars(op));
+ e1 = e1.optimize(WANTvalue);
+ if (isCommutative(op) && e1.isConst())
+ {
+ /* Swap operands to minimize number of functions generated
+ */
+ //printf("swap %s\n", Token::toChars(op));
+ Expression tmp = e1;
+ e1 = e2;
+ e2 = tmp;
+ }
+ return Lret(t);
+ }
+
+ return null;
+}
+
+/************************************
+ * Bring leaves to common type.
+ * Returns:
+ * null on success, ErrorExp if error occurs
+ */
+Expression typeCombine(BinExp be, Scope* sc)
+{
+ Expression errorReturn()
+ {
+ Expression ex = be.incompatibleTypes();
+ if (ex.op == TOK.error)
+ return ex;
+ return ErrorExp.get();
+ }
+
+ Type t1 = be.e1.type.toBasetype();
+ Type t2 = be.e2.type.toBasetype();
+
+ if (be.op == TOK.min || be.op == TOK.add)
+ {
+ // struct+struct, and class+class are errors
+ if (t1.ty == Tstruct && t2.ty == Tstruct)
+ return errorReturn();
+ else if (t1.ty == Tclass && t2.ty == Tclass)
+ return errorReturn();
+ else if (t1.ty == Taarray && t2.ty == Taarray)
+ return errorReturn();
+ }
+
+ if (auto result = typeMerge(sc, be.op, be.e1, be.e2))
+ {
+ if (be.type is null)
+ be.type = result;
+ }
+ else
+ return errorReturn();
+
+ // If the types have no value, return an error
+ if (be.e1.op == TOK.error)
+ return be.e1;
+ if (be.e2.op == TOK.error)
+ return be.e2;
+ return null;
+}
+
+/***********************************
+ * Do integral promotions (convertchk).
+ * Don't convert <array of> to <pointer to>
+ */
+Expression integralPromotions(Expression e, Scope* sc)
+{
+ //printf("integralPromotions %s %s\n", e.toChars(), e.type.toChars());
+ switch (e.type.toBasetype().ty)
+ {
+ case Tvoid:
+ e.error("void has no value");
+ return ErrorExp.get();
+
+ case Tint8:
+ case Tuns8:
+ case Tint16:
+ case Tuns16:
+ case Tbool:
+ case Tchar:
+ case Twchar:
+ e = e.castTo(sc, Type.tint32);
+ break;
+
+ case Tdchar:
+ e = e.castTo(sc, Type.tuns32);
+ break;
+
+ default:
+ break;
+ }
+ return e;
+}
+
+/******************************************************
+ * This provides a transition from the non-promoting behavior
+ * of unary + - ~ to the C-like integral promotion behavior.
+ * Params:
+ * sc = context
+ * ue = NegExp, UAddExp, or ComExp which is revised per rules
+ * References:
+ * https://issues.dlang.org/show_bug.cgi?id=16997
+ */
+
+void fix16997(Scope* sc, UnaExp ue)
+{
+ if (global.params.fix16997 || sc.flags & SCOPE.Cfile)
+ ue.e1 = integralPromotions(ue.e1, sc); // desired C-like behavor
+ else
+ {
+ switch (ue.e1.type.toBasetype.ty)
+ {
+ case Tint8:
+ case Tuns8:
+ case Tint16:
+ case Tuns16:
+ //case Tbool: // these operations aren't allowed on bool anyway
+ case Tchar:
+ case Twchar:
+ case Tdchar:
+ ue.deprecation("integral promotion not done for `%s`, use '-preview=intpromote' switch or `%scast(int)(%s)`",
+ ue.toChars(), Token.toChars(ue.op), ue.e1.toChars());
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/***********************************
+ * See if both types are arrays that can be compared
+ * for equality without any casting. Return true if so.
+ * This is to enable comparing things like an immutable
+ * array with a mutable one.
+ */
+extern (C++) bool arrayTypeCompatibleWithoutCasting(Type t1, Type t2)
+{
+ t1 = t1.toBasetype();
+ t2 = t2.toBasetype();
+
+ if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && t2.ty == t1.ty)
+ {
+ if (t1.nextOf().implicitConvTo(t2.nextOf()) >= MATCH.constant || t2.nextOf().implicitConvTo(t1.nextOf()) >= MATCH.constant)
+ return true;
+ }
+ return false;
+}
+
+/******************************************************************/
+/* Determine the integral ranges of an expression.
+ * This is used to determine if implicit narrowing conversions will
+ * be allowed.
+ */
+IntRange getIntRange(Expression e)
+{
+ extern (C++) final class IntRangeVisitor : Visitor
+ {
+ alias visit = Visitor.visit;
+
+ public:
+ IntRange range;
+
+ override void visit(Expression e)
+ {
+ range = IntRange.fromType(e.type);
+ }
+
+ override void visit(IntegerExp e)
+ {
+ range = IntRange(SignExtendedNumber(e.getInteger()))._cast(e.type);
+ }
+
+ override void visit(CastExp e)
+ {
+ range = getIntRange(e.e1)._cast(e.type);
+ }
+
+ override void visit(AddExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+ range = (ir1 + ir2)._cast(e.type);
+ }
+
+ override void visit(MinExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+ range = (ir1 - ir2)._cast(e.type);
+ }
+
+ override void visit(DivExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 / ir2)._cast(e.type);
+ }
+
+ override void visit(MulExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 * ir2)._cast(e.type);
+ }
+
+ override void visit(ModExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ // Modding on 0 is invalid anyway.
+ if (!ir2.absNeg().imin.negative)
+ {
+ visit(cast(Expression)e);
+ return;
+ }
+ range = (ir1 % ir2)._cast(e.type);
+ }
+
+ override void visit(AndExp e)
+ {
+ IntRange result;
+ bool hasResult = false;
+ result.unionOrAssign(getIntRange(e.e1) & getIntRange(e.e2), hasResult);
+
+ assert(hasResult);
+ range = result._cast(e.type);
+ }
+
+ override void visit(OrExp e)
+ {
+ IntRange result;
+ bool hasResult = false;
+ result.unionOrAssign(getIntRange(e.e1) | getIntRange(e.e2), hasResult);
+
+ assert(hasResult);
+ range = result._cast(e.type);
+ }
+
+ override void visit(XorExp e)
+ {
+ IntRange result;
+ bool hasResult = false;
+ result.unionOrAssign(getIntRange(e.e1) ^ getIntRange(e.e2), hasResult);
+
+ assert(hasResult);
+ range = result._cast(e.type);
+ }
+
+ override void visit(ShlExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 << ir2)._cast(e.type);
+ }
+
+ override void visit(ShrExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 >> ir2)._cast(e.type);
+ }
+
+ override void visit(UshrExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1).castUnsigned(e.e1.type);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 >>> ir2)._cast(e.type);
+ }
+
+ override void visit(AssignExp e)
+ {
+ range = getIntRange(e.e2)._cast(e.type);
+ }
+
+ override void visit(CondExp e)
+ {
+ // No need to check e.econd; assume caller has called optimize()
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+ range = ir1.unionWith(ir2)._cast(e.type);
+ }
+
+ override void visit(VarExp e)
+ {
+ Expression ie;
+ VarDeclaration vd = e.var.isVarDeclaration();
+ if (vd && vd.range)
+ range = vd.range._cast(e.type);
+ else if (vd && vd._init && !vd.type.isMutable() && (ie = vd.getConstInitializer()) !is null)
+ ie.accept(this);
+ else
+ visit(cast(Expression)e);
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+
+ override void visit(ComExp e)
+ {
+ IntRange ir = getIntRange(e.e1);
+ range = IntRange(SignExtendedNumber(~ir.imax.value, !ir.imax.negative), SignExtendedNumber(~ir.imin.value, !ir.imin.negative))._cast(e.type);
+ }
+
+ override void visit(NegExp e)
+ {
+ IntRange ir = getIntRange(e.e1);
+ range = (-ir)._cast(e.type);
+ }
+ }
+
+ scope IntRangeVisitor v = new IntRangeVisitor();
+ e.accept(v);
+ return v.range;
+}