/* Compiler implementation of the D programming language * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved * written by Walter Bright * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt * https://github.com/D-Programming-Language/dmd/blob/master/src/cast.c */ #include "root/dsystem.h" // mem{set|cpy}() #include "root/rmem.h" #include "mars.h" #include "expression.h" #include "mtype.h" #include "utf.h" #include "declaration.h" #include "aggregate.h" #include "template.h" #include "scope.h" #include "id.h" #include "init.h" #include "tokens.h" FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL); bool isCommutative(TOK op); MOD MODmerge(MOD mod1, MOD mod2); void toAutoQualChars(const char **result, Type *t1, Type *t2); /* ==================== implicitCast ====================== */ /************************************** * Do an implicit cast. * Issue error if it can't be done. */ Expression *implicitCastTo(Expression *e, Scope *sc, Type *t) { class ImplicitCastTo : public Visitor { public: Type *t; Scope *sc; Expression *result; ImplicitCastTo(Scope *sc, Type *t) : t(t), sc(sc) { result = NULL; } void visit(Expression *e) { //printf("Expression::implicitCastTo(%s of type %s) => %s\n", e->toChars(), e->type->toChars(), t->toChars()); MATCH match = e->implicitConvTo(t); if (match) { if (match == MATCHconst && (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; } 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->semantic(loc, sc); //printf("type %s t %s\n", type->deco, t->deco); const char *ts[2]; toAutoQualChars(ts, e->type, t); e->error("cannot implicitly convert expression (%s) of type %s to %s", e->toChars(), ts[0], ts[1]); } } result = new ErrorExp(); } void visit(StringExp *e) { //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", e->toChars(), e->type->toChars(), t->toChars()); visit((Expression *)e); if (result->op == TOKstring) { // Retain polysemous nature if it started out that way ((StringExp *)result)->committed = e->committed; } } void visit(ErrorExp *e) { result = e; } 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) > MATCHnomatch) { result = fe; return; } visit((Expression *)e); } void visit(ArrayLiteralExp *e) { visit((Expression *)e); Type *tb = result->type->toBasetype(); if (tb->ty == Tarray && global.params.useTypeInfo && Type::dtypeinfo) semanticTypeInfo(sc, ((TypeDArray *)tb)->next); } void visit(SliceExp *e) { visit((Expression *)e); if (result->op != TOKslice) return; e = (SliceExp *)result; if (e->e1->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e->e1; Type *tb = t->toBasetype(); Type *tx; if (tb->ty == Tsarray) tx = tb->nextOf()->sarrayOf(ale->elements ? ale->elements->length : 0); else tx = tb->nextOf()->arrayOf(); e->e1 = ale->implicitCastTo(sc, tx); } } }; ImplicitCastTo v(sc, t); e->accept(&v); return v.result; } /******************************************* * Return MATCH level of implicitly converting e to type t. * Don't do the actual cast; don't change e. */ MATCH implicitConvTo(Expression *e, Type *t) { class ImplicitConvTo : public Visitor { public: Type *t; MATCH result; ImplicitConvTo(Type *t) : t(t) { result = MATCHnomatch; } void visit(Expression *e) { //static int nest; if (++nest == 10) halt(); 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 = MATCHexact; 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 != MATCHnomatch) { 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 = MATCHconvert; 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 *typeb = e->type->toBasetype(); Type *tb = t->toBasetype(); if (typeb->ty != Tpointer || tb->ty != Tpointer) return MATCHnomatch; 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 > MATCHconst) ? MATCHconst : m; } if (t2b->ty == Tpointer && t1b->isintegral() && t2b->equivalent(tb)) { // offset + ptr MATCH m = e->e2->implicitConvTo(t); return (m > MATCHconst) ? MATCHconst : m; } return MATCHnomatch; } void visit(AddExp *e) { visit((Expression *)e); if (result == MATCHnomatch) result = implicitConvToAddMin(e, t); } void visit(MinExp *e) { visit((Expression *)e); if (result == MATCHnomatch) result = implicitConvToAddMin(e, t); } void visit(IntegerExp *e) { MATCH m = e->type->implicitConvTo(t); if (m >= MATCHconst) { result = m; return; } TY ty = e->type->toBasetype()->ty; TY toty = t->toBasetype()->ty; TY oldty = ty; if (m == MATCHnomatch && t->ty == Tenum) return; if (t->ty == Tvector) { TypeVector *tv = (TypeVector *)t; 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 dinteger_t value = e->toInteger(); switch (toty) { case Tbool: if ((value & 1) != value) return; break; case Tint8: if (ty == Tuns64 && value & ~0x7FUL) return; else if ((signed char)value != (sinteger_t)value) return; break; case Tchar: if ((oldty == Twchar || oldty == Tdchar) && value > 0x7F) return; /* fall through */ case Tuns8: //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value); if ((unsigned char)value != value) return; break; case Tint16: if (ty == Tuns64 && value & ~0x7FFFUL) return; else if ((short)value != (sinteger_t)value) return; break; case Twchar: if (oldty == Tdchar && value > 0xD7FF && value < 0xE000) return; /* fall through */ case Tuns16: if ((unsigned short)value != value) return; break; case Tint32: if (ty == Tuns32) { } else if (ty == Tuns64 && value & ~0x7FFFFFFFUL) return; else if ((int)value != (sinteger_t)value) return; break; case Tuns32: if (ty == Tint32) { } else if ((unsigned)value != value) return; break; case Tdchar: if (value > 0x10FFFFUL) return; break; case Tfloat32: { volatile float f; if (e->type->isunsigned()) { f = (float)value; if (f != value) return; } else { f = (float)(sinteger_t)value; if (f != (sinteger_t)value) return; } break; } case Tfloat64: { volatile double f; if (e->type->isunsigned()) { f = (double)value; if (f != value) return; } else { f = (double)(sinteger_t)value; if (f != (sinteger_t)value) return; } break; } case Tfloat80: { volatile_longdouble f; if (e->type->isunsigned()) { f = ldouble(value); if ((dinteger_t)f != value) // isn't this a noop, because the compiler prefers ld return; } else { f = ldouble((sinteger_t)value); if ((sinteger_t)f != (sinteger_t)value) 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; } /* fall through */ default: visit((Expression *)e); return; } //printf("MATCHconvert\n"); result = MATCHconvert; } void visit(ErrorExp *) { // no match } void visit(NullExp *e) { if (e->type->equals(t)) { result = MATCHexact; 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 = MATCHconst; return; } visit((Expression *)e); } void visit(StructLiteralExp *e) { visit((Expression *)e); if (result != MATCHnomatch) return; if (e->type->ty == t->ty && e->type->ty == Tstruct && ((TypeStruct *)e->type)->sym == ((TypeStruct *)t)->sym) { result = MATCHconst; for (size_t i = 0; i < e->elements->length; i++) { Expression *el = (*e->elements)[i]; if (!el) continue; Type *te = el->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; } } } void visit(StringExp *e) { if (!e->committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) return; if (e->type->ty == Tsarray || e->type->ty == Tarray || e->type->ty == Tpointer) { TY tyn = e->type->nextOf()->ty; if (tyn == Tchar || tyn == Twchar || tyn == Tdchar) { switch (t->ty) { case Tsarray: if (e->type->ty == Tsarray) { TY tynto = t->nextOf()->ty; if (tynto == tyn) { if (((TypeSArray *)e->type)->dim->toInteger() == ((TypeSArray *)t)->dim->toInteger()) { result = MATCHexact; } return; } if (tynto == Tchar || tynto == Twchar || tynto == Tdchar) { if (e->committed && tynto != tyn) return; size_t fromlen = e->numberOfCodeUnits(tynto); size_t tolen = (size_t)((TypeSArray *)t)->dim->toInteger(); if (tolen < fromlen) return; if (tolen != fromlen) { // implicit length extending result = MATCHconvert; return; } } if (!e->committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) { result = MATCHexact; return; } } else if (e->type->ty == Tarray) { TY tynto = t->nextOf()->ty; if (tynto == Tchar || tynto == Twchar || tynto == Tdchar) { if (e->committed && tynto != tyn) return; size_t fromlen = e->numberOfCodeUnits(tynto); size_t tolen = (size_t)((TypeSArray *)t)->dim->toInteger(); if (tolen < fromlen) return; if (tolen != fromlen) { // implicit length extending result = MATCHconvert; return; } } if (tynto == tyn) { result = MATCHexact; return; } if (!e->committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) { result = MATCHexact; return; } } /* fall through */ case Tarray: case Tpointer: Type *tn = t->nextOf(); MATCH m = MATCHexact; if (e->type->nextOf()->mod != tn->mod) { if (!tn->isConst()) return; m = MATCHconst; } if (!e->committed) { switch (tn->ty) { case Tchar: if (e->postfix == 'w' || e->postfix == 'd') m = MATCHconvert; result = m; return; case Twchar: if (e->postfix != 'w') m = MATCHconvert; result = m; return; case Tdchar: if (e->postfix != 'd') m = MATCHconvert; result = m; return; } } break; } } } visit((Expression *)e); } void visit(ArrayLiteralExp *e) { Type *typeb = e->type->toBasetype(); Type *tb = t->toBasetype(); if ((tb->ty == Tarray || tb->ty == Tsarray) && (typeb->ty == Tarray || typeb->ty == Tsarray)) { result = MATCHexact; Type *typen = typeb->nextOf()->toBasetype(); if (tb->ty == Tsarray) { TypeSArray *tsa = (TypeSArray *)tb; if (e->elements->length != tsa->dim->toInteger()) result = MATCHnomatch; } Type *telement = tb->nextOf(); if (!e->elements->length) { 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->length; i++) { Expression *el = (*e->elements)[i]; if (result == MATCHnomatch) 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 = MATCHexact; // Convert array literal to vector type TypeVector *tv = (TypeVector *)tb; TypeSArray *tbase = (TypeSArray *)tv->basetype; assert(tbase->ty == Tsarray); const size_t edim = e->elements->length; const size_t tbasedim = tbase->dim->toInteger(); if (edim > tbasedim) { result = MATCHnomatch; 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 } for (size_t i = 0; i < edim; i++) { Expression *el = (*e->elements)[i]; MATCH m = el->implicitConvTo(telement); if (m < result) result = m; // remember worst match if (result == MATCHnomatch) break; // no need to check for worse } return; } visit((Expression *)e); } void visit(AssocArrayLiteralExp *e) { Type *typeb = e->type->toBasetype(); Type *tb = t->toBasetype(); if (tb->ty == Taarray && typeb->ty == Taarray) { result = MATCHexact; for (size_t i = 0; i < e->keys->length; i++) { Expression *el = (*e->keys)[i]; MATCH m = el->implicitConvTo(((TypeAArray *)tb)->index); if (m < result) result = m; // remember worst match if (result == MATCHnomatch) break; // no need to check for worse el = (*e->values)[i]; m = el->implicitConvTo(tb->nextOf()); if (m < result) result = m; // remember worst match if (result == MATCHnomatch) break; // no need to check for worse } return; } else visit((Expression *)e); } void visit(CallExp *e) { visit((Expression *)e); if (result != MATCHnomatch) return; /* Allow the result of strongly pure functions to * convert to immutable */ if (e->f && e->f->isolateReturn()) { result = e->type->immutableOf()->implicitConvTo(t); if (result > MATCHconst) // Match level is MATCHconst at best. result = MATCHconst; 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 */ Type *tx = e->f ? e->f->type : e->e1->type; tx = tx->toBasetype(); if (tx->ty != Tfunction) return; TypeFunction *tf = (TypeFunction *)tx; if (tf->purity == PUREimpure) return; if (e->f && e->f->isNested()) return; /* See if fail only because of mod bits. * * Bugzilla 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) < MATCHconst && e->type->addMod(MODshared)->implicitConvTo(t) < MATCHconst && e->type->implicitConvTo(t->addMod(MODshared)) < MATCHconst) { 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 { Type *ti = getIndirection(t); if (ti) mod = ti->mod; } if (mod & MODwild) 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 (e->e1->op == TOKdotvar) { /* Treat 'this' as just another function argument */ DotVarExp *dve = (DotVarExp *)e->e1; Type *targ = dve->e1->type; if (targ->constConv(targ->castMod(mod)) == MATCHnomatch) return; } for (size_t i = j; i < e->arguments->length; ++i) { Expression *earg = (*e->arguments)[i]; Type *targ = earg->type->toBasetype(); if (i - j < nparams) { Parameter *fparam = tf->parameterList[i - j]; if (fparam->storageClass & STClazy) return; // not sure what to do with this Type *tparam = fparam->type; if (!tparam) continue; if (fparam->storageClass & (STCout | STCref)) { if (targ->constConv(tparam->castMod(mod)) == MATCHnomatch) return; continue; } } if (implicitMod(earg, targ, mod) == MATCHnomatch) return; } /* Success */ result = MATCHconst; } void visit(AddrExp *e) { result = e->type->implicitConvTo(t); //printf("\tresult = %d\n", result); if (result != MATCHnomatch) return; // Look for pointers to functions where the functions are overloaded. t = t->toBasetype(); if (e->e1->op == TOKoverloadset && (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) { OverExp *eo = (OverExp *)e->e1; FuncDeclaration *f = NULL; for (size_t i = 0; i < eo->vars->a.length; i++) { Dsymbol *s = eo->vars->a[i]; FuncDeclaration *f2 = s->isFuncDeclaration(); assert(f2); if (f2->overloadExactMatch(t->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 = MATCHexact; } } } if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && t->ty == Tpointer && t->nextOf()->ty == Tfunction && e->e1->op == TOKvar) { /* I don't think this can ever happen - * it should have been * converted to a SymOffExp. */ assert(0); } //printf("\tresult = %d\n", result); } void visit(SymOffExp *e) { result = e->type->implicitConvTo(t); //printf("\tresult = %d\n", result); if (result != MATCHnomatch) return; // Look for pointers to functions where the functions are overloaded. t = t->toBasetype(); if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) { if (FuncDeclaration *f = e->var->isFuncDeclaration()) { f = f->overloadExactMatch(t->nextOf()); if (f) { if ((t->ty == Tdelegate && (f->needThis() || f->isNested())) || (t->ty == Tpointer && !(f->needThis() || f->isNested()))) { result = MATCHexact; } } } } //printf("\tresult = %d\n", result); } void visit(DelegateExp *e) { result = e->type->implicitConvTo(t); if (result != MATCHnomatch) return; // Look for pointers to functions where the functions are overloaded. t = t->toBasetype(); if (e->type->ty == Tdelegate && t->ty == Tdelegate) { if (e->func && e->func->overloadExactMatch(t->nextOf())) result = MATCHexact; } } 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 > MATCHnomatch) { result = m; return; } visit((Expression *)e); } void visit(AndExp *e) { visit((Expression *)e); if (result != MATCHnomatch) return; MATCH m1 = e->e1->implicitConvTo(t); MATCH m2 = e->e2->implicitConvTo(t); // Pick the worst match result = (m1 < m2) ? m1 : m2; } void visit(OrExp *e) { visit((Expression *)e); if (result != MATCHnomatch) return; MATCH m1 = e->e1->implicitConvTo(t); MATCH m2 = e->e2->implicitConvTo(t); // Pick the worst match result = (m1 < m2) ? m1 : m2; } void visit(XorExp *e) { visit((Expression *)e); if (result != MATCHnomatch) return; MATCH m1 = e->e1->implicitConvTo(t); MATCH m2 = e->e2->implicitConvTo(t); // Pick the worst match result = (m1 < m2) ? m1 : m2; } 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; } void visit(CommaExp *e) { e->e2->accept(this); } void visit(CastExp *e) { result = e->type->implicitConvTo(t); if (result != MATCHnomatch) return; if (t->isintegral() && e->e1->type->isintegral() && e->e1->implicitConvTo(t) != MATCHnomatch) result = MATCHconvert; else visit((Expression *)e); } void visit(NewExp *e) { visit((Expression *)e); if (result != MATCHnomatch) 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' and 'allocator' need to be pure. */ /* See if fail only because of mod bits */ if (e->type->immutableOf()->implicitConvTo(t->immutableOf()) == MATCHnomatch) 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; if (mod & MODwild) 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)) == MATCHnomatch) return; } /* Check call to 'allocator', then 'member' */ FuncDeclaration *fd = e->allocator; for (int count = 0; count < 2; ++count, (fd = e->member)) { if (!fd) continue; if (fd->errors || fd->type->ty != Tfunction) return; // error TypeFunction *tf = (TypeFunction *)fd->type; if (tf->purity == PUREimpure) return; // impure if (fd == e->member) { if (e->type->immutableOf()->implicitConvTo(t) < MATCHconst && e->type->addMod(MODshared)->implicitConvTo(t) < MATCHconst && e->type->implicitConvTo(t->addMod(MODshared)) < MATCHconst) { return; } // Allow a conversion to immutable type, or // conversions of mutable types between thread-local and shared. } Expressions *args = (fd == e->allocator) ? e->newargs : e->arguments; size_t nparams = tf->parameterList.length(); size_t j = tf->isDstyleVariadic(); // if TypeInfoArray was prepended for (size_t i = j; i < e->arguments->length; ++i) { Expression *earg = (*args)[i]; Type *targ = earg->type->toBasetype(); if (i - j < nparams) { Parameter *fparam = tf->parameterList[i - j]; if (fparam->storageClass & STClazy) return; // not sure what to do with this Type *tparam = fparam->type; if (!tparam) continue; if (fparam->storageClass & (STCout | STCref)) { if (targ->constConv(tparam->castMod(mod)) == MATCHnomatch) return; continue; } } if (implicitMod(earg, targ, mod) == MATCHnomatch) 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->length; ++i) { Expression *earg = (*e->arguments)[i]; if (!earg) // Bugzilla 14853: if it's on overlapped field continue; Type *targ = earg->type->toBasetype(); if (implicitMod(earg, targ, mod) == MATCHnomatch) return; } } /* Consider the .init expression as an argument */ Type *ntb = e->newtype->toBasetype(); if (ntb->ty == Tarray) ntb = ntb->nextOf()->toBasetype(); if (ntb->ty == Tstruct) { // Don't allow nested structs - uplevel reference may not be convertible StructDeclaration *sd = ((TypeStruct *)ntb)->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 (ntb->ty == Tclass) { /* With new() must look at the class instance initializer. */ ClassDeclaration *cd = ((TypeClass *)ntb)->sym; cd->size(e->loc); // resolve any forward references if (cd->isNested()) return; // uplevel reference may not be convertible assert(!cd->isInterfaceDeclaration()); struct ClassCheck { static bool convertible(Loc loc, ClassDeclaration *cd, MOD mod) { for (size_t i = 0; i < cd->fields.length; i++) { VarDeclaration *v = cd->fields[i]; Initializer *init = v->_init; if (init) { if (init->isVoidInitializer()) ; else if (ExpInitializer *ei = init->isExpInitializer()) { Type *tb = v->type->toBasetype(); if (implicitMod(ei->exp, tb, mod) == MATCHnomatch) return false; } else { /* Enhancement: handle StructInitializer and ArrayInitializer */ return false; } } else if (!v->type->isZeroInit(loc)) return false; } return cd->baseClass ? convertible(loc, cd->baseClass, mod) : true; } }; if (!ClassCheck::convertible(e->loc, cd, mod)) return; } } else { Expression *earg = e->newtype->defaultInitLiteral(e->loc); Type *targ = e->newtype->toBasetype(); if (implicitMod(earg, targ, mod) == MATCHnomatch) return; } /* Success */ result = MATCHconst; } void visit(SliceExp *e) { //printf("SliceExp::implicitConvTo e = %s, type = %s\n", e->toChars(), e->type->toChars()); visit((Expression *)e); if (result != MATCHnomatch) return; Type *tb = t->toBasetype(); Type *typeb = e->type->toBasetype(); if (tb->ty == Tsarray && typeb->ty == Tarray) { typeb = toStaticArrayType(e); if (typeb) result = typeb->implicitConvTo(t); 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 > MATCHconst) // Match level is MATCHconst at best. result = MATCHconst; } } // Enhancement 10724 if (tb->ty == Tpointer && e->e1->op == TOKstring) e->e1->accept(this); } }; ImplicitConvTo v(t); e->accept(&v); return v.result; } 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 = (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; } /* ==================== castTo ====================== */ /************************************** * Do an explicit cast. * Assume that the 'this' expression does not have any indirections. */ Expression *castTo(Expression *e, Scope *sc, Type *t) { class CastTo : public Visitor { public: Type *t; Scope *sc; Expression *result; CastTo(Scope *sc, Type *t) : t(t), sc(sc) { result = NULL; } void visit(Expression *e) { //printf("Expression::castTo(this=%s, t=%s)\n", e->toChars(), t->toChars()); if (e->type->equals(t)) { result = e; return; } if (e->op == TOKvar) { VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); if (v && v->storage_class & STCmanifest) { result = e->ctfeInterpret(); 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); 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; } /* Forward the cast to our alias this member, rewrite to: * cast(to)e1.aliasthis */ result = resolveAliasThis(sc, e); result = result->castTo(sc, t); return; } } 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 = (TypeVector *)tob; result = new CastExp(e->loc, e, tv->elementType()); result = new VectorExp(e->loc, result, tob); result = expressionSemantic(result, 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) == MATCHconst && 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 (t1b->size(e->loc) == tob->size(e->loc)) goto Lok; e->error("cannot cast expression %s of type %s to %s because of different sizes", e->toChars(), e->type->toChars(), t->toChars()); result = new ErrorExp(); 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[]; d_uns64 fsize = t1b->nextOf()->size(); d_uns64 tsize = tob->nextOf()->size(); if (fsize != tsize) { dinteger_t dim = ((TypeSArray *)t1b)->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 = new ErrorExp(); 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; // Bugzilla 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: e->error("cannot cast expression %s of type %s to %s", e->toChars(), e->type->toChars(), t->toChars()); result = new ErrorExp(); return; } Lok: result = new CastExp(e->loc, e, t); result->type = t; // Don't call semantic() //printf("Returning: %s\n", result->toChars()); } void visit(ErrorExp *e) { result = e; } 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((Expression *)e); return; } result = e; } void visit(ComplexExp *e) { if (!e->type->equals(t)) { if (e->type->iscomplex() && t->iscomplex()) { result = e->copy(); result->type = t; } else visit((Expression *)e); return; } result = e; } void visit(NullExp *e) { //printf("NullExp::castTo(t = %s) %s\n", t->toChars(), toChars()); visit((Expression *)e); if (result->op == TOKnull) { NullExp *ex = (NullExp *)result; ex->committed = 1; return; } } void visit(StructLiteralExp *e) { visit((Expression *)e); if (result->op == TOKstructliteral) ((StructLiteralExp *)result)->stype = t; // commit type } 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 = new ErrorExp(); return; } StringExp *se = e; if (!e->committed) { se = (StringExp *)e->copy(); se->committed = 1; copied = 1; } if (e->type->equals(t)) { result = se; return; } Type *tb = t->toBasetype(); //printf("\ttype = %s\n", e->type->toChars()); if (tb->ty == Tdelegate && e->type->toBasetype()->ty != Tdelegate) { visit((Expression *)e); return; } Type *typeb = e->type->toBasetype(); if (typeb->equals(tb)) { if (!copied) { se = (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] */ if (e->committed && tb->ty == Tsarray && typeb->ty == Tarray) { se = (StringExp *)e->copy(); d_uns64 szx = tb->nextOf()->size(); assert(szx <= 255); se->sz = (unsigned char)szx; se->len = (size_t)((TypeSArray *)tb)->dim->toInteger(); se->committed = 1; se->type = t; /* Assure space for terminating 0 */ if ((se->len + 1) * se->sz > (e->len + 1) * e->sz) { void *s = (void *)mem.xmalloc((se->len + 1) * se->sz); memcpy(s, se->string, se->len * se->sz); memset((char *)s + se->len * se->sz, 0, se->sz); se->string = s; } result = se; return; } if (tb->ty != Tsarray && tb->ty != Tarray && tb->ty != Tpointer) { if (!copied) { se = (StringExp *)e->copy(); copied = 1; } goto Lcast; } if (typeb->ty != Tsarray && typeb->ty != Tarray && typeb->ty != Tpointer) { if (!copied) { se = (StringExp *)e->copy(); copied = 1; } goto Lcast; } if (typeb->nextOf()->size() == tb->nextOf()->size()) { if (!copied) { se = (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; #define X(tf,tt) ((int)(tf) * 256 + (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;) { unsigned c; const char *p = utf_decodeChar((utf8_t *)se->string, e->len, &u, &c); if (p) e->error("%s", p); 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;) { unsigned c; const char *p = utf_decodeChar((utf8_t *)se->string, e->len, &u, &c); if (p) e->error("%s", p); buffer.write4(c); newlen++; } buffer.write4(0); goto L1; case X(Twchar,Tchar): for (size_t u = 0; u < e->len;) { unsigned c; const char *p = utf_decodeWchar((unsigned short *)se->string, e->len, &u, &c); if (p) e->error("%s", p); else buffer.writeUTF8(c); } newlen = buffer.length(); buffer.writeUTF8(0); goto L1; case X(Twchar,Tdchar): for (size_t u = 0; u < e->len;) { unsigned c; const char *p = utf_decodeWchar((unsigned short *)se->string, e->len, &u, &c); if (p) e->error("%s", p); buffer.write4(c); newlen++; } buffer.write4(0); goto L1; case X(Tdchar,Tchar): for (size_t u = 0; u < e->len; u++) { unsigned c = ((unsigned *)se->string)[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++) { unsigned c = ((unsigned *)se->string)[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 = (StringExp *)e->copy(); copied = 1; } se->string = buffer.extractData(); se->len = newlen; { d_uns64 szx = tb->nextOf()->size(); assert(szx <= 255); se->sz = (unsigned char)szx; } break; default: assert(typeb->nextOf()->size() != tb->nextOf()->size()); goto Lcast; } } #undef X L2: assert(copied); // See if need to truncate or extend the literal if (tb->ty == Tsarray) { size_t dim2 = (size_t)((TypeSArray *)tb)->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 size_t newsz = se->sz; size_t d = (dim2 < se->len) ? dim2 : se->len; void *s = (void *)mem.xmalloc((dim2 + 1) * newsz); memcpy(s, se->string, d * newsz); // Extend with 0, add terminating 0 memset((char *)s + d * newsz, 0, (dim2 + 1 - d) * newsz); se->string = s; se->len = dim2; } } 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 } void visit(AddrExp *e) { Type *tb; result = e; tb = t->toBasetype(); e->type = e->type->toBasetype(); if (!tb->equals(e->type)) { // Look for pointers to functions where the functions are overloaded. if (e->e1->op == TOKoverloadset && (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) { OverExp *eo = (OverExp *)e->e1; FuncDeclaration *f = NULL; for (size_t i = 0; i < eo->vars->a.length; i++) { Dsymbol *s = eo->vars->a[i]; FuncDeclaration *f2 = s->isFuncDeclaration(); assert(f2); if (f2->overloadExactMatch(t->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++; SymOffExp *se = new SymOffExp(e->loc, f, 0, false); expressionSemantic(se, sc); // Let SymOffExp::castTo() do the heavy lifting visit(se); return; } } if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && tb->ty == Tpointer && tb->nextOf()->ty == Tfunction && e->e1->op == TOKvar) { VarExp *ve = (VarExp *)e->e1; FuncDeclaration *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 (FuncDeclaration *f = isFuncAddress(e)) { if (f->checkForwardRef(e->loc)) { result = new ErrorExp(); return; } } visit((Expression *)e); } result->type = t; } void visit(TupleExp *e) { if (e->type->equals(t)) { result = e; return; } TupleExp *te = (TupleExp *)e->copy(); te->e0 = e->e0 ? e->e0->copy() : NULL; te->exps = (Expressions *)e->exps->copy(); for (size_t i = 0; i < te->exps->length; 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[] */ } void visit(ArrayLiteralExp *e) { if (e->type == t) { result = e; return; } ArrayLiteralExp *ae = e; Type *typeb = e->type->toBasetype(); Type *tb = t->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 (tb->ty == Tsarray) { TypeSArray *tsa = (TypeSArray *)tb; if (e->elements->length != tsa->dim->toInteger()) goto L1; } ae = (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->length; 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 = (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 = (TypeVector *)tb; TypeSArray *tbase = (TypeSArray *)tv->basetype; assert(tbase->ty == Tsarray); const size_t edim = e->elements->length; const size_t tbasedim = tbase->dim->toInteger(); if (edim > tbasedim) goto L1; ae = (ArrayLiteralExp *)e->copy(); ae->type = tbase; // Bugzilla 12642 ae->elements = e->elements->copy(); Type *telement = tv->elementType(); for (size_t i = 0; i < edim; i++) { 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(tbasedim); for (size_t i = edim; i < tbasedim; i++) { 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 = expressionSemantic(ev, sc); result = ev; return; } L1: visit((Expression *)ae); } void visit(AssocArrayLiteralExp *e) { if (e->type == t) { result = e; return; } Type *typeb = e->type->toBasetype(); Type *tb = t->toBasetype(); if (tb->ty == Taarray && typeb->ty == Taarray && tb->nextOf()->toBasetype()->ty != Tvoid) { AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e->copy(); ae->keys = e->keys->copy(); ae->values = e->values->copy(); assert(e->keys->length == e->values->length); for (size_t i = 0; i < e->keys->length; i++) { Expression *ex = (*e->values)[i]; ex = ex->castTo(sc, tb->nextOf()); (*ae->values)[i] = ex; ex = (*e->keys)[i]; ex = ex->castTo(sc, ((TypeAArray *)tb)->index); (*ae->keys)[i] = ex; } ae->type = t; result = ae; return; } visit((Expression *)e); } void visit(SymOffExp *e) { 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; ((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 = expressionSemantic(result, sc); } else if (f->isNested()) { result = new DelegateExp(e->loc, new IntegerExp(0), f, false); result = expressionSemantic(result, sc); } else if (f->needThis()) { e->error("no `this` to create delegate for %s", f->toChars()); result = new ErrorExp(); return; } else { e->error("cannot cast from function pointer to delegate"); result = new ErrorExp(); return; } } else { result = new SymOffExp(e->loc, f, 0, false); result->type = t; } f->tookAddressOf++; return; } } if (FuncDeclaration *f = isFuncAddress(e)) { if (f->checkForwardRef(e->loc)) { result = new ErrorExp(); return; } } visit((Expression *)e); } void visit(DelegateExp *e) { static 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) { // Look for delegates to functions where the functions are overloaded. if (typeb->ty == Tdelegate && tb->ty == Tdelegate) { if (e->func) { FuncDeclaration *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); result->type = t; return; } if (e->func->tintro) e->error("%s", msg); } } if (FuncDeclaration *f = isFuncAddress(e)) { if (f->checkForwardRef(e->loc)) { result = new ErrorExp(); return; } } visit((Expression *)e); } else { 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; } } 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) > MATCHnomatch) { result = fe; return; } visit((Expression *)e); } 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; } 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; } } void visit(SliceExp *e) { //printf("SliceExp::castTo e = %s, type = %s, t = %s\n", e->toChars(), e->type->toChars(), t->toChars()); Type *typeb = e->type->toBasetype(); Type *tb = t->toBasetype(); if (e->type->equals(t) || typeb->ty != Tarray || (tb->ty != Tarray && tb->ty != Tsarray)) { visit((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((Expression *)e); } return; } // Handle the cast from Tarray to Tsarray with CT-known slicing TypeSArray *tsa = (TypeSArray *)toStaticArrayType(e); 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(((TypeSArray *)tb)->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(((TypeSArray *)t1b)->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) > MATCHnomatch) { Expression *e1x = e->e1->implicitCastTo(sc, t1b); assert(e1x->op != TOKerror); e = (SliceExp *)e->copy(); e->e1 = e1x; e->type = t; result = e; return; } } e->error("cannot cast expression %s of type %s to %s", e->toChars(), tsa ? tsa->toChars() : e->type->toChars(), t->toChars()); result = new ErrorExp(); } }; CastTo v(sc, t); e->accept(&v); return v.result; } /* ==================== inferType ====================== */ /**************************************** * 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) { class InferType : public Visitor { public: Type *t; int flag; Expression *result; InferType(Type *t, int flag) : t(t), flag(flag) { result = NULL; } void visit(Expression *e) { result = e; } void visit(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->length; i++) { Expression *e = (*ale->elements)[i]; if (e) { e = inferType(e, tn, flag); (*ale->elements)[i] = e; } } } result = ale; } void visit(AssocArrayLiteralExp *aale) { Type *tb = t->toBasetype(); if (tb->ty == Taarray) { TypeAArray *taa = (TypeAArray *)tb; Type *ti = taa->index; Type *tv = taa->nextOf(); for (size_t i = 0; i < aale->keys->length; i++) { Expression *e = (*aale->keys)[i]; if (e) { e = inferType(e, ti, flag); (*aale->keys)[i] = e; } } for (size_t i = 0; i < aale->values->length; i++) { Expression *e = (*aale->values)[i]; if (e) { e = inferType(e, tv, flag); (*aale->values)[i] = e; } } } result = aale; } void visit(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; } result = fe; } void visit(CondExp *ce) { Type *tb = t->toBasetype(); ce->e1 = inferType(ce->e1, tb, flag); ce->e2 = inferType(ce->e2, tb, flag); result = ce; } }; if (!t) return e; InferType v(t, flag); e->accept(&v); return v.result; } /* ==================== ====================== */ /**************************************** * 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(), 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(), 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 == TOKint64 && eoff->toInteger() == 0) ; else if (sc->func->setUnsafe()) { be->error("pointer arithmetic not allowed in @safe functions"); return new ErrorExp(); } } 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. */ bool isVoidArrayLiteral(Expression *e, Type *other) { while (e->op == TOKarrayliteral && e->type->ty == Tarray && (((ArrayLiteralExp *)e)->elements->length == 1)) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e; e = ale->getElement(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 == TOKarrayliteral && t->ty == Tarray && t->nextOf()->ty == Tvoid && ((ArrayLiteralExp *)e)->elements->length == 0); } // used by deduceType() Type *rawTypeMerge(Type *t1, Type *t2) { if (t1->equals(t2)) return t1; if (t1->equivalent(t2)) return t1->castMod(MODmerge(t1->mod, t2->mod)); Type *t1b = t1->toBasetype(); Type *t2b = t2->toBasetype(); if (t1b->equals(t2b)) return t1b; if (t1b->equivalent(t2b)) return t1b->castMod(MODmerge(t1b->mod, t2b->mod)); TY ty = (TY)impcnvResult[t1b->ty][t2b->ty]; if (ty != Terror) return Type::basic[ty]; return NULL; } /************************************** * Combine types. * Output: * *pt merged type, if *pt is not NULL * *pe1 rewritten e1 * *pe2 rewritten e2 * Returns: * true success * false failed */ bool typeMerge(Scope *sc, TOK op, Type **pt, Expression **pe1, Expression **pe2) { //printf("typeMerge() %s op %s\n", (*pe1)->toChars(), (*pe2)->toChars()); MATCH m; Expression *e1 = *pe1; Expression *e2 = *pe2; Type *t1b = e1->type->toBasetype(); Type *t2b = e2->type->toBasetype(); if (op != TOKquestion || (t1b->ty != t2b->ty && (t1b->isTypeBasic() && t2b->isTypeBasic()))) { e1 = integralPromotions(e1, sc); e2 = integralPromotions(e2, sc); } 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) printf("\tt1 = %s\n", t1->toChars()); //if (t2) printf("\tt2 = %s\n", t2->toChars()); assert(t2); if (t1->mod != t2->mod && t1->ty == Tenum && t2->ty == Tenum && ((TypeEnum *)t1)->sym == ((TypeEnum *)t2)->sym) { unsigned char mod = MODmerge(t1->mod, t2->mod); t1 = t1->castMod(mod); t2 = t2->castMod(mod); } Lagain: t1b = t1->toBasetype(); t2b = t2->toBasetype(); TY ty = (TY)impcnvResult[t1b->ty][t2b->ty]; if (ty != Terror) { TY ty1 = (TY)impcnvType1[t1b->ty][t2b->ty]; TY ty2 = (TY)impcnvType2[t1b->ty][t2b->ty]; if (t1b->ty == ty1) // if no promotions { if (t1->equals(t2)) { t = t1; goto Lret; } if (t1b->equals(t2b)) { t = t1b; goto Lret; } } t = Type::basic[ty]; t1 = Type::basic[ty1]; t2 = Type::basic[ty2]; e1 = e1->castTo(sc, t1); e2 = e2->castTo(sc, t2); //printf("after typeCombine():\n"); //print(); //printf("ty = %d, ty1 = %d, ty2 = %d\n", ty, ty1, ty2); goto Lret; } t1 = t1b; t2 = t2b; if (t1->ty == Ttuple || t2->ty == Ttuple) goto Lincompatible; if (t1->equals(t2)) { // merging can not result in new enum type if (t->ty == Tenum) t = t1b; } else 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)) ; else if (t1n->ty == Tvoid) // pointers to void are always compatible t = t2; else if (t2n->ty == Tvoid) ; else if (t1->implicitConvTo(t2)) { goto Lt2; } else if (t2->implicitConvTo(t1)) { goto Lt1; } else if (t1n->ty == Tfunction && t2n->ty == Tfunction) { TypeFunction *tf1 = (TypeFunction *)t1n; TypeFunction *tf2 = (TypeFunction *)t2n; tf1->purityLevel(); tf2->purityLevel(); TypeFunction *d = (TypeFunction *)tf1->syntaxCopy(); if (tf1->purity != tf2->purity) d->purity = PUREimpure; assert(d->purity != PUREfwdref); 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 <= TRUSTsystem || tf2->trust <= TRUSTsystem) d->trust = TRUSTsystem; else d->trust = TRUSTtrusted; Type *tx = NULL; if (t1->ty == Tdelegate) { tx = new TypeDelegate(d); } else tx = d->pointerTo(); tx = typeSemantic(tx, e1->loc, sc); if (t1->implicitConvTo(tx) && t2->implicitConvTo(tx)) { t = tx; e1 = e1->castTo(sc, t); e2 = e2->castTo(sc, t); goto Lret; } goto Lincompatible; } else if (t1n->mod != t2n->mod) { if (!t1n->isImmutable() && !t2n->isImmutable() && t1n->isShared() != t2n->isShared()) goto Lincompatible; unsigned char mod = MODmerge(t1n->mod, t2n->mod); t1 = t1n->castMod(mod)->pointerTo(); t2 = t2n->castMod(mod)->pointerTo(); t = t1; goto Lagain; } else 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); } else if (cd2->isBaseOf(cd1, &offset)) { t = t2; if (offset) e1 = e1->castTo(sc, t); } else goto Lincompatible; } else { t1 = t1n->constOf()->pointerTo(); t2 = t2n->constOf()->pointerTo(); if (t1->implicitConvTo(t2)) { goto Lt2; } else if (t2->implicitConvTo(t1)) { goto Lt1; } goto Lincompatible; } } else if ((t1->ty == Tsarray || t1->ty == Tarray) && ((e2->op == TOKnull && t2->ty == Tpointer && t2->nextOf()->ty == Tvoid) || (e2->op == TOKarrayliteral && t2->ty == Tsarray && t2->nextOf()->ty == Tvoid && ((TypeSArray *)t2)->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[] */ goto Lx1; } else if ((t2->ty == Tsarray || t2->ty == Tarray) && ((e1->op == TOKnull && t1->ty == Tpointer && t1->nextOf()->ty == Tvoid) || (e1->op == TOKarrayliteral && t1->ty == Tsarray && t1->nextOf()->ty == Tvoid && ((TypeSArray *)t1)->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[] */ goto Lx2; } else if ((t1->ty == Tsarray || t1->ty == Tarray) && (m = t1->implicitConvTo(t2)) != MATCHnomatch) { // Bugzilla 7285: Tsarray op [x, y, ...] should to be Tsarray // Bugzilla 14737: Tsarray ~ [x, y, ...] should to be Tarray if (t1->ty == Tsarray && e2->op == TOKarrayliteral && op != TOKcat) goto Lt1; if (m == MATCHconst && (op == TOKaddass || op == TOKminass || op == TOKmulass || op == TOKdivass || op == TOKmodass || op == TOKpowass || op == TOKandass || op == TOKorass || op == TOKxorass) ) { // Don't make the lvalue const t = t2; goto Lret; } goto Lt2; } else if ((t2->ty == Tsarray || t2->ty == Tarray) && t2->implicitConvTo(t1)) { // Bugzilla 7285 & 14737 if (t2->ty == Tsarray && e1->op == TOKarrayliteral && op != TOKcat) goto Lt2; goto Lt1; } else 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 invariant, then retry * with both of them as const */ Type *t1n = t1->nextOf(); Type *t2n = t2->nextOf(); unsigned char mod; if (e1->op == TOKnull && e2->op != TOKnull) mod = t2n->mod; else if (e1->op != TOKnull && e2->op == TOKnull) mod = t1n->mod; else if (!t1n->isImmutable() && !t2n->isImmutable() && t1n->isShared() != t2n->isShared()) goto Lincompatible; 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; } else if (t1->ty == Tclass && t2->ty == Tclass) { if (t1->mod != t2->mod) { unsigned char mod; if (e1->op == TOKnull && e2->op != TOKnull) mod = t2->mod; else if (e1->op != TOKnull && e2->op == TOKnull) mod = t1->mod; else if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared()) goto Lincompatible; else mod = MODmerge(t1->mod, t2->mod); t1 = t1->castMod(mod); t2 = t2->castMod(mod); t = t1; goto Lagain; } goto Lcc; } else 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 = MATCHnomatch; else if (t2->ty == Tpointer) i2 = MATCHnomatch; } if (i2) { e2 = e2->castTo(sc, t2); goto Lt2; } else if (i1) { e1 = e1->castTo(sc, t1); goto Lt1; } else if (t1->ty == Tclass && t2->ty == Tclass) { TypeClass *tc1 = (TypeClass *)t1; TypeClass *tc2 = (TypeClass *)t2; /* 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 goto Lincompatible; } else if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis) { if (att1 && e1->type == att1) goto Lincompatible; if (!att1 && e1->type->checkAliasThisRec()) att1 = e1->type; //printf("att tmerge(c || c) e1 = %s\n", e1->type->toChars()); e1 = resolveAliasThis(sc, e1); t1 = e1->type; continue; } else if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis) { if (att2 && e2->type == att2) goto Lincompatible; if (!att2 && e2->type->checkAliasThisRec()) att2 = e2->type; //printf("att tmerge(c || c) e2 = %s\n", e2->type->toChars()); e2 = resolveAliasThis(sc, e2); t2 = e2->type; continue; } else goto Lincompatible; } } else if (t1->ty == Tstruct && t2->ty == Tstruct) { if (t1->mod != t2->mod) { if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared()) goto Lincompatible; unsigned char mod = MODmerge(t1->mod, t2->mod); t1 = t1->castMod(mod); t2 = t2->castMod(mod); t = t1; goto Lagain; } TypeStruct *ts1 = (TypeStruct *)t1; TypeStruct *ts2 = (TypeStruct *)t2; if (ts1->sym != ts2->sym) { if (!ts1->sym->aliasthis && !ts2->sym->aliasthis) goto Lincompatible; MATCH i1 = MATCHnomatch; MATCH i2 = MATCHnomatch; Expression *e1b = NULL; Expression *e2b = NULL; if (ts2->sym->aliasthis) { if (att2 && e2->type == att2) goto Lincompatible; if (!att2 && e2->type->checkAliasThisRec()) att2 = e2->type; //printf("att tmerge(s && s) e2 = %s\n", e2->type->toChars()); e2b = resolveAliasThis(sc, e2); i1 = e2b->implicitConvTo(t1); } if (ts1->sym->aliasthis) { if (att1 && e1->type == att1) goto Lincompatible; if (!att1 && e1->type->checkAliasThisRec()) att1 = e1->type; //printf("att tmerge(s && s) e1 = %s\n", e1->type->toChars()); e1b = resolveAliasThis(sc, e1); i2 = e1b->implicitConvTo(t2); } if (i1 && i2) goto Lincompatible; if (i1) goto Lt1; else if (i2) goto Lt2; if (e1b) { e1 = e1b; t1 = e1b->type->toBasetype(); } if (e2b) { e2 = e2b; t2 = e2b->type->toBasetype(); } t = t1; goto Lagain; } } else if (t1->ty == Tstruct || t2->ty == Tstruct) { if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis) { if (att1 && e1->type == att1) goto Lincompatible; if (!att1 && e1->type->checkAliasThisRec()) att1 = e1->type; //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 && ((TypeStruct *)t2)->sym->aliasthis) { if (att2 && e2->type == att2) goto Lincompatible; if (!att2 && e2->type->checkAliasThisRec()) att2 = e2->type; //printf("att tmerge(s || s) e2 = %s\n", e2->type->toChars()); e2 = resolveAliasThis(sc, e2); t2 = e2->type; t = t2; goto Lagain; } goto Lincompatible; } else if ((e1->op == TOKstring || e1->op == TOKnull) && e1->implicitConvTo(t2)) { goto Lt2; } else if ((e2->op == TOKstring || e2->op == TOKnull) && e2->implicitConvTo(t1)) { goto Lt1; } else if (t1->ty == Tsarray && t2->ty == Tsarray && e2->implicitConvTo(t1->nextOf()->arrayOf())) { Lx1: t = t1->nextOf()->arrayOf(); // T[] e1 = e1->castTo(sc, t); e2 = e2->castTo(sc, t); } else if (t1->ty == Tsarray && t2->ty == Tsarray && e1->implicitConvTo(t2->nextOf()->arrayOf())) { Lx2: t = t2->nextOf()->arrayOf(); e1 = e1->castTo(sc, t); e2 = e2->castTo(sc, t); } else if (t1->ty == Tvector && t2->ty == Tvector) { // Bugzilla 13841, all vector types should have no common types between // different vectors, even though their sizes are same. TypeVector *tv1 = (TypeVector *)t1; TypeVector *tv2 = (TypeVector *)t2; if (!tv1->basetype->equals(tv2->basetype)) goto Lincompatible; goto LmodCompare; } else if (t1->ty == Tvector && t2->ty != Tvector && e2->implicitConvTo(t1)) { e2 = e2->castTo(sc, t1); t2 = t1; t = t1; goto Lagain; } else if (t2->ty == Tvector && t1->ty != Tvector && e1->implicitConvTo(t2)) { e1 = e1->castTo(sc, t2); t1 = t2; t = t1; goto Lagain; } else if (t1->isintegral() && t2->isintegral()) { if (t1->ty != t2->ty) { if (t1->ty == Tvector || t2->ty == Tvector) goto Lincompatible; 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()) goto Lincompatible; unsigned char 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; } else if (t1->ty == Tnull && t2->ty == Tnull) { unsigned char mod = MODmerge(t1->mod, t2->mod); t = t1->castMod(mod); e1 = e1->castTo(sc, t); e2 = e2->castTo(sc, t); goto Lret; } else if (t2->ty == Tnull && (t1->ty == Tpointer || t1->ty == Taarray || t1->ty == Tarray)) { goto Lt1; } else if (t1->ty == Tnull && (t2->ty == Tpointer || t2->ty == Taarray || t2->ty == Tarray)) { goto Lt2; } else 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()); t = t1->nextOf()->arrayOf(); } else if (t1->nextOf()->implicitConvTo(e2->type)) { // (cast(T)U)[] op T (Bugzilla 12780) // e1 is left as U[], it will be handled in arrayOp() later. t = e2->type->arrayOf(); } else if (t2->ty == Tarray && isArrayOpOperand(e2)) { if (t1->nextOf()->implicitConvTo(t2->nextOf())) { // (cast(T)U)[] op T[] (Bugzilla 12780) // e1 is left as U[], it will be handled in arrayOp() later. t = t2->nextOf()->arrayOf(); } else if (t2->nextOf()->implicitConvTo(t1->nextOf())) { // T[] op (cast(T)U)[] (Bugzilla 12780) // e2 is left as U[], it will be handled in arrayOp() later. t = t1->nextOf()->arrayOf(); } else goto Lincompatible; } else goto Lincompatible; } 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)[] (Bugzilla 12780) // e2 is left as U[], it will be handled in arrayOp() later. t = e1->type->arrayOf(); } else goto Lincompatible; //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; } } else { Lincompatible: return false; } Lret: if (!*pt) *pt = t; *pe1 = e1; *pe2 = e2; //print(); return true; Lt1: e2 = e2->castTo(sc, t1); t = t1; goto Lret; Lt2: e1 = e1->castTo(sc, t2); t = t2; goto Lret; } /************************************ * Bring leaves to common type. * Returns ErrorExp if error occurs. otherwise returns NULL. */ Expression *typeCombine(BinExp *be, Scope *sc) { Type *t1 = be->e1->type->toBasetype(); Type *t2 = be->e2->type->toBasetype(); if (be->op == TOKmin || be->op == TOKadd) { // struct+struct, and class+class are errors if (t1->ty == Tstruct && t2->ty == Tstruct) goto Lerror; else if (t1->ty == Tclass && t2->ty == Tclass) goto Lerror; else if (t1->ty == Taarray && t2->ty == Taarray) goto Lerror; } if (!typeMerge(sc, be->op, &be->type, &be->e1, &be->e2)) goto Lerror; // If the types have no value, return an error if (be->e1->op == TOKerror) return be->e1; if (be->e2->op == TOKerror) return be->e2; return NULL; Lerror: Expression *ex = be->incompatibleTypes(); if (ex->op == TOKerror) return ex; return new ErrorExp(); } /*********************************** * Do integral promotions (convertchk). * Don't convert 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 new ErrorExp(); 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; } /*********************************** * See if both types are arrays that can be compared * for equality. Return true if so. * If they are arrays, but incompatible, issue error. * This is to enable comparing things like an immutable * array with a mutable one. */ bool arrayTypeCompatible(Loc loc, Type *t1, Type *t2) { t1 = t1->toBasetype()->merge2(); t2 = t2->toBasetype()->merge2(); if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) && (t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer)) { if (t1->nextOf()->implicitConvTo(t2->nextOf()) < MATCHconst && t2->nextOf()->implicitConvTo(t1->nextOf()) < MATCHconst && (t1->nextOf()->ty != Tvoid && t2->nextOf()->ty != Tvoid)) { error(loc, "array equality comparison type mismatch, %s vs %s", t1->toChars(), t2->toChars()); } return true; } return false; } /*********************************** * 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. */ 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()) >= MATCHconst || t2->nextOf()->implicitConvTo(t1->nextOf()) >= MATCHconst) 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) { class IntRangeVisitor : public Visitor { public: IntRange range; void visit(Expression *e) { range = IntRange::fromType(e->type); } void visit(IntegerExp *e) { range = IntRange(SignExtendedNumber(e->getInteger())).cast(e->type); } void visit(CastExp *e) { range = getIntRange(e->e1).cast(e->type); } void visit(AddExp *e) { IntRange ir1 = getIntRange(e->e1); IntRange ir2 = getIntRange(e->e2); range = (ir1 + ir2).cast(e->type); } void visit(MinExp *e) { IntRange ir1 = getIntRange(e->e1); IntRange ir2 = getIntRange(e->e2); range = (ir1 - ir2).cast(e->type); } void visit(DivExp *e) { IntRange ir1 = getIntRange(e->e1); IntRange ir2 = getIntRange(e->e2); range = (ir1 / ir2).cast(e->type); } void visit(MulExp *e) { IntRange ir1 = getIntRange(e->e1); IntRange ir2 = getIntRange(e->e2); range = (ir1 * ir2).cast(e->type); } 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((Expression *)e); return; } range = (ir1 % ir2).cast(e->type); } 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); } 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); } 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); } void visit(ShlExp *e) { IntRange ir1 = getIntRange(e->e1); IntRange ir2 = getIntRange(e->e2); range = (ir1 << ir2).cast(e->type); } void visit(ShrExp *e) { IntRange ir1 = getIntRange(e->e1); IntRange ir2 = getIntRange(e->e2); range = (ir1 >> ir2).cast(e->type); } void visit(UshrExp *e) { IntRange ir1 = getIntRange(e->e1).castUnsigned(e->e1->type); IntRange ir2 = getIntRange(e->e2); range = (ir1 >> ir2).cast(e->type); } void visit(AssignExp *e) { range = getIntRange(e->e2).cast(e->type); } 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); } 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()) != NULL) ie->accept(this); else visit((Expression *)e); } void visit(CommaExp *e) { e->e2->accept(this); } 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); } void visit(NegExp *e) { IntRange ir = getIntRange(e->e1); range = (-ir).cast(e->type); } }; IntRangeVisitor v; e->accept(&v); return v.range; }