| /** |
| * Semantic analysis for cast-expressions. |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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.hdrgen; |
| import dmd.impcnvtab; |
| import dmd.id; |
| import dmd.importc; |
| import dmd.init; |
| import dmd.intrange; |
| import dmd.mtype; |
| import dmd.opover; |
| import dmd.root.ctfloat; |
| import dmd.common.outbuffer; |
| import dmd.root.rmem; |
| import dmd.root.utf; |
| import dmd.tokens; |
| import dmd.typesem; |
| 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) |
| { |
| Expression 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. |
| */ |
| auto result = e.copy(); |
| result.type = t; |
| return result; |
| } |
| |
| 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)) |
| { |
| auto result = new CastExp(e.loc, e, t); |
| result.type = t; |
| return result; |
| } |
| } |
| |
| /* Forward the cast to our alias this member, rewrite to: |
| * cast(to)e1.aliasthis |
| */ |
| auto result = resolveAliasThis(sc, e); |
| return result.castTo(sc, t); |
| } |
| } |
| } |
| |
| return e.castTo(sc, t); |
| } |
| |
| auto result = e.optimize(WANTvalue); |
| if (result != e) |
| { |
| return implicitCastTo(result, sc, t); |
| } |
| |
| 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]); |
| } |
| } |
| return ErrorExp.get(); |
| } |
| |
| Expression visitString(StringExp e) |
| { |
| //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); |
| auto result = visit(e); |
| if (auto se = result.isStringExp()) |
| { |
| // Retain polysemous nature if it started out that way |
| se.committed = e.committed; |
| } |
| return result; |
| } |
| |
| Expression visitError(ErrorExp e) |
| { |
| return e; |
| } |
| |
| Expression visitFunc(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) |
| { |
| return fe; |
| } |
| return visit(e); |
| } |
| |
| Expression visitArrayLiteral(ArrayLiteralExp e) |
| { |
| auto result = visit(e); |
| |
| Type tb = result.type.toBasetype(); |
| if (auto ta = tb.isTypeDArray()) |
| if (global.params.useTypeInfo && Type.dtypeinfo) |
| semanticTypeInfo(sc, ta.next); |
| return result; |
| } |
| |
| Expression visitSlice(SliceExp e) |
| { |
| auto result = visit(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); |
| } |
| |
| return result; |
| } |
| |
| switch (e.op) |
| { |
| default : return visit (e); |
| case EXP.string_ : return visitString (e.isStringExp()); |
| case EXP.error : return visitError (e.isErrorExp()); |
| case EXP.function_ : return visitFunc (e.isFuncExp()); |
| case EXP.arrayLiteral: return visitArrayLiteral(e.isArrayLiteralExp()); |
| case EXP.slice : return visitSlice (e.isSliceExp()); |
| } |
| } |
| |
| /** |
| * 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) |
| { |
| MATCH 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 MATCH.nomatch; |
| 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)) |
| { |
| return MATCH.exact; |
| } |
| if (ex != e) |
| { |
| //printf("\toptimized to %s of type %s\n", e.toChars(), e.type.toChars()); |
| return ex.implicitConvTo(t); |
| } |
| |
| MATCH match = e.type.implicitConvTo(t); |
| if (match != MATCH.nomatch) |
| { |
| return match; |
| } |
| |
| /* 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)) |
| { |
| return MATCH.convert; |
| } |
| } |
| return MATCH.nomatch; |
| } |
| |
| /****** |
| * 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; |
| } |
| |
| // Apply mod bits to each function parameter, |
| // and see if we can convert the function argument to the modded type |
| static bool parametersModMatch(Expressions* args, TypeFunction tf, MOD mod) |
| { |
| const size_t nparams = tf.parameterList.length; |
| const size_t j = tf.isDstyleVariadic(); // if TypeInfoArray was prepended |
| foreach (const i; j .. args.dim) |
| { |
| 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.isLazy()) |
| return false; // 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 false; |
| 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 false; |
| } |
| return true; |
| } |
| |
| MATCH visitAdd(AddExp e) |
| { |
| version (none) |
| { |
| printf("AddExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| auto result = visit(e); |
| if (result == MATCH.nomatch) |
| result = implicitConvToAddMin(e, t); |
| return result; |
| } |
| |
| MATCH visitMin(MinExp e) |
| { |
| version (none) |
| { |
| printf("MinExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| auto result = visit(e); |
| if (result == MATCH.nomatch) |
| result = implicitConvToAddMin(e, t); |
| return result; |
| } |
| |
| MATCH visitInteger(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) |
| { |
| return m; |
| } |
| |
| TY ty = e.type.toBasetype().ty; |
| TY toty = t.toBasetype().ty; |
| TY oldty = ty; |
| |
| if (m == MATCH.nomatch && t.ty == Tenum) |
| return MATCH.nomatch; |
| |
| if (auto tv = t.isTypeVector()) |
| { |
| TypeBasic tb = tv.elementType(); |
| if (tb.ty == Tvoid) |
| return MATCH.nomatch; |
| 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 MATCH.nomatch; |
| break; |
| |
| case Tint8: |
| if (ty == Tuns64 && value & ~0x7FU) |
| return MATCH.nomatch; |
| else if (cast(byte)value != value) |
| return MATCH.nomatch; |
| break; |
| |
| case Tchar: |
| if ((oldty == Twchar || oldty == Tdchar) && value > 0x7F) |
| return MATCH.nomatch; |
| goto case Tuns8; |
| case Tuns8: |
| //printf("value = %llu %llu\n", cast(dinteger_t)cast(ubyte)value, value); |
| if (cast(ubyte)value != value) |
| return MATCH.nomatch; |
| break; |
| |
| case Tint16: |
| if (ty == Tuns64 && value & ~0x7FFFU) |
| return MATCH.nomatch; |
| else if (cast(short)value != value) |
| return MATCH.nomatch; |
| break; |
| |
| case Twchar: |
| if (oldty == Tdchar && value > 0xD7FF && value < 0xE000) |
| return MATCH.nomatch; |
| goto case Tuns16; |
| case Tuns16: |
| if (cast(ushort)value != value) |
| return MATCH.nomatch; |
| break; |
| |
| case Tint32: |
| if (ty == Tuns32) |
| { |
| } |
| else if (ty == Tuns64 && value & ~0x7FFFFFFFU) |
| return MATCH.nomatch; |
| else if (cast(int)value != value) |
| return MATCH.nomatch; |
| break; |
| |
| case Tuns32: |
| if (ty == Tint32) |
| { |
| } |
| else if (cast(uint)value != value) |
| return MATCH.nomatch; |
| break; |
| |
| case Tdchar: |
| if (value > 0x10FFFFU) |
| return MATCH.nomatch; |
| break; |
| |
| case Tfloat32: |
| if (!isLosslesslyConvertibleToFP!float) |
| return MATCH.nomatch; |
| break; |
| |
| case Tfloat64: |
| if (!isLosslesslyConvertibleToFP!double) |
| return MATCH.nomatch; |
| break; |
| |
| case Tfloat80: |
| if (!isLosslesslyConvertibleToFP!real_t) |
| return MATCH.nomatch; |
| 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: |
| return visit(e); |
| } |
| |
| //printf("MATCH.convert\n"); |
| return MATCH.convert; |
| } |
| |
| MATCH visitError(ErrorExp e) |
| { |
| return MATCH.nomatch; |
| } |
| |
| MATCH visitNull(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)) |
| { |
| return MATCH.exact; |
| } |
| |
| /* 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)) |
| { |
| return MATCH.constant; |
| } |
| |
| return visit(e); |
| } |
| |
| MATCH visitStructLiteral(StructLiteralExp e) |
| { |
| version (none) |
| { |
| printf("StructLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| auto result = visit(e); |
| if (result != MATCH.nomatch) |
| return result; |
| 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; |
| } |
| } |
| return result; |
| } |
| |
| MATCH visitString(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 MATCH.nomatch; |
| |
| if (!(e.type.ty == Tsarray || e.type.ty == Tarray || e.type.ty == Tpointer)) |
| return visit(e); |
| |
| TY tyn = e.type.nextOf().ty; |
| |
| if (!tyn.isSomeChar) |
| return visit(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()) |
| { |
| return MATCH.exact; |
| } |
| return MATCH.nomatch; |
| } |
| if (tynto.isSomeChar) |
| { |
| if (e.committed && tynto != tyn) |
| return MATCH.nomatch; |
| size_t fromlen = e.numberOfCodeUnits(tynto); |
| size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger(); |
| if (tolen < fromlen) |
| return MATCH.nomatch; |
| if (tolen != fromlen) |
| { |
| // implicit length extending |
| return MATCH.convert; |
| } |
| } |
| if (!e.committed && tynto.isSomeChar) |
| { |
| return MATCH.exact; |
| } |
| } |
| else if (e.type.ty == Tarray) |
| { |
| TY tynto = t.nextOf().ty; |
| if (tynto.isSomeChar) |
| { |
| if (e.committed && tynto != tyn) |
| return MATCH.nomatch; |
| size_t fromlen = e.numberOfCodeUnits(tynto); |
| size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger(); |
| if (tolen < fromlen) |
| return MATCH.nomatch; |
| if (tolen != fromlen) |
| { |
| // implicit length extending |
| return MATCH.convert; |
| } |
| } |
| if (tynto == tyn) |
| { |
| return MATCH.exact; |
| } |
| if (!e.committed && tynto.isSomeChar) |
| { |
| return MATCH.exact; |
| } |
| } |
| 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 MATCH.nomatch; |
| m = MATCH.constant; |
| } |
| if (!e.committed) |
| { |
| switch (tn.ty) |
| { |
| case Tchar: |
| if (e.postfix == 'w' || e.postfix == 'd') |
| m = MATCH.convert; |
| return m; |
| case Twchar: |
| if (e.postfix != 'w') |
| m = MATCH.convert; |
| return m; |
| case Tdchar: |
| if (e.postfix != 'd') |
| m = MATCH.convert; |
| return m; |
| case Tenum: |
| if (tn.isTypeEnum().sym.isSpecial()) |
| { |
| /* Allow string literal -> const(wchar_t)[] |
| */ |
| if (TypeBasic tob = tn.toBasetype().isTypeBasic()) |
| return tn.implicitConvTo(tob); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return visit(e); |
| } |
| |
| MATCH visitArrayLiteral(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(); |
| |
| auto result = MATCH.nomatch; |
| 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 result; |
| } |
| 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) |
| { |
| return MATCH.nomatch; |
| } |
| |
| 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 result; |
| } |
| |
| return visit(e); |
| } |
| |
| MATCH visitAssocArrayLiteral(AssocArrayLiteralExp e) |
| { |
| auto taa = t.toBasetype().isTypeAArray(); |
| Type typeb = e.type.toBasetype(); |
| |
| if (!(taa && typeb.ty == Taarray)) |
| return visit(e); |
| |
| auto 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 |
| } |
| return result; |
| } |
| |
| MATCH visitCall(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()); |
| } |
| |
| auto result = visit(e); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| /* Allow the result of strongly pure functions to |
| * convert to immutable |
| */ |
| if (e.f && |
| (!global.params.fixImmutableConv || e.f.isPure() >= PURE.const_) && |
| 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 result; |
| } |
| |
| /* 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 result; |
| |
| if (tf.purity == PURE.impure) |
| return result; |
| if (e.f && e.f.isNested()) |
| return result; |
| |
| /* 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 result; |
| } |
| // 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 result; // 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 |
| */ |
| 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 result; |
| } |
| |
| if (!parametersModMatch(e.arguments, tf, mod)) |
| return result; |
| |
| /* Success |
| */ |
| return MATCH.constant; |
| } |
| |
| MATCH visitAddr(AddrExp e) |
| { |
| version (none) |
| { |
| printf("AddrExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| auto result = e.type.implicitConvTo(t); |
| //printf("\tresult = %d\n", result); |
| |
| if (result != MATCH.nomatch) |
| return result; |
| |
| Type tb = t.toBasetype(); |
| Type typeb = e.type.toBasetype(); |
| |
| // Look for pointers to functions where the functions are overloaded. |
| if (e.e1.op == EXP.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 == EXP.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); |
| return result; |
| } |
| |
| MATCH visitSymOff(SymOffExp e) |
| { |
| version (none) |
| { |
| printf("SymOffExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| auto result = e.type.implicitConvTo(t); |
| //printf("\tresult = %d\n", result); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| 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); |
| return result; |
| } |
| |
| MATCH visitDelegate(DelegateExp e) |
| { |
| version (none) |
| { |
| printf("DelegateExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| auto result = e.type.implicitConvTo(t); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| 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; |
| } |
| return result; |
| } |
| |
| MATCH visitFunc(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) |
| { |
| return m; |
| } |
| return visit(e); |
| } |
| |
| MATCH visitAnd(AndExp e) |
| { |
| auto result = visit(e); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| MATCH m1 = e.e1.implicitConvTo(t); |
| MATCH m2 = e.e2.implicitConvTo(t); |
| |
| // Pick the worst match |
| return (m1 < m2) ? m1 : m2; |
| } |
| |
| MATCH visitOr(OrExp e) |
| { |
| auto result = visit(e); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| MATCH m1 = e.e1.implicitConvTo(t); |
| MATCH m2 = e.e2.implicitConvTo(t); |
| |
| // Pick the worst match |
| return (m1 < m2) ? m1 : m2; |
| } |
| |
| MATCH visitXor(XorExp e) |
| { |
| auto result = visit(e); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| MATCH m1 = e.e1.implicitConvTo(t); |
| MATCH m2 = e.e2.implicitConvTo(t); |
| |
| // Pick the worst match |
| return (m1 < m2) ? m1 : m2; |
| } |
| |
| MATCH visitCond(CondExp e) |
| { |
| e.econd = e.econd.optimize(WANTvalue); |
| const opt = e.econd.toBool(); |
| if (opt.isPresent()) |
| { |
| auto result = visit(e); |
| if (result != MATCH.nomatch) |
| return result; |
| } |
| |
| 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 |
| return (m1 < m2) ? m1 : m2; |
| } |
| |
| MATCH visitComma(CommaExp e) |
| { |
| return e.e2.implicitConvTo(t); |
| } |
| |
| MATCH visitCast(CastExp e) |
| { |
| version (none) |
| { |
| printf("CastExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| auto result = e.type.implicitConvTo(t); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| if (t.isintegral() && e.e1.type.isintegral() && e.e1.implicitConvTo(t) != MATCH.nomatch) |
| result = MATCH.convert; |
| else |
| result = visit(e); |
| return result; |
| } |
| |
| MATCH visitNew(NewExp e) |
| { |
| version (none) |
| { |
| printf("NewExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| auto result = visit(e); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| /* 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 |
| * 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 MATCH.nomatch; |
| |
| /* 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 MATCH.nomatch; // 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 MATCH.nomatch; |
| } |
| |
| /* Check call to 'member' |
| */ |
| if (e.member) |
| { |
| FuncDeclaration fd = e.member; |
| if (fd.errors || fd.type.ty != Tfunction) |
| return MATCH.nomatch; // error |
| TypeFunction tf = fd.type.isTypeFunction(); |
| if (tf.purity == PURE.impure) |
| return MATCH.nomatch; // impure |
| |
| // Allow a conversion to immutable type, or |
| // conversions of mutable types between thread-local and shared. |
| 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 MATCH.nomatch; |
| } |
| |
| if (!parametersModMatch(e.arguments, tf, mod)) |
| { |
| return MATCH.nomatch; |
| } |
| } |
| |
| /* 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 MATCH.nomatch; |
| } |
| } |
| |
| /* 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 MATCH.nomatch; |
| } |
| 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 MATCH.nomatch; // 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 MATCH.nomatch; |
| } |
| } |
| else |
| { |
| Expression earg = e.newtype.defaultInitLiteral(e.loc); |
| Type targ = e.newtype.toBasetype(); |
| |
| if (implicitMod(earg, targ, mod) == MATCH.nomatch) |
| return MATCH.nomatch; |
| } |
| |
| /* Success |
| */ |
| return MATCH.constant; |
| } |
| |
| MATCH visitSlice(SliceExp e) |
| { |
| //printf("SliceExp::implicitConvTo e = %s, type = %s\n", e.toChars(), e.type.toChars()); |
| auto result = visit(e); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| 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 result; |
| } |
| |
| /* 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 == EXP.string_) |
| result = e.e1.implicitConvTo(t); |
| return result; |
| } |
| |
| MATCH visitTuple(TupleExp e) |
| { |
| auto result = e.type.implicitConvTo(t); |
| if (result != MATCH.nomatch) |
| return result; |
| |
| /* If target type is a tuple of same length, test conversion of |
| * each expression to the corresponding type in the tuple. |
| */ |
| TypeTuple totuple = t.isTypeTuple(); |
| if (totuple && e.exps.length == totuple.arguments.length) |
| { |
| result = MATCH.exact; |
| foreach (i, ex; *e.exps) |
| { |
| auto to = (*totuple.arguments)[i].type; |
| MATCH mi = ex.implicitConvTo(to); |
| if (mi < result) |
| result = mi; |
| } |
| } |
| return result; |
| } |
| |
| switch (e.op) |
| { |
| default : return visit(e); |
| case EXP.add : return visitAdd(e.isAddExp()); |
| case EXP.min : return visitMin(e.isMinExp()); |
| case EXP.int64 : return visitInteger(e.isIntegerExp()); |
| case EXP.error : return visitError(e.isErrorExp()); |
| case EXP.null_ : return visitNull(e.isNullExp()); |
| case EXP.structLiteral : return visitStructLiteral(e.isStructLiteralExp()); |
| case EXP.string_ : return visitString(e.isStringExp()); |
| case EXP.arrayLiteral : return visitArrayLiteral(e.isArrayLiteralExp()); |
| case EXP.assocArrayLiteral: return visitAssocArrayLiteral(e.isAssocArrayLiteralExp()); |
| case EXP.call : return visitCall(e.isCallExp()); |
| case EXP.address : return visitAddr(e.isAddrExp()); |
| case EXP.symbolOffset : return visitSymOff(e.isSymOffExp()); |
| case EXP.delegate_ : return visitDelegate(e.isDelegateExp()); |
| case EXP.function_ : return visitFunc(e.isFuncExp()); |
| case EXP.and : return visitAnd(e.isAndExp()); |
| case EXP.or : return visitOr(e.isOrExp()); |
| case EXP.xor : return visitXor(e.isXorExp()); |
| case EXP.question : return visitCond(e.isCondExp()); |
| case EXP.comma : return visitComma(e.isCommaExp()); |
| case EXP.cast_ : return visitCast(e.isCastExp()); |
| case EXP.new_ : return visitNew(e.isNewExp()); |
| case EXP.slice : return visitSlice(e.isSliceExp()); |
| case EXP.tuple : return visitTuple(e.isTupleExp()); |
| } |
| } |
| |
| /** |
| * 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) // C11 6.3.2.3-7 |
| return MATCH.convert; |
| |
| 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) |
| { |
| Expression 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)) |
| { |
| return e; |
| } |
| if (auto ve = e.isVarExp()) |
| { |
| VarDeclaration v = ve.var.isVarDeclaration(); |
| if (v && v.storage_class & STC.manifest) |
| { |
| auto 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; |
| return result.castTo(sc, t); |
| } |
| } |
| |
| Type tob = t.toBasetype(); |
| Type t1b = e.type.toBasetype(); |
| if (tob.equals(t1b)) |
| { |
| auto result = e.copy(); // because of COW for assignment to e.type |
| result.type = t; |
| return result; |
| } |
| |
| /* 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(); |
| Expression result = new CastExp(e.loc, e, tv.elementType()); |
| result = new VectorExp(e.loc, result, tob); |
| result = result.expressionSemantic(sc); |
| return result; |
| } |
| 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())) |
| { |
| auto result = e.copy(); |
| result.type = t; |
| return result; |
| } |
| |
| // 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) |
| { |
| auto result = tryAliasThisCast(); |
| if (result) |
| return result; |
| } |
| |
| 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]); |
| return ErrorExp.get(); |
| } |
| |
| // 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; |
| return new AddrExp(e.loc, e, t); |
| } |
| if (tob.ty == Tarray && t1b.ty == Tsarray) |
| { |
| // T[n] sa; |
| // cast(U[])sa; // ==> cast(U[])sa[]; |
| const fsize = t1b.nextOf().size(); |
| const tsize = tob.nextOf().size(); |
| if (fsize == SIZE_INVALID || tsize == SIZE_INVALID) |
| { |
| return ErrorExp.get(); |
| } |
| 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()); |
| return ErrorExp.get(); |
| } |
| } |
| 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) |
| { |
| auto result = tryAliasThisCast(); |
| if (result) |
| return result; |
| } |
| e.error("cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars()); |
| return ErrorExp.get(); |
| } |
| |
| Lok: |
| auto result = new CastExp(e.loc, e, t); |
| result.type = t; // Don't call semantic() |
| //printf("Returning: %s\n", result.toChars()); |
| return result; |
| } |
| |
| Expression visitError(ErrorExp e) |
| { |
| return e; |
| } |
| |
| Expression visitReal(RealExp e) |
| { |
| if (!e.type.equals(t)) |
| { |
| if ((e.type.isreal() && t.isreal()) || (e.type.isimaginary() && t.isimaginary())) |
| { |
| auto result = e.copy(); |
| result.type = t; |
| return result; |
| } |
| else |
| return visit(e); |
| } |
| return e; |
| } |
| |
| Expression visitComplex(ComplexExp e) |
| { |
| if (!e.type.equals(t)) |
| { |
| if (e.type.iscomplex() && t.iscomplex()) |
| { |
| auto result = e.copy(); |
| result.type = t; |
| return result; |
| } |
| else |
| return visit(e); |
| } |
| return e; |
| } |
| |
| Expression visitStructLiteral(StructLiteralExp e) |
| { |
| auto result = visit(e); |
| if (auto sle = result.isStructLiteralExp()) |
| sle.stype = t; // commit type |
| return result; |
| } |
| |
| Expression visitString(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 && |
| (!sc || !(sc.flags & SCOPE.Cfile))) |
| { |
| e.error("cannot convert string literal to `void*`"); |
| return ErrorExp.get(); |
| } |
| |
| StringExp se = e; |
| |
| Expression lcast() |
| { |
| auto result = new CastExp(e.loc, se, t); |
| result.type = t; // so semantic() won't be run on e |
| return result; |
| } |
| |
| if (!e.committed) |
| { |
| se = e.copy().isStringExp(); |
| se.committed = 1; |
| copied = 1; |
| } |
| |
| if (e.type.equals(t)) |
| { |
| return se; |
| } |
| |
| Type tb = t.toBasetype(); |
| Type typeb = e.type.toBasetype(); |
| |
| //printf("\ttype = %s\n", e.type.toChars()); |
| if (tb.ty == Tdelegate && typeb.ty != Tdelegate) |
| { |
| return visit(e); |
| } |
| |
| if (typeb.equals(tb)) |
| { |
| if (!copied) |
| { |
| se = e.copy().isStringExp(); |
| copied = 1; |
| } |
| se.type = t; |
| return se; |
| } |
| |
| /* 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 = e.copy().isStringExp(); |
| uinteger_t 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); |
| } |
| return se; |
| } |
| |
| if (tb.ty != Tsarray && tb.ty != Tarray && tb.ty != Tpointer) |
| { |
| if (!copied) |
| { |
| se = e.copy().isStringExp(); |
| copied = 1; |
| } |
| return lcast(); |
| } |
| if (typeb.ty != Tsarray && typeb.ty != Tarray && typeb.ty != Tpointer) |
| { |
| if (!copied) |
| { |
| se = e.copy().isStringExp(); |
| copied = 1; |
| } |
| return lcast(); |
| } |
| |
| const nextSz = typeb.nextOf().size(); |
| if (nextSz == SIZE_INVALID) |
| { |
| return ErrorExp.get(); |
| } |
| if (nextSz == tb.nextOf().size()) |
| { |
| if (!copied) |
| { |
| se = e.copy().isStringExp(); |
| copied = 1; |
| } |
| if (tb.ty == Tsarray) |
| goto L2; // handle possible change in static array dimension |
| se.type = t; |
| return se; |
| } |
| |
| 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 = e.copy().isStringExp(); |
| copied = 1; |
| } |
| |
| { |
| uinteger_t 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", cast(int)se.len, cast(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; |
| return se; |
| |
| Lcast: |
| auto result = new CastExp(e.loc, se, t); |
| result.type = t; // so semantic() won't be run on e |
| return result; |
| } |
| |
| Expression visitAddr(AddrExp e) |
| { |
| version (none) |
| { |
| printf("AddrExp::castTo(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.equals(typeb)) |
| { |
| auto result = e.copy(); |
| result.type = t; |
| return result; |
| } |
| |
| // Look for pointers to functions where the functions are overloaded. |
| if (e.e1.isOverExp() && |
| (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction) |
| { |
| OverExp eo = e.e1.isOverExp(); |
| 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 |
| return visit(se2); |
| } |
| } |
| |
| if (e.e1.isVarExp() && |
| typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction && |
| tb.ty == Tpointer && tb.nextOf().ty == Tfunction) |
| { |
| auto ve = e.e1.isVarExp(); |
| auto f = ve.var.isFuncDeclaration(); |
| if (f) |
| { |
| assert(f.isImportedSymbol()); |
| f = f.overloadExactMatch(tb.nextOf()); |
| if (f) |
| { |
| Expression result = new VarExp(e.loc, f, false); |
| result.type = f.type; |
| result = new AddrExp(e.loc, result, t); |
| return result; |
| } |
| } |
| } |
| |
| if (auto f = isFuncAddress(e)) |
| { |
| if (f.checkForwardRef(e.loc)) |
| { |
| return ErrorExp.get(); |
| } |
| } |
| |
| return visit(e); |
| } |
| |
| Expression visitTuple(TupleExp e) |
| { |
| if (e.type.equals(t)) |
| { |
| return e; |
| } |
| |
| /* If target type is a tuple of same length, cast each expression to |
| * the corresponding type in the tuple. |
| */ |
| TypeTuple totuple; |
| if (auto tt = t.isTypeTuple()) |
| totuple = e.exps.length == tt.arguments.length ? tt : null; |
| |
| 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, totuple ? (*totuple.arguments)[i].type : t); |
| (*te.exps)[i] = ex; |
| } |
| if (totuple) |
| te.type = totuple; |
| return te; |
| |
| /* Questionable behavior: In here, result.type is not set to t |
| * if target type is not a tuple of same length. |
| * 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[] |
| */ |
| } |
| |
| Expression visitArrayLiteral(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) |
| { |
| if (checkArrayLiteralEscape(sc, ae, false)) |
| { |
| return ErrorExp.get(); |
| } |
| } |
| |
| if (e.type == t) |
| { |
| return e; |
| } |
| 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 = e.copy().isArrayLiteralExp(); |
| 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; |
| return ae; |
| } |
| } |
| else if (tb.ty == Tpointer && typeb.ty == Tsarray) |
| { |
| Type tp = typeb.nextOf().pointerTo(); |
| if (!tp.equals(ae.type)) |
| { |
| ae = e.copy().isArrayLiteralExp(); |
| 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); |
| return ev; |
| } |
| L1: |
| return visit(ae); |
| } |
| |
| Expression visitAssocArrayLiteral(AssocArrayLiteralExp e) |
| { |
| //printf("AssocArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| if (e.type == t) |
| { |
| return e; |
| } |
| |
| Type tb = t.toBasetype(); |
| Type typeb = e.type.toBasetype(); |
| |
| if (tb.ty == Taarray && typeb.ty == Taarray && |
| tb.nextOf().toBasetype().ty != Tvoid) |
| { |
| AssocArrayLiteralExp ae = e.copy().isAssocArrayLiteralExp(); |
| 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; |
| return ae; |
| } |
| return visit(e); |
| } |
| |
| Expression visitSymOff(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) |
| { |
| return e; |
| } |
| |
| Type tb = t.toBasetype(); |
| Type typeb = e.type.toBasetype(); |
| |
| if (tb.equals(typeb)) |
| { |
| auto result = e.copy(); |
| result.type = t; |
| result.isSymOffExp().hasOverloads = false; |
| return result; |
| } |
| |
| // 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) |
| { |
| Expression result; |
| 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()); |
| return ErrorExp.get(); |
| } |
| 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"); |
| return ErrorExp.get(); |
| } |
| } |
| else |
| { |
| result = new SymOffExp(e.loc, f, 0, false); |
| result.type = t; |
| } |
| f.tookAddressOf++; |
| return result; |
| } |
| } |
| |
| if (auto f = isFuncAddress(e)) |
| { |
| if (f.checkForwardRef(e.loc)) |
| { |
| return ErrorExp.get(); |
| } |
| } |
| |
| return visit(e); |
| } |
| |
| Expression visitDelegate(DelegateExp e) |
| { |
| version (none) |
| { |
| printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); |
| } |
| static immutable 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.ptr); |
| auto result = e.copy(); |
| result.type = t; |
| return result; |
| } |
| |
| // 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.ptr); |
| if (f != e.func) // if address not already marked as taken |
| f.tookAddressOf++; |
| auto result = new DelegateExp(e.loc, e.e1, f, false, e.vthis2); |
| result.type = t; |
| return result; |
| } |
| if (e.func.tintro) |
| e.error("%s", msg.ptr); |
| } |
| } |
| |
| if (auto f = isFuncAddress(e)) |
| { |
| if (f.checkForwardRef(e.loc)) |
| { |
| return ErrorExp.get(); |
| } |
| } |
| |
| return visit(e); |
| } |
| |
| Expression visitFunc(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) |
| { |
| return fe; |
| } |
| return visit(e); |
| } |
| |
| Expression visitCond(CondExp e) |
| { |
| if (!e.type.equals(t)) |
| { |
| auto result = new CondExp(e.loc, e.econd, e.e1.castTo(sc, t), e.e2.castTo(sc, t)); |
| result.type = t; |
| return result; |
| } |
| return e; |
| } |
| |
| Expression visitComma(CommaExp e) |
| { |
| Expression e2c = e.e2.castTo(sc, t); |
| |
| if (e2c != e.e2) |
| { |
| auto result = new CommaExp(e.loc, e.e1, e2c); |
| result.type = e2c.type; |
| return result; |
| } |
| else |
| { |
| e.type = e.e2.type; |
| return e; |
| } |
| } |
| |
| Expression visitSlice(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)) |
| { |
| return visit(e); |
| } |
| |
| if (tb.ty == Tarray) |
| { |
| if (typeb.nextOf().equivalent(tb.nextOf())) |
| { |
| // T[] to const(T)[] |
| auto result = e.copy(); |
| result.type = t; |
| return result; |
| } |
| else |
| { |
| return visit(e); |
| } |
| } |
| |
| // 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 |
| */ |
| auto result = e.copy(); |
| result.type = t; |
| return result; |
| } |
| 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 != EXP.error); |
| e = e.copy().isSliceExp(); |
| e.e1 = e1x; |
| e.type = t; |
| return e; |
| } |
| } |
| 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]); |
| return 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); |
| } |
| |
| switch (e.op) |
| { |
| default : return visit(e); |
| case EXP.error : return visitError(e.isErrorExp()); |
| case EXP.float64 : return visitReal(e.isRealExp()); |
| case EXP.complex80 : return visitComplex(e.isComplexExp()); |
| case EXP.structLiteral : return visitStructLiteral(e.isStructLiteralExp()); |
| case EXP.string_ : return visitString(e.isStringExp()); |
| case EXP.address : return visitAddr(e.isAddrExp()); |
| case EXP.tuple : return visitTuple(e.isTupleExp()); |
| case EXP.arrayLiteral : return visitArrayLiteral(e.isArrayLiteralExp()); |
| case EXP.assocArrayLiteral: return visitAssocArrayLiteral(e.isAssocArrayLiteralExp()); |
| case EXP.symbolOffset : return visitSymOff(e.isSymOffExp()); |
| case EXP.delegate_ : return visitDelegate(e.isDelegateExp()); |
| case EXP.function_ : return visitFunc(e.isFuncExp()); |
| case EXP.question : return visitCond(e.isCondExp()); |
| case EXP.comma : return visitComma(e.isCommaExp()); |
| case EXP.slice : return visitSlice(e.isSliceExp()); |
| } |
| } |
| |
| /**************************************** |
| * 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 EXP.arrayLiteral: return visitAle(e.isArrayLiteralExp()); |
| case EXP.assocArrayLiteral: return visitAar(e.isAssocArrayLiteralExp()); |
| case EXP.function_: return visitFun(e.isFuncExp()); |
| case EXP.question: return visitTer(e.isCondExp()); |
| 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; |
| |
| uinteger_t 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; |
| |
| uinteger_t 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); |
| |
| |
| eoff = eoff.optimize(WANTvalue); |
| if (eoff.op == EXP.int64 && eoff.toInteger() == 0) |
| { |
| } |
| else if (sc.setUnsafe(false, be.loc, "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 == EXP.arrayLiteral && e.type.ty == Tarray && (e.isArrayLiteralExp().elements.dim == 1)) |
| { |
| auto ale = e.isArrayLiteralExp(); |
| 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 == EXP.arrayLiteral && t.ty == Tarray && t.nextOf().ty == Tvoid && e.isArrayLiteralExp().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 EXP.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, EXP op, ref Expression pe1, ref Expression pe2) |
| { |
| //printf("typeMerge() %s op %s\n", e1.toChars(), e2.toChars()); |
| |
| Expression e1 = pe1; |
| Expression e2 = pe2; |
| |
| // ImportC: do array/function conversions |
| if (sc) |
| { |
| e1 = e1.arrayFuncConv(sc); |
| e2 = e2.arrayFuncConv(sc); |
| } |
| |
| 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 to the other |
| Type convert(ref Expression from, Type to) |
| { |
| from = from.castTo(sc, to); |
| return Lret(to); |
| } |
| |
| /// Converts both expression to a third type |
|